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);
3618 // ----------------------------------------------------------------------------
3619 // functions for loading EM level
3620 // ----------------------------------------------------------------------------
3622 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3624 static int ball_xy[8][2] =
3635 struct LevelInfo_EM *level_em = level->native_em_level;
3636 struct CAVE *cav = level_em->cav;
3639 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3640 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3642 cav->time_seconds = level->time;
3643 cav->gems_needed = level->gems_needed;
3645 cav->emerald_score = level->score[SC_EMERALD];
3646 cav->diamond_score = level->score[SC_DIAMOND];
3647 cav->alien_score = level->score[SC_ROBOT];
3648 cav->tank_score = level->score[SC_SPACESHIP];
3649 cav->bug_score = level->score[SC_BUG];
3650 cav->eater_score = level->score[SC_YAMYAM];
3651 cav->nut_score = level->score[SC_NUT];
3652 cav->dynamite_score = level->score[SC_DYNAMITE];
3653 cav->key_score = level->score[SC_KEY];
3654 cav->exit_score = level->score[SC_TIME_BONUS];
3656 cav->num_eater_arrays = level->num_yamyam_contents;
3658 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3659 for (y = 0; y < 3; y++)
3660 for (x = 0; x < 3; x++)
3661 cav->eater_array[i][y * 3 + x] =
3662 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3664 cav->amoeba_time = level->amoeba_speed;
3665 cav->wonderwall_time = level->time_magic_wall;
3666 cav->wheel_time = level->time_wheel;
3668 cav->android_move_time = level->android_move_time;
3669 cav->android_clone_time = level->android_clone_time;
3670 cav->ball_random = level->ball_random;
3671 cav->ball_active = level->ball_active_initial;
3672 cav->ball_time = level->ball_time;
3673 cav->num_ball_arrays = level->num_ball_contents;
3675 cav->lenses_score = level->lenses_score;
3676 cav->magnify_score = level->magnify_score;
3677 cav->slurp_score = level->slurp_score;
3679 cav->lenses_time = level->lenses_time;
3680 cav->magnify_time = level->magnify_time;
3682 cav->wind_direction =
3683 map_direction_RND_to_EM(level->wind_direction_initial);
3685 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3686 for (j = 0; j < 8; j++)
3687 cav->ball_array[i][j] =
3688 map_element_RND_to_EM_cave(level->ball_content[i].
3689 e[ball_xy[j][0]][ball_xy[j][1]]);
3691 map_android_clone_elements_RND_to_EM(level);
3693 // first fill the complete playfield with the empty space element
3694 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3695 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3696 cav->cave[x][y] = Cblank;
3698 // then copy the real level contents from level file into the playfield
3699 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3701 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3703 if (level->field[x][y] == EL_AMOEBA_DEAD)
3704 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3706 cav->cave[x][y] = new_element;
3709 for (i = 0; i < MAX_PLAYERS; i++)
3711 cav->player_x[i] = -1;
3712 cav->player_y[i] = -1;
3715 // initialize player positions and delete players from the playfield
3716 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3718 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3720 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3722 cav->player_x[player_nr] = x;
3723 cav->player_y[player_nr] = y;
3725 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3730 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3732 static int ball_xy[8][2] =
3743 struct LevelInfo_EM *level_em = level->native_em_level;
3744 struct CAVE *cav = level_em->cav;
3747 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3748 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3750 level->time = cav->time_seconds;
3751 level->gems_needed = cav->gems_needed;
3753 sprintf(level->name, "Level %d", level->file_info.nr);
3755 level->score[SC_EMERALD] = cav->emerald_score;
3756 level->score[SC_DIAMOND] = cav->diamond_score;
3757 level->score[SC_ROBOT] = cav->alien_score;
3758 level->score[SC_SPACESHIP] = cav->tank_score;
3759 level->score[SC_BUG] = cav->bug_score;
3760 level->score[SC_YAMYAM] = cav->eater_score;
3761 level->score[SC_NUT] = cav->nut_score;
3762 level->score[SC_DYNAMITE] = cav->dynamite_score;
3763 level->score[SC_KEY] = cav->key_score;
3764 level->score[SC_TIME_BONUS] = cav->exit_score;
3766 level->num_yamyam_contents = cav->num_eater_arrays;
3768 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3769 for (y = 0; y < 3; y++)
3770 for (x = 0; x < 3; x++)
3771 level->yamyam_content[i].e[x][y] =
3772 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3774 level->amoeba_speed = cav->amoeba_time;
3775 level->time_magic_wall = cav->wonderwall_time;
3776 level->time_wheel = cav->wheel_time;
3778 level->android_move_time = cav->android_move_time;
3779 level->android_clone_time = cav->android_clone_time;
3780 level->ball_random = cav->ball_random;
3781 level->ball_active_initial = cav->ball_active;
3782 level->ball_time = cav->ball_time;
3783 level->num_ball_contents = cav->num_ball_arrays;
3785 level->lenses_score = cav->lenses_score;
3786 level->magnify_score = cav->magnify_score;
3787 level->slurp_score = cav->slurp_score;
3789 level->lenses_time = cav->lenses_time;
3790 level->magnify_time = cav->magnify_time;
3792 level->wind_direction_initial =
3793 map_direction_EM_to_RND(cav->wind_direction);
3795 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3796 for (j = 0; j < 8; j++)
3797 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3798 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3800 map_android_clone_elements_EM_to_RND(level);
3802 // convert the playfield (some elements need special treatment)
3803 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3805 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3807 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3808 new_element = EL_AMOEBA_DEAD;
3810 level->field[x][y] = new_element;
3813 for (i = 0; i < MAX_PLAYERS; i++)
3815 // in case of all players set to the same field, use the first player
3816 int nr = MAX_PLAYERS - i - 1;
3817 int jx = cav->player_x[nr];
3818 int jy = cav->player_y[nr];
3820 if (jx != -1 && jy != -1)
3821 level->field[jx][jy] = EL_PLAYER_1 + nr;
3824 // time score is counted for each 10 seconds left in Emerald Mine levels
3825 level->time_score_base = 10;
3829 // ----------------------------------------------------------------------------
3830 // functions for loading SP level
3831 // ----------------------------------------------------------------------------
3833 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3835 struct LevelInfo_SP *level_sp = level->native_sp_level;
3836 LevelInfoType *header = &level_sp->header;
3839 level_sp->width = level->fieldx;
3840 level_sp->height = level->fieldy;
3842 for (x = 0; x < level->fieldx; x++)
3843 for (y = 0; y < level->fieldy; y++)
3844 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3846 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3848 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3849 header->LevelTitle[i] = level->name[i];
3850 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3852 header->InfotronsNeeded = level->gems_needed;
3854 header->SpecialPortCount = 0;
3856 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3858 boolean gravity_port_found = FALSE;
3859 boolean gravity_port_valid = FALSE;
3860 int gravity_port_flag;
3861 int gravity_port_base_element;
3862 int element = level->field[x][y];
3864 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3865 element <= EL_SP_GRAVITY_ON_PORT_UP)
3867 gravity_port_found = TRUE;
3868 gravity_port_valid = TRUE;
3869 gravity_port_flag = 1;
3870 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3872 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3873 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3875 gravity_port_found = TRUE;
3876 gravity_port_valid = TRUE;
3877 gravity_port_flag = 0;
3878 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3880 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3881 element <= EL_SP_GRAVITY_PORT_UP)
3883 // change R'n'D style gravity inverting special port to normal port
3884 // (there are no gravity inverting ports in native Supaplex engine)
3886 gravity_port_found = TRUE;
3887 gravity_port_valid = FALSE;
3888 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3891 if (gravity_port_found)
3893 if (gravity_port_valid &&
3894 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3896 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3898 port->PortLocation = (y * level->fieldx + x) * 2;
3899 port->Gravity = gravity_port_flag;
3901 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3903 header->SpecialPortCount++;
3907 // change special gravity port to normal port
3909 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3912 level_sp->playfield[x][y] = element - EL_SP_START;
3917 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3919 struct LevelInfo_SP *level_sp = level->native_sp_level;
3920 LevelInfoType *header = &level_sp->header;
3921 boolean num_invalid_elements = 0;
3924 level->fieldx = level_sp->width;
3925 level->fieldy = level_sp->height;
3927 for (x = 0; x < level->fieldx; x++)
3929 for (y = 0; y < level->fieldy; y++)
3931 int element_old = level_sp->playfield[x][y];
3932 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3934 if (element_new == EL_UNKNOWN)
3936 num_invalid_elements++;
3938 Debug("level:native:SP", "invalid element %d at position %d, %d",
3942 level->field[x][y] = element_new;
3946 if (num_invalid_elements > 0)
3947 Warn("found %d invalid elements%s", num_invalid_elements,
3948 (!options.debug ? " (use '--debug' for more details)" : ""));
3950 for (i = 0; i < MAX_PLAYERS; i++)
3951 level->initial_player_gravity[i] =
3952 (header->InitialGravity == 1 ? TRUE : FALSE);
3954 // skip leading spaces
3955 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3956 if (header->LevelTitle[i] != ' ')
3960 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3961 level->name[j] = header->LevelTitle[i];
3962 level->name[j] = '\0';
3964 // cut trailing spaces
3966 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3967 level->name[j - 1] = '\0';
3969 level->gems_needed = header->InfotronsNeeded;
3971 for (i = 0; i < header->SpecialPortCount; i++)
3973 SpecialPortType *port = &header->SpecialPort[i];
3974 int port_location = port->PortLocation;
3975 int gravity = port->Gravity;
3976 int port_x, port_y, port_element;
3978 port_x = (port_location / 2) % level->fieldx;
3979 port_y = (port_location / 2) / level->fieldx;
3981 if (port_x < 0 || port_x >= level->fieldx ||
3982 port_y < 0 || port_y >= level->fieldy)
3984 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3989 port_element = level->field[port_x][port_y];
3991 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3992 port_element > EL_SP_GRAVITY_PORT_UP)
3994 Warn("no special port at position (%d, %d)", port_x, port_y);
3999 // change previous (wrong) gravity inverting special port to either
4000 // gravity enabling special port or gravity disabling special port
4001 level->field[port_x][port_y] +=
4002 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4003 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4006 // change special gravity ports without database entries to normal ports
4007 for (x = 0; x < level->fieldx; x++)
4008 for (y = 0; y < level->fieldy; y++)
4009 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4010 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4011 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4013 level->time = 0; // no time limit
4014 level->amoeba_speed = 0;
4015 level->time_magic_wall = 0;
4016 level->time_wheel = 0;
4017 level->amoeba_content = EL_EMPTY;
4019 // original Supaplex does not use score values -- rate by playing time
4020 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4021 level->score[i] = 0;
4023 level->rate_time_over_score = TRUE;
4025 // there are no yamyams in supaplex levels
4026 for (i = 0; i < level->num_yamyam_contents; i++)
4027 for (x = 0; x < 3; x++)
4028 for (y = 0; y < 3; y++)
4029 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4032 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4034 struct LevelInfo_SP *level_sp = level->native_sp_level;
4035 struct DemoInfo_SP *demo = &level_sp->demo;
4038 // always start with reliable default values
4039 demo->is_available = FALSE;
4042 if (TAPE_IS_EMPTY(tape))
4045 demo->level_nr = tape.level_nr; // (currently not used)
4047 level_sp->header.DemoRandomSeed = tape.random_seed;
4051 for (i = 0; i < tape.length; i++)
4053 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4054 int demo_repeat = tape.pos[i].delay;
4055 int demo_entries = (demo_repeat + 15) / 16;
4057 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4059 Warn("tape truncated: size exceeds maximum SP demo size %d",
4065 for (j = 0; j < demo_repeat / 16; j++)
4066 demo->data[demo->length++] = 0xf0 | demo_action;
4068 if (demo_repeat % 16)
4069 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4072 demo->is_available = TRUE;
4075 static void setTapeInfoToDefaults(void);
4077 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4079 struct LevelInfo_SP *level_sp = level->native_sp_level;
4080 struct DemoInfo_SP *demo = &level_sp->demo;
4081 char *filename = level->file_info.filename;
4084 // always start with reliable default values
4085 setTapeInfoToDefaults();
4087 if (!demo->is_available)
4090 tape.level_nr = demo->level_nr; // (currently not used)
4091 tape.random_seed = level_sp->header.DemoRandomSeed;
4093 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4096 tape.pos[tape.counter].delay = 0;
4098 for (i = 0; i < demo->length; i++)
4100 int demo_action = demo->data[i] & 0x0f;
4101 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4102 int tape_action = map_key_SP_to_RND(demo_action);
4103 int tape_repeat = demo_repeat + 1;
4104 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4105 boolean success = 0;
4108 for (j = 0; j < tape_repeat; j++)
4109 success = TapeAddAction(action);
4113 Warn("SP demo truncated: size exceeds maximum tape size %d",
4120 TapeHaltRecording();
4124 // ----------------------------------------------------------------------------
4125 // functions for loading MM level
4126 // ----------------------------------------------------------------------------
4128 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4130 struct LevelInfo_MM *level_mm = level->native_mm_level;
4133 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4134 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4136 level_mm->time = level->time;
4137 level_mm->kettles_needed = level->gems_needed;
4138 level_mm->auto_count_kettles = level->auto_count_gems;
4140 level_mm->laser_red = level->mm_laser_red;
4141 level_mm->laser_green = level->mm_laser_green;
4142 level_mm->laser_blue = level->mm_laser_blue;
4144 strcpy(level_mm->name, level->name);
4145 strcpy(level_mm->author, level->author);
4147 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4148 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4149 level_mm->score[SC_KEY] = level->score[SC_KEY];
4150 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4151 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4153 level_mm->amoeba_speed = level->amoeba_speed;
4154 level_mm->time_fuse = level->mm_time_fuse;
4155 level_mm->time_bomb = level->mm_time_bomb;
4156 level_mm->time_ball = level->mm_time_ball;
4157 level_mm->time_block = level->mm_time_block;
4159 for (x = 0; x < level->fieldx; x++)
4160 for (y = 0; y < level->fieldy; y++)
4162 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4165 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4167 struct LevelInfo_MM *level_mm = level->native_mm_level;
4170 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4171 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4173 level->time = level_mm->time;
4174 level->gems_needed = level_mm->kettles_needed;
4175 level->auto_count_gems = level_mm->auto_count_kettles;
4177 level->mm_laser_red = level_mm->laser_red;
4178 level->mm_laser_green = level_mm->laser_green;
4179 level->mm_laser_blue = level_mm->laser_blue;
4181 strcpy(level->name, level_mm->name);
4183 // only overwrite author from 'levelinfo.conf' if author defined in level
4184 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4185 strcpy(level->author, level_mm->author);
4187 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4188 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4189 level->score[SC_KEY] = level_mm->score[SC_KEY];
4190 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4191 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4193 level->amoeba_speed = level_mm->amoeba_speed;
4194 level->mm_time_fuse = level_mm->time_fuse;
4195 level->mm_time_bomb = level_mm->time_bomb;
4196 level->mm_time_ball = level_mm->time_ball;
4197 level->mm_time_block = level_mm->time_block;
4199 for (x = 0; x < level->fieldx; x++)
4200 for (y = 0; y < level->fieldy; y++)
4201 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4205 // ----------------------------------------------------------------------------
4206 // functions for loading DC level
4207 // ----------------------------------------------------------------------------
4209 #define DC_LEVEL_HEADER_SIZE 344
4211 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4214 static int last_data_encoded;
4218 int diff_hi, diff_lo;
4219 int data_hi, data_lo;
4220 unsigned short data_decoded;
4224 last_data_encoded = 0;
4231 diff = data_encoded - last_data_encoded;
4232 diff_hi = diff & ~0xff;
4233 diff_lo = diff & 0xff;
4237 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4238 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4239 data_hi = data_hi & 0xff00;
4241 data_decoded = data_hi | data_lo;
4243 last_data_encoded = data_encoded;
4245 offset1 = (offset1 + 1) % 31;
4246 offset2 = offset2 & 0xff;
4248 return data_decoded;
4251 static int getMappedElement_DC(int element)
4259 // 0x0117 - 0x036e: (?)
4262 // 0x042d - 0x0684: (?)
4278 element = EL_CRYSTAL;
4281 case 0x0e77: // quicksand (boulder)
4282 element = EL_QUICKSAND_FAST_FULL;
4285 case 0x0e99: // slow quicksand (boulder)
4286 element = EL_QUICKSAND_FULL;
4290 element = EL_EM_EXIT_OPEN;
4294 element = EL_EM_EXIT_CLOSED;
4298 element = EL_EM_STEEL_EXIT_OPEN;
4302 element = EL_EM_STEEL_EXIT_CLOSED;
4305 case 0x0f4f: // dynamite (lit 1)
4306 element = EL_EM_DYNAMITE_ACTIVE;
4309 case 0x0f57: // dynamite (lit 2)
4310 element = EL_EM_DYNAMITE_ACTIVE;
4313 case 0x0f5f: // dynamite (lit 3)
4314 element = EL_EM_DYNAMITE_ACTIVE;
4317 case 0x0f67: // dynamite (lit 4)
4318 element = EL_EM_DYNAMITE_ACTIVE;
4325 element = EL_AMOEBA_WET;
4329 element = EL_AMOEBA_DROP;
4333 element = EL_DC_MAGIC_WALL;
4337 element = EL_SPACESHIP_UP;
4341 element = EL_SPACESHIP_DOWN;
4345 element = EL_SPACESHIP_LEFT;
4349 element = EL_SPACESHIP_RIGHT;
4353 element = EL_BUG_UP;
4357 element = EL_BUG_DOWN;
4361 element = EL_BUG_LEFT;
4365 element = EL_BUG_RIGHT;
4369 element = EL_MOLE_UP;
4373 element = EL_MOLE_DOWN;
4377 element = EL_MOLE_LEFT;
4381 element = EL_MOLE_RIGHT;
4389 element = EL_YAMYAM_UP;
4393 element = EL_SWITCHGATE_OPEN;
4397 element = EL_SWITCHGATE_CLOSED;
4401 element = EL_DC_SWITCHGATE_SWITCH_UP;
4405 element = EL_TIMEGATE_CLOSED;
4408 case 0x144c: // conveyor belt switch (green)
4409 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4412 case 0x144f: // conveyor belt switch (red)
4413 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4416 case 0x1452: // conveyor belt switch (blue)
4417 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4421 element = EL_CONVEYOR_BELT_3_MIDDLE;
4425 element = EL_CONVEYOR_BELT_3_LEFT;
4429 element = EL_CONVEYOR_BELT_3_RIGHT;
4433 element = EL_CONVEYOR_BELT_1_MIDDLE;
4437 element = EL_CONVEYOR_BELT_1_LEFT;
4441 element = EL_CONVEYOR_BELT_1_RIGHT;
4445 element = EL_CONVEYOR_BELT_4_MIDDLE;
4449 element = EL_CONVEYOR_BELT_4_LEFT;
4453 element = EL_CONVEYOR_BELT_4_RIGHT;
4457 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4461 element = EL_EXPANDABLE_WALL_VERTICAL;
4465 element = EL_EXPANDABLE_WALL_ANY;
4468 case 0x14ce: // growing steel wall (left/right)
4469 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4472 case 0x14df: // growing steel wall (up/down)
4473 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4476 case 0x14e8: // growing steel wall (up/down/left/right)
4477 element = EL_EXPANDABLE_STEELWALL_ANY;
4481 element = EL_SHIELD_DEADLY;
4485 element = EL_EXTRA_TIME;
4493 element = EL_EMPTY_SPACE;
4496 case 0x1578: // quicksand (empty)
4497 element = EL_QUICKSAND_FAST_EMPTY;
4500 case 0x1579: // slow quicksand (empty)
4501 element = EL_QUICKSAND_EMPTY;
4511 element = EL_EM_DYNAMITE;
4514 case 0x15a1: // key (red)
4515 element = EL_EM_KEY_1;
4518 case 0x15a2: // key (yellow)
4519 element = EL_EM_KEY_2;
4522 case 0x15a3: // key (blue)
4523 element = EL_EM_KEY_4;
4526 case 0x15a4: // key (green)
4527 element = EL_EM_KEY_3;
4530 case 0x15a5: // key (white)
4531 element = EL_DC_KEY_WHITE;
4535 element = EL_WALL_SLIPPERY;
4542 case 0x15a8: // wall (not round)
4546 case 0x15a9: // (blue)
4547 element = EL_CHAR_A;
4550 case 0x15aa: // (blue)
4551 element = EL_CHAR_B;
4554 case 0x15ab: // (blue)
4555 element = EL_CHAR_C;
4558 case 0x15ac: // (blue)
4559 element = EL_CHAR_D;
4562 case 0x15ad: // (blue)
4563 element = EL_CHAR_E;
4566 case 0x15ae: // (blue)
4567 element = EL_CHAR_F;
4570 case 0x15af: // (blue)
4571 element = EL_CHAR_G;
4574 case 0x15b0: // (blue)
4575 element = EL_CHAR_H;
4578 case 0x15b1: // (blue)
4579 element = EL_CHAR_I;
4582 case 0x15b2: // (blue)
4583 element = EL_CHAR_J;
4586 case 0x15b3: // (blue)
4587 element = EL_CHAR_K;
4590 case 0x15b4: // (blue)
4591 element = EL_CHAR_L;
4594 case 0x15b5: // (blue)
4595 element = EL_CHAR_M;
4598 case 0x15b6: // (blue)
4599 element = EL_CHAR_N;
4602 case 0x15b7: // (blue)
4603 element = EL_CHAR_O;
4606 case 0x15b8: // (blue)
4607 element = EL_CHAR_P;
4610 case 0x15b9: // (blue)
4611 element = EL_CHAR_Q;
4614 case 0x15ba: // (blue)
4615 element = EL_CHAR_R;
4618 case 0x15bb: // (blue)
4619 element = EL_CHAR_S;
4622 case 0x15bc: // (blue)
4623 element = EL_CHAR_T;
4626 case 0x15bd: // (blue)
4627 element = EL_CHAR_U;
4630 case 0x15be: // (blue)
4631 element = EL_CHAR_V;
4634 case 0x15bf: // (blue)
4635 element = EL_CHAR_W;
4638 case 0x15c0: // (blue)
4639 element = EL_CHAR_X;
4642 case 0x15c1: // (blue)
4643 element = EL_CHAR_Y;
4646 case 0x15c2: // (blue)
4647 element = EL_CHAR_Z;
4650 case 0x15c3: // (blue)
4651 element = EL_CHAR_AUMLAUT;
4654 case 0x15c4: // (blue)
4655 element = EL_CHAR_OUMLAUT;
4658 case 0x15c5: // (blue)
4659 element = EL_CHAR_UUMLAUT;
4662 case 0x15c6: // (blue)
4663 element = EL_CHAR_0;
4666 case 0x15c7: // (blue)
4667 element = EL_CHAR_1;
4670 case 0x15c8: // (blue)
4671 element = EL_CHAR_2;
4674 case 0x15c9: // (blue)
4675 element = EL_CHAR_3;
4678 case 0x15ca: // (blue)
4679 element = EL_CHAR_4;
4682 case 0x15cb: // (blue)
4683 element = EL_CHAR_5;
4686 case 0x15cc: // (blue)
4687 element = EL_CHAR_6;
4690 case 0x15cd: // (blue)
4691 element = EL_CHAR_7;
4694 case 0x15ce: // (blue)
4695 element = EL_CHAR_8;
4698 case 0x15cf: // (blue)
4699 element = EL_CHAR_9;
4702 case 0x15d0: // (blue)
4703 element = EL_CHAR_PERIOD;
4706 case 0x15d1: // (blue)
4707 element = EL_CHAR_EXCLAM;
4710 case 0x15d2: // (blue)
4711 element = EL_CHAR_COLON;
4714 case 0x15d3: // (blue)
4715 element = EL_CHAR_LESS;
4718 case 0x15d4: // (blue)
4719 element = EL_CHAR_GREATER;
4722 case 0x15d5: // (blue)
4723 element = EL_CHAR_QUESTION;
4726 case 0x15d6: // (blue)
4727 element = EL_CHAR_COPYRIGHT;
4730 case 0x15d7: // (blue)
4731 element = EL_CHAR_UP;
4734 case 0x15d8: // (blue)
4735 element = EL_CHAR_DOWN;
4738 case 0x15d9: // (blue)
4739 element = EL_CHAR_BUTTON;
4742 case 0x15da: // (blue)
4743 element = EL_CHAR_PLUS;
4746 case 0x15db: // (blue)
4747 element = EL_CHAR_MINUS;
4750 case 0x15dc: // (blue)
4751 element = EL_CHAR_APOSTROPHE;
4754 case 0x15dd: // (blue)
4755 element = EL_CHAR_PARENLEFT;
4758 case 0x15de: // (blue)
4759 element = EL_CHAR_PARENRIGHT;
4762 case 0x15df: // (green)
4763 element = EL_CHAR_A;
4766 case 0x15e0: // (green)
4767 element = EL_CHAR_B;
4770 case 0x15e1: // (green)
4771 element = EL_CHAR_C;
4774 case 0x15e2: // (green)
4775 element = EL_CHAR_D;
4778 case 0x15e3: // (green)
4779 element = EL_CHAR_E;
4782 case 0x15e4: // (green)
4783 element = EL_CHAR_F;
4786 case 0x15e5: // (green)
4787 element = EL_CHAR_G;
4790 case 0x15e6: // (green)
4791 element = EL_CHAR_H;
4794 case 0x15e7: // (green)
4795 element = EL_CHAR_I;
4798 case 0x15e8: // (green)
4799 element = EL_CHAR_J;
4802 case 0x15e9: // (green)
4803 element = EL_CHAR_K;
4806 case 0x15ea: // (green)
4807 element = EL_CHAR_L;
4810 case 0x15eb: // (green)
4811 element = EL_CHAR_M;
4814 case 0x15ec: // (green)
4815 element = EL_CHAR_N;
4818 case 0x15ed: // (green)
4819 element = EL_CHAR_O;
4822 case 0x15ee: // (green)
4823 element = EL_CHAR_P;
4826 case 0x15ef: // (green)
4827 element = EL_CHAR_Q;
4830 case 0x15f0: // (green)
4831 element = EL_CHAR_R;
4834 case 0x15f1: // (green)
4835 element = EL_CHAR_S;
4838 case 0x15f2: // (green)
4839 element = EL_CHAR_T;
4842 case 0x15f3: // (green)
4843 element = EL_CHAR_U;
4846 case 0x15f4: // (green)
4847 element = EL_CHAR_V;
4850 case 0x15f5: // (green)
4851 element = EL_CHAR_W;
4854 case 0x15f6: // (green)
4855 element = EL_CHAR_X;
4858 case 0x15f7: // (green)
4859 element = EL_CHAR_Y;
4862 case 0x15f8: // (green)
4863 element = EL_CHAR_Z;
4866 case 0x15f9: // (green)
4867 element = EL_CHAR_AUMLAUT;
4870 case 0x15fa: // (green)
4871 element = EL_CHAR_OUMLAUT;
4874 case 0x15fb: // (green)
4875 element = EL_CHAR_UUMLAUT;
4878 case 0x15fc: // (green)
4879 element = EL_CHAR_0;
4882 case 0x15fd: // (green)
4883 element = EL_CHAR_1;
4886 case 0x15fe: // (green)
4887 element = EL_CHAR_2;
4890 case 0x15ff: // (green)
4891 element = EL_CHAR_3;
4894 case 0x1600: // (green)
4895 element = EL_CHAR_4;
4898 case 0x1601: // (green)
4899 element = EL_CHAR_5;
4902 case 0x1602: // (green)
4903 element = EL_CHAR_6;
4906 case 0x1603: // (green)
4907 element = EL_CHAR_7;
4910 case 0x1604: // (green)
4911 element = EL_CHAR_8;
4914 case 0x1605: // (green)
4915 element = EL_CHAR_9;
4918 case 0x1606: // (green)
4919 element = EL_CHAR_PERIOD;
4922 case 0x1607: // (green)
4923 element = EL_CHAR_EXCLAM;
4926 case 0x1608: // (green)
4927 element = EL_CHAR_COLON;
4930 case 0x1609: // (green)
4931 element = EL_CHAR_LESS;
4934 case 0x160a: // (green)
4935 element = EL_CHAR_GREATER;
4938 case 0x160b: // (green)
4939 element = EL_CHAR_QUESTION;
4942 case 0x160c: // (green)
4943 element = EL_CHAR_COPYRIGHT;
4946 case 0x160d: // (green)
4947 element = EL_CHAR_UP;
4950 case 0x160e: // (green)
4951 element = EL_CHAR_DOWN;
4954 case 0x160f: // (green)
4955 element = EL_CHAR_BUTTON;
4958 case 0x1610: // (green)
4959 element = EL_CHAR_PLUS;
4962 case 0x1611: // (green)
4963 element = EL_CHAR_MINUS;
4966 case 0x1612: // (green)
4967 element = EL_CHAR_APOSTROPHE;
4970 case 0x1613: // (green)
4971 element = EL_CHAR_PARENLEFT;
4974 case 0x1614: // (green)
4975 element = EL_CHAR_PARENRIGHT;
4978 case 0x1615: // (blue steel)
4979 element = EL_STEEL_CHAR_A;
4982 case 0x1616: // (blue steel)
4983 element = EL_STEEL_CHAR_B;
4986 case 0x1617: // (blue steel)
4987 element = EL_STEEL_CHAR_C;
4990 case 0x1618: // (blue steel)
4991 element = EL_STEEL_CHAR_D;
4994 case 0x1619: // (blue steel)
4995 element = EL_STEEL_CHAR_E;
4998 case 0x161a: // (blue steel)
4999 element = EL_STEEL_CHAR_F;
5002 case 0x161b: // (blue steel)
5003 element = EL_STEEL_CHAR_G;
5006 case 0x161c: // (blue steel)
5007 element = EL_STEEL_CHAR_H;
5010 case 0x161d: // (blue steel)
5011 element = EL_STEEL_CHAR_I;
5014 case 0x161e: // (blue steel)
5015 element = EL_STEEL_CHAR_J;
5018 case 0x161f: // (blue steel)
5019 element = EL_STEEL_CHAR_K;
5022 case 0x1620: // (blue steel)
5023 element = EL_STEEL_CHAR_L;
5026 case 0x1621: // (blue steel)
5027 element = EL_STEEL_CHAR_M;
5030 case 0x1622: // (blue steel)
5031 element = EL_STEEL_CHAR_N;
5034 case 0x1623: // (blue steel)
5035 element = EL_STEEL_CHAR_O;
5038 case 0x1624: // (blue steel)
5039 element = EL_STEEL_CHAR_P;
5042 case 0x1625: // (blue steel)
5043 element = EL_STEEL_CHAR_Q;
5046 case 0x1626: // (blue steel)
5047 element = EL_STEEL_CHAR_R;
5050 case 0x1627: // (blue steel)
5051 element = EL_STEEL_CHAR_S;
5054 case 0x1628: // (blue steel)
5055 element = EL_STEEL_CHAR_T;
5058 case 0x1629: // (blue steel)
5059 element = EL_STEEL_CHAR_U;
5062 case 0x162a: // (blue steel)
5063 element = EL_STEEL_CHAR_V;
5066 case 0x162b: // (blue steel)
5067 element = EL_STEEL_CHAR_W;
5070 case 0x162c: // (blue steel)
5071 element = EL_STEEL_CHAR_X;
5074 case 0x162d: // (blue steel)
5075 element = EL_STEEL_CHAR_Y;
5078 case 0x162e: // (blue steel)
5079 element = EL_STEEL_CHAR_Z;
5082 case 0x162f: // (blue steel)
5083 element = EL_STEEL_CHAR_AUMLAUT;
5086 case 0x1630: // (blue steel)
5087 element = EL_STEEL_CHAR_OUMLAUT;
5090 case 0x1631: // (blue steel)
5091 element = EL_STEEL_CHAR_UUMLAUT;
5094 case 0x1632: // (blue steel)
5095 element = EL_STEEL_CHAR_0;
5098 case 0x1633: // (blue steel)
5099 element = EL_STEEL_CHAR_1;
5102 case 0x1634: // (blue steel)
5103 element = EL_STEEL_CHAR_2;
5106 case 0x1635: // (blue steel)
5107 element = EL_STEEL_CHAR_3;
5110 case 0x1636: // (blue steel)
5111 element = EL_STEEL_CHAR_4;
5114 case 0x1637: // (blue steel)
5115 element = EL_STEEL_CHAR_5;
5118 case 0x1638: // (blue steel)
5119 element = EL_STEEL_CHAR_6;
5122 case 0x1639: // (blue steel)
5123 element = EL_STEEL_CHAR_7;
5126 case 0x163a: // (blue steel)
5127 element = EL_STEEL_CHAR_8;
5130 case 0x163b: // (blue steel)
5131 element = EL_STEEL_CHAR_9;
5134 case 0x163c: // (blue steel)
5135 element = EL_STEEL_CHAR_PERIOD;
5138 case 0x163d: // (blue steel)
5139 element = EL_STEEL_CHAR_EXCLAM;
5142 case 0x163e: // (blue steel)
5143 element = EL_STEEL_CHAR_COLON;
5146 case 0x163f: // (blue steel)
5147 element = EL_STEEL_CHAR_LESS;
5150 case 0x1640: // (blue steel)
5151 element = EL_STEEL_CHAR_GREATER;
5154 case 0x1641: // (blue steel)
5155 element = EL_STEEL_CHAR_QUESTION;
5158 case 0x1642: // (blue steel)
5159 element = EL_STEEL_CHAR_COPYRIGHT;
5162 case 0x1643: // (blue steel)
5163 element = EL_STEEL_CHAR_UP;
5166 case 0x1644: // (blue steel)
5167 element = EL_STEEL_CHAR_DOWN;
5170 case 0x1645: // (blue steel)
5171 element = EL_STEEL_CHAR_BUTTON;
5174 case 0x1646: // (blue steel)
5175 element = EL_STEEL_CHAR_PLUS;
5178 case 0x1647: // (blue steel)
5179 element = EL_STEEL_CHAR_MINUS;
5182 case 0x1648: // (blue steel)
5183 element = EL_STEEL_CHAR_APOSTROPHE;
5186 case 0x1649: // (blue steel)
5187 element = EL_STEEL_CHAR_PARENLEFT;
5190 case 0x164a: // (blue steel)
5191 element = EL_STEEL_CHAR_PARENRIGHT;
5194 case 0x164b: // (green steel)
5195 element = EL_STEEL_CHAR_A;
5198 case 0x164c: // (green steel)
5199 element = EL_STEEL_CHAR_B;
5202 case 0x164d: // (green steel)
5203 element = EL_STEEL_CHAR_C;
5206 case 0x164e: // (green steel)
5207 element = EL_STEEL_CHAR_D;
5210 case 0x164f: // (green steel)
5211 element = EL_STEEL_CHAR_E;
5214 case 0x1650: // (green steel)
5215 element = EL_STEEL_CHAR_F;
5218 case 0x1651: // (green steel)
5219 element = EL_STEEL_CHAR_G;
5222 case 0x1652: // (green steel)
5223 element = EL_STEEL_CHAR_H;
5226 case 0x1653: // (green steel)
5227 element = EL_STEEL_CHAR_I;
5230 case 0x1654: // (green steel)
5231 element = EL_STEEL_CHAR_J;
5234 case 0x1655: // (green steel)
5235 element = EL_STEEL_CHAR_K;
5238 case 0x1656: // (green steel)
5239 element = EL_STEEL_CHAR_L;
5242 case 0x1657: // (green steel)
5243 element = EL_STEEL_CHAR_M;
5246 case 0x1658: // (green steel)
5247 element = EL_STEEL_CHAR_N;
5250 case 0x1659: // (green steel)
5251 element = EL_STEEL_CHAR_O;
5254 case 0x165a: // (green steel)
5255 element = EL_STEEL_CHAR_P;
5258 case 0x165b: // (green steel)
5259 element = EL_STEEL_CHAR_Q;
5262 case 0x165c: // (green steel)
5263 element = EL_STEEL_CHAR_R;
5266 case 0x165d: // (green steel)
5267 element = EL_STEEL_CHAR_S;
5270 case 0x165e: // (green steel)
5271 element = EL_STEEL_CHAR_T;
5274 case 0x165f: // (green steel)
5275 element = EL_STEEL_CHAR_U;
5278 case 0x1660: // (green steel)
5279 element = EL_STEEL_CHAR_V;
5282 case 0x1661: // (green steel)
5283 element = EL_STEEL_CHAR_W;
5286 case 0x1662: // (green steel)
5287 element = EL_STEEL_CHAR_X;
5290 case 0x1663: // (green steel)
5291 element = EL_STEEL_CHAR_Y;
5294 case 0x1664: // (green steel)
5295 element = EL_STEEL_CHAR_Z;
5298 case 0x1665: // (green steel)
5299 element = EL_STEEL_CHAR_AUMLAUT;
5302 case 0x1666: // (green steel)
5303 element = EL_STEEL_CHAR_OUMLAUT;
5306 case 0x1667: // (green steel)
5307 element = EL_STEEL_CHAR_UUMLAUT;
5310 case 0x1668: // (green steel)
5311 element = EL_STEEL_CHAR_0;
5314 case 0x1669: // (green steel)
5315 element = EL_STEEL_CHAR_1;
5318 case 0x166a: // (green steel)
5319 element = EL_STEEL_CHAR_2;
5322 case 0x166b: // (green steel)
5323 element = EL_STEEL_CHAR_3;
5326 case 0x166c: // (green steel)
5327 element = EL_STEEL_CHAR_4;
5330 case 0x166d: // (green steel)
5331 element = EL_STEEL_CHAR_5;
5334 case 0x166e: // (green steel)
5335 element = EL_STEEL_CHAR_6;
5338 case 0x166f: // (green steel)
5339 element = EL_STEEL_CHAR_7;
5342 case 0x1670: // (green steel)
5343 element = EL_STEEL_CHAR_8;
5346 case 0x1671: // (green steel)
5347 element = EL_STEEL_CHAR_9;
5350 case 0x1672: // (green steel)
5351 element = EL_STEEL_CHAR_PERIOD;
5354 case 0x1673: // (green steel)
5355 element = EL_STEEL_CHAR_EXCLAM;
5358 case 0x1674: // (green steel)
5359 element = EL_STEEL_CHAR_COLON;
5362 case 0x1675: // (green steel)
5363 element = EL_STEEL_CHAR_LESS;
5366 case 0x1676: // (green steel)
5367 element = EL_STEEL_CHAR_GREATER;
5370 case 0x1677: // (green steel)
5371 element = EL_STEEL_CHAR_QUESTION;
5374 case 0x1678: // (green steel)
5375 element = EL_STEEL_CHAR_COPYRIGHT;
5378 case 0x1679: // (green steel)
5379 element = EL_STEEL_CHAR_UP;
5382 case 0x167a: // (green steel)
5383 element = EL_STEEL_CHAR_DOWN;
5386 case 0x167b: // (green steel)
5387 element = EL_STEEL_CHAR_BUTTON;
5390 case 0x167c: // (green steel)
5391 element = EL_STEEL_CHAR_PLUS;
5394 case 0x167d: // (green steel)
5395 element = EL_STEEL_CHAR_MINUS;
5398 case 0x167e: // (green steel)
5399 element = EL_STEEL_CHAR_APOSTROPHE;
5402 case 0x167f: // (green steel)
5403 element = EL_STEEL_CHAR_PARENLEFT;
5406 case 0x1680: // (green steel)
5407 element = EL_STEEL_CHAR_PARENRIGHT;
5410 case 0x1681: // gate (red)
5411 element = EL_EM_GATE_1;
5414 case 0x1682: // secret gate (red)
5415 element = EL_EM_GATE_1_GRAY;
5418 case 0x1683: // gate (yellow)
5419 element = EL_EM_GATE_2;
5422 case 0x1684: // secret gate (yellow)
5423 element = EL_EM_GATE_2_GRAY;
5426 case 0x1685: // gate (blue)
5427 element = EL_EM_GATE_4;
5430 case 0x1686: // secret gate (blue)
5431 element = EL_EM_GATE_4_GRAY;
5434 case 0x1687: // gate (green)
5435 element = EL_EM_GATE_3;
5438 case 0x1688: // secret gate (green)
5439 element = EL_EM_GATE_3_GRAY;
5442 case 0x1689: // gate (white)
5443 element = EL_DC_GATE_WHITE;
5446 case 0x168a: // secret gate (white)
5447 element = EL_DC_GATE_WHITE_GRAY;
5450 case 0x168b: // secret gate (no key)
5451 element = EL_DC_GATE_FAKE_GRAY;
5455 element = EL_ROBOT_WHEEL;
5459 element = EL_DC_TIMEGATE_SWITCH;
5463 element = EL_ACID_POOL_BOTTOM;
5467 element = EL_ACID_POOL_TOPLEFT;
5471 element = EL_ACID_POOL_TOPRIGHT;
5475 element = EL_ACID_POOL_BOTTOMLEFT;
5479 element = EL_ACID_POOL_BOTTOMRIGHT;
5483 element = EL_STEELWALL;
5487 element = EL_STEELWALL_SLIPPERY;
5490 case 0x1695: // steel wall (not round)
5491 element = EL_STEELWALL;
5494 case 0x1696: // steel wall (left)
5495 element = EL_DC_STEELWALL_1_LEFT;
5498 case 0x1697: // steel wall (bottom)
5499 element = EL_DC_STEELWALL_1_BOTTOM;
5502 case 0x1698: // steel wall (right)
5503 element = EL_DC_STEELWALL_1_RIGHT;
5506 case 0x1699: // steel wall (top)
5507 element = EL_DC_STEELWALL_1_TOP;
5510 case 0x169a: // steel wall (left/bottom)
5511 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5514 case 0x169b: // steel wall (right/bottom)
5515 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5518 case 0x169c: // steel wall (right/top)
5519 element = EL_DC_STEELWALL_1_TOPRIGHT;
5522 case 0x169d: // steel wall (left/top)
5523 element = EL_DC_STEELWALL_1_TOPLEFT;
5526 case 0x169e: // steel wall (right/bottom small)
5527 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5530 case 0x169f: // steel wall (left/bottom small)
5531 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5534 case 0x16a0: // steel wall (right/top small)
5535 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5538 case 0x16a1: // steel wall (left/top small)
5539 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5542 case 0x16a2: // steel wall (left/right)
5543 element = EL_DC_STEELWALL_1_VERTICAL;
5546 case 0x16a3: // steel wall (top/bottom)
5547 element = EL_DC_STEELWALL_1_HORIZONTAL;
5550 case 0x16a4: // steel wall 2 (left end)
5551 element = EL_DC_STEELWALL_2_LEFT;
5554 case 0x16a5: // steel wall 2 (right end)
5555 element = EL_DC_STEELWALL_2_RIGHT;
5558 case 0x16a6: // steel wall 2 (top end)
5559 element = EL_DC_STEELWALL_2_TOP;
5562 case 0x16a7: // steel wall 2 (bottom end)
5563 element = EL_DC_STEELWALL_2_BOTTOM;
5566 case 0x16a8: // steel wall 2 (left/right)
5567 element = EL_DC_STEELWALL_2_HORIZONTAL;
5570 case 0x16a9: // steel wall 2 (up/down)
5571 element = EL_DC_STEELWALL_2_VERTICAL;
5574 case 0x16aa: // steel wall 2 (mid)
5575 element = EL_DC_STEELWALL_2_MIDDLE;
5579 element = EL_SIGN_EXCLAMATION;
5583 element = EL_SIGN_RADIOACTIVITY;
5587 element = EL_SIGN_STOP;
5591 element = EL_SIGN_WHEELCHAIR;
5595 element = EL_SIGN_PARKING;
5599 element = EL_SIGN_NO_ENTRY;
5603 element = EL_SIGN_HEART;
5607 element = EL_SIGN_GIVE_WAY;
5611 element = EL_SIGN_ENTRY_FORBIDDEN;
5615 element = EL_SIGN_EMERGENCY_EXIT;
5619 element = EL_SIGN_YIN_YANG;
5623 element = EL_WALL_EMERALD;
5627 element = EL_WALL_DIAMOND;
5631 element = EL_WALL_PEARL;
5635 element = EL_WALL_CRYSTAL;
5639 element = EL_INVISIBLE_WALL;
5643 element = EL_INVISIBLE_STEELWALL;
5647 // EL_INVISIBLE_SAND
5650 element = EL_LIGHT_SWITCH;
5654 element = EL_ENVELOPE_1;
5658 if (element >= 0x0117 && element <= 0x036e) // (?)
5659 element = EL_DIAMOND;
5660 else if (element >= 0x042d && element <= 0x0684) // (?)
5661 element = EL_EMERALD;
5662 else if (element >= 0x157c && element <= 0x158b)
5664 else if (element >= 0x1590 && element <= 0x159f)
5665 element = EL_DC_LANDMINE;
5666 else if (element >= 0x16bc && element <= 0x16cb)
5667 element = EL_INVISIBLE_SAND;
5670 Warn("unknown Diamond Caves element 0x%04x", element);
5672 element = EL_UNKNOWN;
5677 return getMappedElement(element);
5680 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5683 byte header[DC_LEVEL_HEADER_SIZE];
5685 int envelope_header_pos = 62;
5686 int envelope_content_pos = 94;
5687 int level_name_pos = 251;
5688 int level_author_pos = 292;
5689 int envelope_header_len;
5690 int envelope_content_len;
5692 int level_author_len;
5694 int num_yamyam_contents;
5697 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5699 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5701 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5703 header[i * 2 + 0] = header_word >> 8;
5704 header[i * 2 + 1] = header_word & 0xff;
5707 // read some values from level header to check level decoding integrity
5708 fieldx = header[6] | (header[7] << 8);
5709 fieldy = header[8] | (header[9] << 8);
5710 num_yamyam_contents = header[60] | (header[61] << 8);
5712 // do some simple sanity checks to ensure that level was correctly decoded
5713 if (fieldx < 1 || fieldx > 256 ||
5714 fieldy < 1 || fieldy > 256 ||
5715 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5717 level->no_valid_file = TRUE;
5719 Warn("cannot decode level from stream -- using empty level");
5724 // maximum envelope header size is 31 bytes
5725 envelope_header_len = header[envelope_header_pos];
5726 // maximum envelope content size is 110 (156?) bytes
5727 envelope_content_len = header[envelope_content_pos];
5729 // maximum level title size is 40 bytes
5730 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5731 // maximum level author size is 30 (51?) bytes
5732 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5736 for (i = 0; i < envelope_header_len; i++)
5737 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5738 level->envelope[0].text[envelope_size++] =
5739 header[envelope_header_pos + 1 + i];
5741 if (envelope_header_len > 0 && envelope_content_len > 0)
5743 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5744 level->envelope[0].text[envelope_size++] = '\n';
5745 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5746 level->envelope[0].text[envelope_size++] = '\n';
5749 for (i = 0; i < envelope_content_len; i++)
5750 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5751 level->envelope[0].text[envelope_size++] =
5752 header[envelope_content_pos + 1 + i];
5754 level->envelope[0].text[envelope_size] = '\0';
5756 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5757 level->envelope[0].ysize = 10;
5758 level->envelope[0].autowrap = TRUE;
5759 level->envelope[0].centered = TRUE;
5761 for (i = 0; i < level_name_len; i++)
5762 level->name[i] = header[level_name_pos + 1 + i];
5763 level->name[level_name_len] = '\0';
5765 for (i = 0; i < level_author_len; i++)
5766 level->author[i] = header[level_author_pos + 1 + i];
5767 level->author[level_author_len] = '\0';
5769 num_yamyam_contents = header[60] | (header[61] << 8);
5770 level->num_yamyam_contents =
5771 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5773 for (i = 0; i < num_yamyam_contents; i++)
5775 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5777 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5778 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5780 if (i < MAX_ELEMENT_CONTENTS)
5781 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5785 fieldx = header[6] | (header[7] << 8);
5786 fieldy = header[8] | (header[9] << 8);
5787 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5788 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5790 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5792 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5793 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5795 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5796 level->field[x][y] = getMappedElement_DC(element_dc);
5799 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5800 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5801 level->field[x][y] = EL_PLAYER_1;
5803 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5804 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5805 level->field[x][y] = EL_PLAYER_2;
5807 level->gems_needed = header[18] | (header[19] << 8);
5809 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5810 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5811 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5812 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5813 level->score[SC_NUT] = header[28] | (header[29] << 8);
5814 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5815 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5816 level->score[SC_BUG] = header[34] | (header[35] << 8);
5817 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5818 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5819 level->score[SC_KEY] = header[40] | (header[41] << 8);
5820 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5822 level->time = header[44] | (header[45] << 8);
5824 level->amoeba_speed = header[46] | (header[47] << 8);
5825 level->time_light = header[48] | (header[49] << 8);
5826 level->time_timegate = header[50] | (header[51] << 8);
5827 level->time_wheel = header[52] | (header[53] << 8);
5828 level->time_magic_wall = header[54] | (header[55] << 8);
5829 level->extra_time = header[56] | (header[57] << 8);
5830 level->shield_normal_time = header[58] | (header[59] << 8);
5832 // shield and extra time elements do not have a score
5833 level->score[SC_SHIELD] = 0;
5834 level->extra_time_score = 0;
5836 // set time for normal and deadly shields to the same value
5837 level->shield_deadly_time = level->shield_normal_time;
5839 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5840 // can slip down from flat walls, like normal walls and steel walls
5841 level->em_slippery_gems = TRUE;
5843 // time score is counted for each 10 seconds left in Diamond Caves levels
5844 level->time_score_base = 10;
5847 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5848 struct LevelFileInfo *level_file_info,
5849 boolean level_info_only)
5851 char *filename = level_file_info->filename;
5853 int num_magic_bytes = 8;
5854 char magic_bytes[num_magic_bytes + 1];
5855 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5857 if (!(file = openFile(filename, MODE_READ)))
5859 level->no_valid_file = TRUE;
5861 if (!level_info_only)
5862 Warn("cannot read level '%s' -- using empty level", filename);
5867 // fseek(file, 0x0000, SEEK_SET);
5869 if (level_file_info->packed)
5871 // read "magic bytes" from start of file
5872 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5873 magic_bytes[0] = '\0';
5875 // check "magic bytes" for correct file format
5876 if (!strPrefix(magic_bytes, "DC2"))
5878 level->no_valid_file = TRUE;
5880 Warn("unknown DC level file '%s' -- using empty level", filename);
5885 if (strPrefix(magic_bytes, "DC2Win95") ||
5886 strPrefix(magic_bytes, "DC2Win98"))
5888 int position_first_level = 0x00fa;
5889 int extra_bytes = 4;
5892 // advance file stream to first level inside the level package
5893 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5895 // each block of level data is followed by block of non-level data
5896 num_levels_to_skip *= 2;
5898 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5899 while (num_levels_to_skip >= 0)
5901 // advance file stream to next level inside the level package
5902 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5904 level->no_valid_file = TRUE;
5906 Warn("cannot fseek in file '%s' -- using empty level", filename);
5911 // skip apparently unused extra bytes following each level
5912 ReadUnusedBytesFromFile(file, extra_bytes);
5914 // read size of next level in level package
5915 skip_bytes = getFile32BitLE(file);
5917 num_levels_to_skip--;
5922 level->no_valid_file = TRUE;
5924 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5930 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5936 // ----------------------------------------------------------------------------
5937 // functions for loading SB level
5938 // ----------------------------------------------------------------------------
5940 int getMappedElement_SB(int element_ascii, boolean use_ces)
5948 sb_element_mapping[] =
5950 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5951 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5952 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5953 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5954 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5955 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5956 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5957 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5964 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5965 if (element_ascii == sb_element_mapping[i].ascii)
5966 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5968 return EL_UNDEFINED;
5971 static void SetLevelSettings_SB(struct LevelInfo *level)
5975 level->use_step_counter = TRUE;
5978 level->score[SC_TIME_BONUS] = 0;
5979 level->time_score_base = 1;
5980 level->rate_time_over_score = TRUE;
5983 level->auto_exit_sokoban = TRUE;
5986 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5987 struct LevelFileInfo *level_file_info,
5988 boolean level_info_only)
5990 char *filename = level_file_info->filename;
5991 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5992 char last_comment[MAX_LINE_LEN];
5993 char level_name[MAX_LINE_LEN];
5996 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5997 boolean read_continued_line = FALSE;
5998 boolean reading_playfield = FALSE;
5999 boolean got_valid_playfield_line = FALSE;
6000 boolean invalid_playfield_char = FALSE;
6001 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6002 int file_level_nr = 0;
6004 int x = 0, y = 0; // initialized to make compilers happy
6006 last_comment[0] = '\0';
6007 level_name[0] = '\0';
6009 if (!(file = openFile(filename, MODE_READ)))
6011 level->no_valid_file = TRUE;
6013 if (!level_info_only)
6014 Warn("cannot read level '%s' -- using empty level", filename);
6019 while (!checkEndOfFile(file))
6021 // level successfully read, but next level may follow here
6022 if (!got_valid_playfield_line && reading_playfield)
6024 // read playfield from single level file -- skip remaining file
6025 if (!level_file_info->packed)
6028 if (file_level_nr >= num_levels_to_skip)
6033 last_comment[0] = '\0';
6034 level_name[0] = '\0';
6036 reading_playfield = FALSE;
6039 got_valid_playfield_line = FALSE;
6041 // read next line of input file
6042 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6045 // check if line was completely read and is terminated by line break
6046 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6049 // cut trailing line break (this can be newline and/or carriage return)
6050 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6051 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6054 // copy raw input line for later use (mainly debugging output)
6055 strcpy(line_raw, line);
6057 if (read_continued_line)
6059 // append new line to existing line, if there is enough space
6060 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6061 strcat(previous_line, line_ptr);
6063 strcpy(line, previous_line); // copy storage buffer to line
6065 read_continued_line = FALSE;
6068 // if the last character is '\', continue at next line
6069 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6071 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6072 strcpy(previous_line, line); // copy line to storage buffer
6074 read_continued_line = TRUE;
6080 if (line[0] == '\0')
6083 // extract comment text from comment line
6086 for (line_ptr = line; *line_ptr; line_ptr++)
6087 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6090 strcpy(last_comment, line_ptr);
6095 // extract level title text from line containing level title
6096 if (line[0] == '\'')
6098 strcpy(level_name, &line[1]);
6100 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6101 level_name[strlen(level_name) - 1] = '\0';
6106 // skip lines containing only spaces (or empty lines)
6107 for (line_ptr = line; *line_ptr; line_ptr++)
6108 if (*line_ptr != ' ')
6110 if (*line_ptr == '\0')
6113 // at this point, we have found a line containing part of a playfield
6115 got_valid_playfield_line = TRUE;
6117 if (!reading_playfield)
6119 reading_playfield = TRUE;
6120 invalid_playfield_char = FALSE;
6122 for (x = 0; x < MAX_LEV_FIELDX; x++)
6123 for (y = 0; y < MAX_LEV_FIELDY; y++)
6124 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6129 // start with topmost tile row
6133 // skip playfield line if larger row than allowed
6134 if (y >= MAX_LEV_FIELDY)
6137 // start with leftmost tile column
6140 // read playfield elements from line
6141 for (line_ptr = line; *line_ptr; line_ptr++)
6143 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6145 // stop parsing playfield line if larger column than allowed
6146 if (x >= MAX_LEV_FIELDX)
6149 if (mapped_sb_element == EL_UNDEFINED)
6151 invalid_playfield_char = TRUE;
6156 level->field[x][y] = mapped_sb_element;
6158 // continue with next tile column
6161 level->fieldx = MAX(x, level->fieldx);
6164 if (invalid_playfield_char)
6166 // if first playfield line, treat invalid lines as comment lines
6168 reading_playfield = FALSE;
6173 // continue with next tile row
6181 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6182 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6184 if (!reading_playfield)
6186 level->no_valid_file = TRUE;
6188 Warn("cannot read level '%s' -- using empty level", filename);
6193 if (*level_name != '\0')
6195 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6196 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6198 else if (*last_comment != '\0')
6200 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6201 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6205 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6208 // set all empty fields beyond the border walls to invisible steel wall
6209 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6211 if ((x == 0 || x == level->fieldx - 1 ||
6212 y == 0 || y == level->fieldy - 1) &&
6213 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6214 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6215 level->field, level->fieldx, level->fieldy);
6218 // set special level settings for Sokoban levels
6219 SetLevelSettings_SB(level);
6221 if (load_xsb_to_ces)
6223 // special global settings can now be set in level template
6224 level->use_custom_template = TRUE;
6229 // -------------------------------------------------------------------------
6230 // functions for handling native levels
6231 // -------------------------------------------------------------------------
6233 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6234 struct LevelFileInfo *level_file_info,
6235 boolean level_info_only)
6237 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6238 level->no_valid_file = TRUE;
6241 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6242 struct LevelFileInfo *level_file_info,
6243 boolean level_info_only)
6247 // determine position of requested level inside level package
6248 if (level_file_info->packed)
6249 pos = level_file_info->nr - leveldir_current->first_level;
6251 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6252 level->no_valid_file = TRUE;
6255 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6256 struct LevelFileInfo *level_file_info,
6257 boolean level_info_only)
6259 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6260 level->no_valid_file = TRUE;
6263 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6265 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6266 CopyNativeLevel_RND_to_EM(level);
6267 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6268 CopyNativeLevel_RND_to_SP(level);
6269 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6270 CopyNativeLevel_RND_to_MM(level);
6273 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6275 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6276 CopyNativeLevel_EM_to_RND(level);
6277 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6278 CopyNativeLevel_SP_to_RND(level);
6279 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6280 CopyNativeLevel_MM_to_RND(level);
6283 void SaveNativeLevel(struct LevelInfo *level)
6285 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6287 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6288 char *filename = getLevelFilenameFromBasename(basename);
6290 CopyNativeLevel_RND_to_SP(level);
6291 CopyNativeTape_RND_to_SP(level);
6293 SaveNativeLevel_SP(filename);
6298 // ----------------------------------------------------------------------------
6299 // functions for loading generic level
6300 // ----------------------------------------------------------------------------
6302 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6303 struct LevelFileInfo *level_file_info,
6304 boolean level_info_only)
6306 // always start with reliable default values
6307 setLevelInfoToDefaults(level, level_info_only, TRUE);
6309 switch (level_file_info->type)
6311 case LEVEL_FILE_TYPE_RND:
6312 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6315 case LEVEL_FILE_TYPE_EM:
6316 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6317 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6320 case LEVEL_FILE_TYPE_SP:
6321 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6322 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6325 case LEVEL_FILE_TYPE_MM:
6326 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6327 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6330 case LEVEL_FILE_TYPE_DC:
6331 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6334 case LEVEL_FILE_TYPE_SB:
6335 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6339 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6343 // if level file is invalid, restore level structure to default values
6344 if (level->no_valid_file)
6345 setLevelInfoToDefaults(level, level_info_only, FALSE);
6347 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6348 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6350 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6351 CopyNativeLevel_Native_to_RND(level);
6354 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6356 static struct LevelFileInfo level_file_info;
6358 // always start with reliable default values
6359 setFileInfoToDefaults(&level_file_info);
6361 level_file_info.nr = 0; // unknown level number
6362 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6364 setString(&level_file_info.filename, filename);
6366 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6369 static void LoadLevel_InitVersion(struct LevelInfo *level)
6373 if (leveldir_current == NULL) // only when dumping level
6376 // all engine modifications also valid for levels which use latest engine
6377 if (level->game_version < VERSION_IDENT(3,2,0,5))
6379 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6380 level->time_score_base = 10;
6383 if (leveldir_current->latest_engine)
6385 // ---------- use latest game engine --------------------------------------
6387 /* For all levels which are forced to use the latest game engine version
6388 (normally all but user contributed, private and undefined levels), set
6389 the game engine version to the actual version; this allows for actual
6390 corrections in the game engine to take effect for existing, converted
6391 levels (from "classic" or other existing games) to make the emulation
6392 of the corresponding game more accurate, while (hopefully) not breaking
6393 existing levels created from other players. */
6395 level->game_version = GAME_VERSION_ACTUAL;
6397 /* Set special EM style gems behaviour: EM style gems slip down from
6398 normal, steel and growing wall. As this is a more fundamental change,
6399 it seems better to set the default behaviour to "off" (as it is more
6400 natural) and make it configurable in the level editor (as a property
6401 of gem style elements). Already existing converted levels (neither
6402 private nor contributed levels) are changed to the new behaviour. */
6404 if (level->file_version < FILE_VERSION_2_0)
6405 level->em_slippery_gems = TRUE;
6410 // ---------- use game engine the level was created with --------------------
6412 /* For all levels which are not forced to use the latest game engine
6413 version (normally user contributed, private and undefined levels),
6414 use the version of the game engine the levels were created for.
6416 Since 2.0.1, the game engine version is now directly stored
6417 in the level file (chunk "VERS"), so there is no need anymore
6418 to set the game version from the file version (except for old,
6419 pre-2.0 levels, where the game version is still taken from the
6420 file format version used to store the level -- see above). */
6422 // player was faster than enemies in 1.0.0 and before
6423 if (level->file_version == FILE_VERSION_1_0)
6424 for (i = 0; i < MAX_PLAYERS; i++)
6425 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6427 // default behaviour for EM style gems was "slippery" only in 2.0.1
6428 if (level->game_version == VERSION_IDENT(2,0,1,0))
6429 level->em_slippery_gems = TRUE;
6431 // springs could be pushed over pits before (pre-release version) 2.2.0
6432 if (level->game_version < VERSION_IDENT(2,2,0,0))
6433 level->use_spring_bug = TRUE;
6435 if (level->game_version < VERSION_IDENT(3,2,0,5))
6437 // time orb caused limited time in endless time levels before 3.2.0-5
6438 level->use_time_orb_bug = TRUE;
6440 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6441 level->block_snap_field = FALSE;
6443 // extra time score was same value as time left score before 3.2.0-5
6444 level->extra_time_score = level->score[SC_TIME_BONUS];
6447 if (level->game_version < VERSION_IDENT(3,2,0,7))
6449 // default behaviour for snapping was "not continuous" before 3.2.0-7
6450 level->continuous_snapping = FALSE;
6453 // only few elements were able to actively move into acid before 3.1.0
6454 // trigger settings did not exist before 3.1.0; set to default "any"
6455 if (level->game_version < VERSION_IDENT(3,1,0,0))
6457 // correct "can move into acid" settings (all zero in old levels)
6459 level->can_move_into_acid_bits = 0; // nothing can move into acid
6460 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6462 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6463 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6464 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6465 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6467 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6468 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6470 // correct trigger settings (stored as zero == "none" in old levels)
6472 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6474 int element = EL_CUSTOM_START + i;
6475 struct ElementInfo *ei = &element_info[element];
6477 for (j = 0; j < ei->num_change_pages; j++)
6479 struct ElementChangeInfo *change = &ei->change_page[j];
6481 change->trigger_player = CH_PLAYER_ANY;
6482 change->trigger_page = CH_PAGE_ANY;
6487 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6489 int element = EL_CUSTOM_256;
6490 struct ElementInfo *ei = &element_info[element];
6491 struct ElementChangeInfo *change = &ei->change_page[0];
6493 /* This is needed to fix a problem that was caused by a bugfix in function
6494 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6495 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6496 not replace walkable elements, but instead just placed the player on it,
6497 without placing the Sokoban field under the player). Unfortunately, this
6498 breaks "Snake Bite" style levels when the snake is halfway through a door
6499 that just closes (the snake head is still alive and can be moved in this
6500 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6501 player (without Sokoban element) which then gets killed as designed). */
6503 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6504 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6505 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6506 change->target_element = EL_PLAYER_1;
6509 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6510 if (level->game_version < VERSION_IDENT(3,2,5,0))
6512 /* This is needed to fix a problem that was caused by a bugfix in function
6513 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6514 corrects the behaviour when a custom element changes to another custom
6515 element with a higher element number that has change actions defined.
6516 Normally, only one change per frame is allowed for custom elements.
6517 Therefore, it is checked if a custom element already changed in the
6518 current frame; if it did, subsequent changes are suppressed.
6519 Unfortunately, this is only checked for element changes, but not for
6520 change actions, which are still executed. As the function above loops
6521 through all custom elements from lower to higher, an element change
6522 resulting in a lower CE number won't be checked again, while a target
6523 element with a higher number will also be checked, and potential change
6524 actions will get executed for this CE, too (which is wrong), while
6525 further changes are ignored (which is correct). As this bugfix breaks
6526 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6527 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6528 behaviour for existing levels and tapes that make use of this bug */
6530 level->use_action_after_change_bug = TRUE;
6533 // not centering level after relocating player was default only in 3.2.3
6534 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6535 level->shifted_relocation = TRUE;
6537 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6538 if (level->game_version < VERSION_IDENT(3,2,6,0))
6539 level->em_explodes_by_fire = TRUE;
6541 // levels were solved by the first player entering an exit up to 4.1.0.0
6542 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6543 level->solved_by_one_player = TRUE;
6545 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6546 if (level->game_version < VERSION_IDENT(4,1,1,1))
6547 level->use_life_bugs = TRUE;
6549 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6550 if (level->game_version < VERSION_IDENT(4,1,1,1))
6551 level->sb_objects_needed = FALSE;
6553 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6554 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6555 level->finish_dig_collect = FALSE;
6557 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6558 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6559 level->keep_walkable_ce = TRUE;
6562 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6564 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6567 // check if this level is (not) a Sokoban level
6568 for (y = 0; y < level->fieldy; y++)
6569 for (x = 0; x < level->fieldx; x++)
6570 if (!IS_SB_ELEMENT(Tile[x][y]))
6571 is_sokoban_level = FALSE;
6573 if (is_sokoban_level)
6575 // set special level settings for Sokoban levels
6576 SetLevelSettings_SB(level);
6580 static void LoadLevel_InitSettings(struct LevelInfo *level)
6582 // adjust level settings for (non-native) Sokoban-style levels
6583 LoadLevel_InitSettings_SB(level);
6585 // rename levels with title "nameless level" or if renaming is forced
6586 if (leveldir_current->empty_level_name != NULL &&
6587 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6588 leveldir_current->force_level_name))
6589 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6590 leveldir_current->empty_level_name, level_nr);
6593 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6597 // map elements that have changed in newer versions
6598 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6599 level->game_version);
6600 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6601 for (x = 0; x < 3; x++)
6602 for (y = 0; y < 3; y++)
6603 level->yamyam_content[i].e[x][y] =
6604 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6605 level->game_version);
6609 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6613 // map custom element change events that have changed in newer versions
6614 // (these following values were accidentally changed in version 3.0.1)
6615 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6616 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6618 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6620 int element = EL_CUSTOM_START + i;
6622 // order of checking and copying events to be mapped is important
6623 // (do not change the start and end value -- they are constant)
6624 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6626 if (HAS_CHANGE_EVENT(element, j - 2))
6628 SET_CHANGE_EVENT(element, j - 2, FALSE);
6629 SET_CHANGE_EVENT(element, j, TRUE);
6633 // order of checking and copying events to be mapped is important
6634 // (do not change the start and end value -- they are constant)
6635 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6637 if (HAS_CHANGE_EVENT(element, j - 1))
6639 SET_CHANGE_EVENT(element, j - 1, FALSE);
6640 SET_CHANGE_EVENT(element, j, TRUE);
6646 // initialize "can_change" field for old levels with only one change page
6647 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6649 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6651 int element = EL_CUSTOM_START + i;
6653 if (CAN_CHANGE(element))
6654 element_info[element].change->can_change = TRUE;
6658 // correct custom element values (for old levels without these options)
6659 if (level->game_version < VERSION_IDENT(3,1,1,0))
6661 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6663 int element = EL_CUSTOM_START + i;
6664 struct ElementInfo *ei = &element_info[element];
6666 if (ei->access_direction == MV_NO_DIRECTION)
6667 ei->access_direction = MV_ALL_DIRECTIONS;
6671 // correct custom element values (fix invalid values for all versions)
6674 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6676 int element = EL_CUSTOM_START + i;
6677 struct ElementInfo *ei = &element_info[element];
6679 for (j = 0; j < ei->num_change_pages; j++)
6681 struct ElementChangeInfo *change = &ei->change_page[j];
6683 if (change->trigger_player == CH_PLAYER_NONE)
6684 change->trigger_player = CH_PLAYER_ANY;
6686 if (change->trigger_side == CH_SIDE_NONE)
6687 change->trigger_side = CH_SIDE_ANY;
6692 // initialize "can_explode" field for old levels which did not store this
6693 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6694 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6696 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6698 int element = EL_CUSTOM_START + i;
6700 if (EXPLODES_1X1_OLD(element))
6701 element_info[element].explosion_type = EXPLODES_1X1;
6703 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6704 EXPLODES_SMASHED(element) ||
6705 EXPLODES_IMPACT(element)));
6709 // correct previously hard-coded move delay values for maze runner style
6710 if (level->game_version < VERSION_IDENT(3,1,1,0))
6712 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6714 int element = EL_CUSTOM_START + i;
6716 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6718 // previously hard-coded and therefore ignored
6719 element_info[element].move_delay_fixed = 9;
6720 element_info[element].move_delay_random = 0;
6725 // set some other uninitialized values of custom elements in older levels
6726 if (level->game_version < VERSION_IDENT(3,1,0,0))
6728 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6730 int element = EL_CUSTOM_START + i;
6732 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6734 element_info[element].explosion_delay = 17;
6735 element_info[element].ignition_delay = 8;
6739 // set mouse click change events to work for left/middle/right mouse button
6740 if (level->game_version < VERSION_IDENT(4,2,3,0))
6742 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6744 int element = EL_CUSTOM_START + i;
6745 struct ElementInfo *ei = &element_info[element];
6747 for (j = 0; j < ei->num_change_pages; j++)
6749 struct ElementChangeInfo *change = &ei->change_page[j];
6751 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6752 change->has_event[CE_PRESSED_BY_MOUSE] ||
6753 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6754 change->has_event[CE_MOUSE_PRESSED_ON_X])
6755 change->trigger_side = CH_SIDE_ANY;
6761 static void LoadLevel_InitElements(struct LevelInfo *level)
6763 LoadLevel_InitStandardElements(level);
6765 if (level->file_has_custom_elements)
6766 LoadLevel_InitCustomElements(level);
6768 // initialize element properties for level editor etc.
6769 InitElementPropertiesEngine(level->game_version);
6770 InitElementPropertiesGfxElement();
6773 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6777 // map elements that have changed in newer versions
6778 for (y = 0; y < level->fieldy; y++)
6779 for (x = 0; x < level->fieldx; x++)
6780 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6781 level->game_version);
6783 // clear unused playfield data (nicer if level gets resized in editor)
6784 for (x = 0; x < MAX_LEV_FIELDX; x++)
6785 for (y = 0; y < MAX_LEV_FIELDY; y++)
6786 if (x >= level->fieldx || y >= level->fieldy)
6787 level->field[x][y] = EL_EMPTY;
6789 // copy elements to runtime playfield array
6790 for (x = 0; x < MAX_LEV_FIELDX; x++)
6791 for (y = 0; y < MAX_LEV_FIELDY; y++)
6792 Tile[x][y] = level->field[x][y];
6794 // initialize level size variables for faster access
6795 lev_fieldx = level->fieldx;
6796 lev_fieldy = level->fieldy;
6798 // determine border element for this level
6799 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6800 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6805 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6807 struct LevelFileInfo *level_file_info = &level->file_info;
6809 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6810 CopyNativeLevel_RND_to_Native(level);
6813 static void LoadLevelTemplate_LoadAndInit(void)
6815 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6817 LoadLevel_InitVersion(&level_template);
6818 LoadLevel_InitElements(&level_template);
6819 LoadLevel_InitSettings(&level_template);
6821 ActivateLevelTemplate();
6824 void LoadLevelTemplate(int nr)
6826 if (!fileExists(getGlobalLevelTemplateFilename()))
6828 Warn("no level template found for this level");
6833 setLevelFileInfo(&level_template.file_info, nr);
6835 LoadLevelTemplate_LoadAndInit();
6838 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6840 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6842 LoadLevelTemplate_LoadAndInit();
6845 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6847 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6849 if (level.use_custom_template)
6851 if (network_level != NULL)
6852 LoadNetworkLevelTemplate(network_level);
6854 LoadLevelTemplate(-1);
6857 LoadLevel_InitVersion(&level);
6858 LoadLevel_InitElements(&level);
6859 LoadLevel_InitPlayfield(&level);
6860 LoadLevel_InitSettings(&level);
6862 LoadLevel_InitNativeEngines(&level);
6865 void LoadLevel(int nr)
6867 SetLevelSetInfo(leveldir_current->identifier, nr);
6869 setLevelFileInfo(&level.file_info, nr);
6871 LoadLevel_LoadAndInit(NULL);
6874 void LoadLevelInfoOnly(int nr)
6876 setLevelFileInfo(&level.file_info, nr);
6878 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6881 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6883 SetLevelSetInfo(network_level->leveldir_identifier,
6884 network_level->file_info.nr);
6886 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6888 LoadLevel_LoadAndInit(network_level);
6891 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6895 chunk_size += putFileVersion(file, level->file_version);
6896 chunk_size += putFileVersion(file, level->game_version);
6901 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6905 chunk_size += putFile16BitBE(file, level->creation_date.year);
6906 chunk_size += putFile8Bit(file, level->creation_date.month);
6907 chunk_size += putFile8Bit(file, level->creation_date.day);
6912 #if ENABLE_HISTORIC_CHUNKS
6913 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6917 putFile8Bit(file, level->fieldx);
6918 putFile8Bit(file, level->fieldy);
6920 putFile16BitBE(file, level->time);
6921 putFile16BitBE(file, level->gems_needed);
6923 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6924 putFile8Bit(file, level->name[i]);
6926 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6927 putFile8Bit(file, level->score[i]);
6929 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6930 for (y = 0; y < 3; y++)
6931 for (x = 0; x < 3; x++)
6932 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6933 level->yamyam_content[i].e[x][y]));
6934 putFile8Bit(file, level->amoeba_speed);
6935 putFile8Bit(file, level->time_magic_wall);
6936 putFile8Bit(file, level->time_wheel);
6937 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6938 level->amoeba_content));
6939 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6940 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6941 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6942 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6944 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6946 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6947 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6948 putFile32BitBE(file, level->can_move_into_acid_bits);
6949 putFile8Bit(file, level->dont_collide_with_bits);
6951 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6952 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6954 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6955 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6956 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6958 putFile8Bit(file, level->game_engine_type);
6960 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6964 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6969 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6970 chunk_size += putFile8Bit(file, level->name[i]);
6975 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6980 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6981 chunk_size += putFile8Bit(file, level->author[i]);
6986 #if ENABLE_HISTORIC_CHUNKS
6987 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6992 for (y = 0; y < level->fieldy; y++)
6993 for (x = 0; x < level->fieldx; x++)
6994 if (level->encoding_16bit_field)
6995 chunk_size += putFile16BitBE(file, level->field[x][y]);
6997 chunk_size += putFile8Bit(file, level->field[x][y]);
7003 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7008 for (y = 0; y < level->fieldy; y++)
7009 for (x = 0; x < level->fieldx; x++)
7010 chunk_size += putFile16BitBE(file, level->field[x][y]);
7015 #if ENABLE_HISTORIC_CHUNKS
7016 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7020 putFile8Bit(file, EL_YAMYAM);
7021 putFile8Bit(file, level->num_yamyam_contents);
7022 putFile8Bit(file, 0);
7023 putFile8Bit(file, 0);
7025 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7026 for (y = 0; y < 3; y++)
7027 for (x = 0; x < 3; x++)
7028 if (level->encoding_16bit_field)
7029 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7031 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7035 #if ENABLE_HISTORIC_CHUNKS
7036 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7039 int num_contents, content_xsize, content_ysize;
7040 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7042 if (element == EL_YAMYAM)
7044 num_contents = level->num_yamyam_contents;
7048 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7049 for (y = 0; y < 3; y++)
7050 for (x = 0; x < 3; x++)
7051 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7053 else if (element == EL_BD_AMOEBA)
7059 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7060 for (y = 0; y < 3; y++)
7061 for (x = 0; x < 3; x++)
7062 content_array[i][x][y] = EL_EMPTY;
7063 content_array[0][0][0] = level->amoeba_content;
7067 // chunk header already written -- write empty chunk data
7068 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7070 Warn("cannot save content for element '%d'", element);
7075 putFile16BitBE(file, element);
7076 putFile8Bit(file, num_contents);
7077 putFile8Bit(file, content_xsize);
7078 putFile8Bit(file, content_ysize);
7080 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7082 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7083 for (y = 0; y < 3; y++)
7084 for (x = 0; x < 3; x++)
7085 putFile16BitBE(file, content_array[i][x][y]);
7089 #if ENABLE_HISTORIC_CHUNKS
7090 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7092 int envelope_nr = element - EL_ENVELOPE_1;
7093 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7097 chunk_size += putFile16BitBE(file, element);
7098 chunk_size += putFile16BitBE(file, envelope_len);
7099 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7100 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7102 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7103 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7105 for (i = 0; i < envelope_len; i++)
7106 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7112 #if ENABLE_HISTORIC_CHUNKS
7113 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7114 int num_changed_custom_elements)
7118 putFile16BitBE(file, num_changed_custom_elements);
7120 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7122 int element = EL_CUSTOM_START + i;
7124 struct ElementInfo *ei = &element_info[element];
7126 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7128 if (check < num_changed_custom_elements)
7130 putFile16BitBE(file, element);
7131 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7138 if (check != num_changed_custom_elements) // should not happen
7139 Warn("inconsistent number of custom element properties");
7143 #if ENABLE_HISTORIC_CHUNKS
7144 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7145 int num_changed_custom_elements)
7149 putFile16BitBE(file, num_changed_custom_elements);
7151 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7153 int element = EL_CUSTOM_START + i;
7155 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7157 if (check < num_changed_custom_elements)
7159 putFile16BitBE(file, element);
7160 putFile16BitBE(file, element_info[element].change->target_element);
7167 if (check != num_changed_custom_elements) // should not happen
7168 Warn("inconsistent number of custom target elements");
7172 #if ENABLE_HISTORIC_CHUNKS
7173 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7174 int num_changed_custom_elements)
7176 int i, j, x, y, check = 0;
7178 putFile16BitBE(file, num_changed_custom_elements);
7180 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7182 int element = EL_CUSTOM_START + i;
7183 struct ElementInfo *ei = &element_info[element];
7185 if (ei->modified_settings)
7187 if (check < num_changed_custom_elements)
7189 putFile16BitBE(file, element);
7191 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7192 putFile8Bit(file, ei->description[j]);
7194 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7196 // some free bytes for future properties and padding
7197 WriteUnusedBytesToFile(file, 7);
7199 putFile8Bit(file, ei->use_gfx_element);
7200 putFile16BitBE(file, ei->gfx_element_initial);
7202 putFile8Bit(file, ei->collect_score_initial);
7203 putFile8Bit(file, ei->collect_count_initial);
7205 putFile16BitBE(file, ei->push_delay_fixed);
7206 putFile16BitBE(file, ei->push_delay_random);
7207 putFile16BitBE(file, ei->move_delay_fixed);
7208 putFile16BitBE(file, ei->move_delay_random);
7210 putFile16BitBE(file, ei->move_pattern);
7211 putFile8Bit(file, ei->move_direction_initial);
7212 putFile8Bit(file, ei->move_stepsize);
7214 for (y = 0; y < 3; y++)
7215 for (x = 0; x < 3; x++)
7216 putFile16BitBE(file, ei->content.e[x][y]);
7218 putFile32BitBE(file, ei->change->events);
7220 putFile16BitBE(file, ei->change->target_element);
7222 putFile16BitBE(file, ei->change->delay_fixed);
7223 putFile16BitBE(file, ei->change->delay_random);
7224 putFile16BitBE(file, ei->change->delay_frames);
7226 putFile16BitBE(file, ei->change->initial_trigger_element);
7228 putFile8Bit(file, ei->change->explode);
7229 putFile8Bit(file, ei->change->use_target_content);
7230 putFile8Bit(file, ei->change->only_if_complete);
7231 putFile8Bit(file, ei->change->use_random_replace);
7233 putFile8Bit(file, ei->change->random_percentage);
7234 putFile8Bit(file, ei->change->replace_when);
7236 for (y = 0; y < 3; y++)
7237 for (x = 0; x < 3; x++)
7238 putFile16BitBE(file, ei->change->content.e[x][y]);
7240 putFile8Bit(file, ei->slippery_type);
7242 // some free bytes for future properties and padding
7243 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7250 if (check != num_changed_custom_elements) // should not happen
7251 Warn("inconsistent number of custom element properties");
7255 #if ENABLE_HISTORIC_CHUNKS
7256 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7258 struct ElementInfo *ei = &element_info[element];
7261 // ---------- custom element base property values (96 bytes) ----------------
7263 putFile16BitBE(file, element);
7265 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7266 putFile8Bit(file, ei->description[i]);
7268 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7270 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7272 putFile8Bit(file, ei->num_change_pages);
7274 putFile16BitBE(file, ei->ce_value_fixed_initial);
7275 putFile16BitBE(file, ei->ce_value_random_initial);
7276 putFile8Bit(file, ei->use_last_ce_value);
7278 putFile8Bit(file, ei->use_gfx_element);
7279 putFile16BitBE(file, ei->gfx_element_initial);
7281 putFile8Bit(file, ei->collect_score_initial);
7282 putFile8Bit(file, ei->collect_count_initial);
7284 putFile8Bit(file, ei->drop_delay_fixed);
7285 putFile8Bit(file, ei->push_delay_fixed);
7286 putFile8Bit(file, ei->drop_delay_random);
7287 putFile8Bit(file, ei->push_delay_random);
7288 putFile16BitBE(file, ei->move_delay_fixed);
7289 putFile16BitBE(file, ei->move_delay_random);
7291 // bits 0 - 15 of "move_pattern" ...
7292 putFile16BitBE(file, ei->move_pattern & 0xffff);
7293 putFile8Bit(file, ei->move_direction_initial);
7294 putFile8Bit(file, ei->move_stepsize);
7296 putFile8Bit(file, ei->slippery_type);
7298 for (y = 0; y < 3; y++)
7299 for (x = 0; x < 3; x++)
7300 putFile16BitBE(file, ei->content.e[x][y]);
7302 putFile16BitBE(file, ei->move_enter_element);
7303 putFile16BitBE(file, ei->move_leave_element);
7304 putFile8Bit(file, ei->move_leave_type);
7306 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7307 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7309 putFile8Bit(file, ei->access_direction);
7311 putFile8Bit(file, ei->explosion_delay);
7312 putFile8Bit(file, ei->ignition_delay);
7313 putFile8Bit(file, ei->explosion_type);
7315 // some free bytes for future custom property values and padding
7316 WriteUnusedBytesToFile(file, 1);
7318 // ---------- change page property values (48 bytes) ------------------------
7320 for (i = 0; i < ei->num_change_pages; i++)
7322 struct ElementChangeInfo *change = &ei->change_page[i];
7323 unsigned int event_bits;
7325 // bits 0 - 31 of "has_event[]" ...
7327 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7328 if (change->has_event[j])
7329 event_bits |= (1 << j);
7330 putFile32BitBE(file, event_bits);
7332 putFile16BitBE(file, change->target_element);
7334 putFile16BitBE(file, change->delay_fixed);
7335 putFile16BitBE(file, change->delay_random);
7336 putFile16BitBE(file, change->delay_frames);
7338 putFile16BitBE(file, change->initial_trigger_element);
7340 putFile8Bit(file, change->explode);
7341 putFile8Bit(file, change->use_target_content);
7342 putFile8Bit(file, change->only_if_complete);
7343 putFile8Bit(file, change->use_random_replace);
7345 putFile8Bit(file, change->random_percentage);
7346 putFile8Bit(file, change->replace_when);
7348 for (y = 0; y < 3; y++)
7349 for (x = 0; x < 3; x++)
7350 putFile16BitBE(file, change->target_content.e[x][y]);
7352 putFile8Bit(file, change->can_change);
7354 putFile8Bit(file, change->trigger_side);
7356 putFile8Bit(file, change->trigger_player);
7357 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7358 log_2(change->trigger_page)));
7360 putFile8Bit(file, change->has_action);
7361 putFile8Bit(file, change->action_type);
7362 putFile8Bit(file, change->action_mode);
7363 putFile16BitBE(file, change->action_arg);
7365 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7367 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7368 if (change->has_event[j])
7369 event_bits |= (1 << (j - 32));
7370 putFile8Bit(file, event_bits);
7375 #if ENABLE_HISTORIC_CHUNKS
7376 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7378 struct ElementInfo *ei = &element_info[element];
7379 struct ElementGroupInfo *group = ei->group;
7382 putFile16BitBE(file, element);
7384 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7385 putFile8Bit(file, ei->description[i]);
7387 putFile8Bit(file, group->num_elements);
7389 putFile8Bit(file, ei->use_gfx_element);
7390 putFile16BitBE(file, ei->gfx_element_initial);
7392 putFile8Bit(file, group->choice_mode);
7394 // some free bytes for future values and padding
7395 WriteUnusedBytesToFile(file, 3);
7397 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7398 putFile16BitBE(file, group->element[i]);
7402 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7403 boolean write_element)
7405 int save_type = entry->save_type;
7406 int data_type = entry->data_type;
7407 int conf_type = entry->conf_type;
7408 int byte_mask = conf_type & CONF_MASK_BYTES;
7409 int element = entry->element;
7410 int default_value = entry->default_value;
7412 boolean modified = FALSE;
7414 if (byte_mask != CONF_MASK_MULTI_BYTES)
7416 void *value_ptr = entry->value;
7417 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7420 // check if any settings have been modified before saving them
7421 if (value != default_value)
7424 // do not save if explicitly told or if unmodified default settings
7425 if ((save_type == SAVE_CONF_NEVER) ||
7426 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7430 num_bytes += putFile16BitBE(file, element);
7432 num_bytes += putFile8Bit(file, conf_type);
7433 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7434 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7435 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7438 else if (data_type == TYPE_STRING)
7440 char *default_string = entry->default_string;
7441 char *string = (char *)(entry->value);
7442 int string_length = strlen(string);
7445 // check if any settings have been modified before saving them
7446 if (!strEqual(string, default_string))
7449 // do not save if explicitly told or if unmodified default settings
7450 if ((save_type == SAVE_CONF_NEVER) ||
7451 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7455 num_bytes += putFile16BitBE(file, element);
7457 num_bytes += putFile8Bit(file, conf_type);
7458 num_bytes += putFile16BitBE(file, string_length);
7460 for (i = 0; i < string_length; i++)
7461 num_bytes += putFile8Bit(file, string[i]);
7463 else if (data_type == TYPE_ELEMENT_LIST)
7465 int *element_array = (int *)(entry->value);
7466 int num_elements = *(int *)(entry->num_entities);
7469 // check if any settings have been modified before saving them
7470 for (i = 0; i < num_elements; i++)
7471 if (element_array[i] != default_value)
7474 // do not save if explicitly told or if unmodified default settings
7475 if ((save_type == SAVE_CONF_NEVER) ||
7476 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7480 num_bytes += putFile16BitBE(file, element);
7482 num_bytes += putFile8Bit(file, conf_type);
7483 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7485 for (i = 0; i < num_elements; i++)
7486 num_bytes += putFile16BitBE(file, element_array[i]);
7488 else if (data_type == TYPE_CONTENT_LIST)
7490 struct Content *content = (struct Content *)(entry->value);
7491 int num_contents = *(int *)(entry->num_entities);
7494 // check if any settings have been modified before saving them
7495 for (i = 0; i < num_contents; i++)
7496 for (y = 0; y < 3; y++)
7497 for (x = 0; x < 3; x++)
7498 if (content[i].e[x][y] != default_value)
7501 // do not save if explicitly told or if unmodified default settings
7502 if ((save_type == SAVE_CONF_NEVER) ||
7503 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7507 num_bytes += putFile16BitBE(file, element);
7509 num_bytes += putFile8Bit(file, conf_type);
7510 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7512 for (i = 0; i < num_contents; i++)
7513 for (y = 0; y < 3; y++)
7514 for (x = 0; x < 3; x++)
7515 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7521 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7526 li = *level; // copy level data into temporary buffer
7528 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7529 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7534 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7539 li = *level; // copy level data into temporary buffer
7541 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7542 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7547 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7549 int envelope_nr = element - EL_ENVELOPE_1;
7553 chunk_size += putFile16BitBE(file, element);
7555 // copy envelope data into temporary buffer
7556 xx_envelope = level->envelope[envelope_nr];
7558 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7559 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7564 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7566 struct ElementInfo *ei = &element_info[element];
7570 chunk_size += putFile16BitBE(file, element);
7572 xx_ei = *ei; // copy element data into temporary buffer
7574 // set default description string for this specific element
7575 strcpy(xx_default_description, getDefaultElementDescription(ei));
7577 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7578 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7580 for (i = 0; i < ei->num_change_pages; i++)
7582 struct ElementChangeInfo *change = &ei->change_page[i];
7584 xx_current_change_page = i;
7586 xx_change = *change; // copy change data into temporary buffer
7589 setEventBitsFromEventFlags(change);
7591 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7592 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7599 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7601 struct ElementInfo *ei = &element_info[element];
7602 struct ElementGroupInfo *group = ei->group;
7606 chunk_size += putFile16BitBE(file, element);
7608 xx_ei = *ei; // copy element data into temporary buffer
7609 xx_group = *group; // copy group data into temporary buffer
7611 // set default description string for this specific element
7612 strcpy(xx_default_description, getDefaultElementDescription(ei));
7614 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7615 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7620 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7622 struct ElementInfo *ei = &element_info[element];
7626 chunk_size += putFile16BitBE(file, element);
7628 xx_ei = *ei; // copy element data into temporary buffer
7630 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7631 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7636 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7637 boolean save_as_template)
7643 if (!(file = fopen(filename, MODE_WRITE)))
7645 Warn("cannot save level file '%s'", filename);
7650 level->file_version = FILE_VERSION_ACTUAL;
7651 level->game_version = GAME_VERSION_ACTUAL;
7653 level->creation_date = getCurrentDate();
7655 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7656 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7658 chunk_size = SaveLevel_VERS(NULL, level);
7659 putFileChunkBE(file, "VERS", chunk_size);
7660 SaveLevel_VERS(file, level);
7662 chunk_size = SaveLevel_DATE(NULL, level);
7663 putFileChunkBE(file, "DATE", chunk_size);
7664 SaveLevel_DATE(file, level);
7666 chunk_size = SaveLevel_NAME(NULL, level);
7667 putFileChunkBE(file, "NAME", chunk_size);
7668 SaveLevel_NAME(file, level);
7670 chunk_size = SaveLevel_AUTH(NULL, level);
7671 putFileChunkBE(file, "AUTH", chunk_size);
7672 SaveLevel_AUTH(file, level);
7674 chunk_size = SaveLevel_INFO(NULL, level);
7675 putFileChunkBE(file, "INFO", chunk_size);
7676 SaveLevel_INFO(file, level);
7678 chunk_size = SaveLevel_BODY(NULL, level);
7679 putFileChunkBE(file, "BODY", chunk_size);
7680 SaveLevel_BODY(file, level);
7682 chunk_size = SaveLevel_ELEM(NULL, level);
7683 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7685 putFileChunkBE(file, "ELEM", chunk_size);
7686 SaveLevel_ELEM(file, level);
7689 for (i = 0; i < NUM_ENVELOPES; i++)
7691 int element = EL_ENVELOPE_1 + i;
7693 chunk_size = SaveLevel_NOTE(NULL, level, element);
7694 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7696 putFileChunkBE(file, "NOTE", chunk_size);
7697 SaveLevel_NOTE(file, level, element);
7701 // if not using template level, check for non-default custom/group elements
7702 if (!level->use_custom_template || save_as_template)
7704 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7706 int element = EL_CUSTOM_START + i;
7708 chunk_size = SaveLevel_CUSX(NULL, level, element);
7709 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7711 putFileChunkBE(file, "CUSX", chunk_size);
7712 SaveLevel_CUSX(file, level, element);
7716 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7718 int element = EL_GROUP_START + i;
7720 chunk_size = SaveLevel_GRPX(NULL, level, element);
7721 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7723 putFileChunkBE(file, "GRPX", chunk_size);
7724 SaveLevel_GRPX(file, level, element);
7728 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7730 int element = GET_EMPTY_ELEMENT(i);
7732 chunk_size = SaveLevel_EMPX(NULL, level, element);
7733 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7735 putFileChunkBE(file, "EMPX", chunk_size);
7736 SaveLevel_EMPX(file, level, element);
7743 SetFilePermissions(filename, PERMS_PRIVATE);
7746 void SaveLevel(int nr)
7748 char *filename = getDefaultLevelFilename(nr);
7750 SaveLevelFromFilename(&level, filename, FALSE);
7753 void SaveLevelTemplate(void)
7755 char *filename = getLocalLevelTemplateFilename();
7757 SaveLevelFromFilename(&level, filename, TRUE);
7760 boolean SaveLevelChecked(int nr)
7762 char *filename = getDefaultLevelFilename(nr);
7763 boolean new_level = !fileExists(filename);
7764 boolean level_saved = FALSE;
7766 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7771 Request("Level saved!", REQ_CONFIRM);
7779 void DumpLevel(struct LevelInfo *level)
7781 if (level->no_level_file || level->no_valid_file)
7783 Warn("cannot dump -- no valid level file found");
7789 Print("Level xxx (file version %08d, game version %08d)\n",
7790 level->file_version, level->game_version);
7793 Print("Level author: '%s'\n", level->author);
7794 Print("Level title: '%s'\n", level->name);
7796 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7798 Print("Level time: %d seconds\n", level->time);
7799 Print("Gems needed: %d\n", level->gems_needed);
7801 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7802 Print("Time for wheel: %d seconds\n", level->time_wheel);
7803 Print("Time for light: %d seconds\n", level->time_light);
7804 Print("Time for timegate: %d seconds\n", level->time_timegate);
7806 Print("Amoeba speed: %d\n", level->amoeba_speed);
7809 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7810 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7811 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7812 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7813 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7814 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7820 for (i = 0; i < NUM_ENVELOPES; i++)
7822 char *text = level->envelope[i].text;
7823 int text_len = strlen(text);
7824 boolean has_text = FALSE;
7826 for (j = 0; j < text_len; j++)
7827 if (text[j] != ' ' && text[j] != '\n')
7833 Print("Envelope %d:\n'%s'\n", i + 1, text);
7841 void DumpLevels(void)
7843 static LevelDirTree *dumplevel_leveldir = NULL;
7845 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7846 global.dumplevel_leveldir);
7848 if (dumplevel_leveldir == NULL)
7849 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7851 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7852 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7853 Fail("no such level number: %d", global.dumplevel_level_nr);
7855 leveldir_current = dumplevel_leveldir;
7857 LoadLevel(global.dumplevel_level_nr);
7864 // ============================================================================
7865 // tape file functions
7866 // ============================================================================
7868 static void setTapeInfoToDefaults(void)
7872 // always start with reliable default values (empty tape)
7875 // default values (also for pre-1.2 tapes) with only the first player
7876 tape.player_participates[0] = TRUE;
7877 for (i = 1; i < MAX_PLAYERS; i++)
7878 tape.player_participates[i] = FALSE;
7880 // at least one (default: the first) player participates in every tape
7881 tape.num_participating_players = 1;
7883 tape.property_bits = TAPE_PROPERTY_NONE;
7885 tape.level_nr = level_nr;
7887 tape.changed = FALSE;
7889 tape.recording = FALSE;
7890 tape.playing = FALSE;
7891 tape.pausing = FALSE;
7893 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7894 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7896 tape.no_info_chunk = TRUE;
7897 tape.no_valid_file = FALSE;
7900 static int getTapePosSize(struct TapeInfo *tape)
7902 int tape_pos_size = 0;
7904 if (tape->use_key_actions)
7905 tape_pos_size += tape->num_participating_players;
7907 if (tape->use_mouse_actions)
7908 tape_pos_size += 3; // x and y position and mouse button mask
7910 tape_pos_size += 1; // tape action delay value
7912 return tape_pos_size;
7915 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7917 tape->use_key_actions = FALSE;
7918 tape->use_mouse_actions = FALSE;
7920 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7921 tape->use_key_actions = TRUE;
7923 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7924 tape->use_mouse_actions = TRUE;
7927 static int getTapeActionValue(struct TapeInfo *tape)
7929 return (tape->use_key_actions &&
7930 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7931 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7932 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7933 TAPE_ACTIONS_DEFAULT);
7936 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7938 tape->file_version = getFileVersion(file);
7939 tape->game_version = getFileVersion(file);
7944 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7948 tape->random_seed = getFile32BitBE(file);
7949 tape->date = getFile32BitBE(file);
7950 tape->length = getFile32BitBE(file);
7952 // read header fields that are new since version 1.2
7953 if (tape->file_version >= FILE_VERSION_1_2)
7955 byte store_participating_players = getFile8Bit(file);
7958 // since version 1.2, tapes store which players participate in the tape
7959 tape->num_participating_players = 0;
7960 for (i = 0; i < MAX_PLAYERS; i++)
7962 tape->player_participates[i] = FALSE;
7964 if (store_participating_players & (1 << i))
7966 tape->player_participates[i] = TRUE;
7967 tape->num_participating_players++;
7971 setTapeActionFlags(tape, getFile8Bit(file));
7973 tape->property_bits = getFile8Bit(file);
7975 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7977 engine_version = getFileVersion(file);
7978 if (engine_version > 0)
7979 tape->engine_version = engine_version;
7981 tape->engine_version = tape->game_version;
7987 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7989 tape->scr_fieldx = getFile8Bit(file);
7990 tape->scr_fieldy = getFile8Bit(file);
7995 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7997 char *level_identifier = NULL;
7998 int level_identifier_size;
8001 tape->no_info_chunk = FALSE;
8003 level_identifier_size = getFile16BitBE(file);
8005 level_identifier = checked_malloc(level_identifier_size);
8007 for (i = 0; i < level_identifier_size; i++)
8008 level_identifier[i] = getFile8Bit(file);
8010 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8011 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8013 checked_free(level_identifier);
8015 tape->level_nr = getFile16BitBE(file);
8017 chunk_size = 2 + level_identifier_size + 2;
8022 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8025 int tape_pos_size = getTapePosSize(tape);
8026 int chunk_size_expected = tape_pos_size * tape->length;
8028 if (chunk_size_expected != chunk_size)
8030 ReadUnusedBytesFromFile(file, chunk_size);
8031 return chunk_size_expected;
8034 for (i = 0; i < tape->length; i++)
8036 if (i >= MAX_TAPE_LEN)
8038 Warn("tape truncated -- size exceeds maximum tape size %d",
8041 // tape too large; read and ignore remaining tape data from this chunk
8042 for (;i < tape->length; i++)
8043 ReadUnusedBytesFromFile(file, tape_pos_size);
8048 if (tape->use_key_actions)
8050 for (j = 0; j < MAX_PLAYERS; j++)
8052 tape->pos[i].action[j] = MV_NONE;
8054 if (tape->player_participates[j])
8055 tape->pos[i].action[j] = getFile8Bit(file);
8059 if (tape->use_mouse_actions)
8061 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8062 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8063 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8066 tape->pos[i].delay = getFile8Bit(file);
8068 if (tape->file_version == FILE_VERSION_1_0)
8070 // eliminate possible diagonal moves in old tapes
8071 // this is only for backward compatibility
8073 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8074 byte action = tape->pos[i].action[0];
8075 int k, num_moves = 0;
8077 for (k = 0; k<4; k++)
8079 if (action & joy_dir[k])
8081 tape->pos[i + num_moves].action[0] = joy_dir[k];
8083 tape->pos[i + num_moves].delay = 0;
8092 tape->length += num_moves;
8095 else if (tape->file_version < FILE_VERSION_2_0)
8097 // convert pre-2.0 tapes to new tape format
8099 if (tape->pos[i].delay > 1)
8102 tape->pos[i + 1] = tape->pos[i];
8103 tape->pos[i + 1].delay = 1;
8106 for (j = 0; j < MAX_PLAYERS; j++)
8107 tape->pos[i].action[j] = MV_NONE;
8108 tape->pos[i].delay--;
8115 if (checkEndOfFile(file))
8119 if (i != tape->length)
8120 chunk_size = tape_pos_size * i;
8125 static void LoadTape_SokobanSolution(char *filename)
8128 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8130 if (!(file = openFile(filename, MODE_READ)))
8132 tape.no_valid_file = TRUE;
8137 while (!checkEndOfFile(file))
8139 unsigned char c = getByteFromFile(file);
8141 if (checkEndOfFile(file))
8148 tape.pos[tape.length].action[0] = MV_UP;
8149 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8155 tape.pos[tape.length].action[0] = MV_DOWN;
8156 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8162 tape.pos[tape.length].action[0] = MV_LEFT;
8163 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8169 tape.pos[tape.length].action[0] = MV_RIGHT;
8170 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8178 // ignore white-space characters
8182 tape.no_valid_file = TRUE;
8184 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8192 if (tape.no_valid_file)
8195 tape.length_frames = GetTapeLengthFrames();
8196 tape.length_seconds = GetTapeLengthSeconds();
8199 void LoadTapeFromFilename(char *filename)
8201 char cookie[MAX_LINE_LEN];
8202 char chunk_name[CHUNK_ID_LEN + 1];
8206 // always start with reliable default values
8207 setTapeInfoToDefaults();
8209 if (strSuffix(filename, ".sln"))
8211 LoadTape_SokobanSolution(filename);
8216 if (!(file = openFile(filename, MODE_READ)))
8218 tape.no_valid_file = TRUE;
8223 getFileChunkBE(file, chunk_name, NULL);
8224 if (strEqual(chunk_name, "RND1"))
8226 getFile32BitBE(file); // not used
8228 getFileChunkBE(file, chunk_name, NULL);
8229 if (!strEqual(chunk_name, "TAPE"))
8231 tape.no_valid_file = TRUE;
8233 Warn("unknown format of tape file '%s'", filename);
8240 else // check for pre-2.0 file format with cookie string
8242 strcpy(cookie, chunk_name);
8243 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8245 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8246 cookie[strlen(cookie) - 1] = '\0';
8248 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8250 tape.no_valid_file = TRUE;
8252 Warn("unknown format of tape file '%s'", filename);
8259 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8261 tape.no_valid_file = TRUE;
8263 Warn("unsupported version of tape file '%s'", filename);
8270 // pre-2.0 tape files have no game version, so use file version here
8271 tape.game_version = tape.file_version;
8274 if (tape.file_version < FILE_VERSION_1_2)
8276 // tape files from versions before 1.2.0 without chunk structure
8277 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8278 LoadTape_BODY(file, 2 * tape.length, &tape);
8286 int (*loader)(File *, int, struct TapeInfo *);
8290 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8291 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8292 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8293 { "INFO", -1, LoadTape_INFO },
8294 { "BODY", -1, LoadTape_BODY },
8298 while (getFileChunkBE(file, chunk_name, &chunk_size))
8302 while (chunk_info[i].name != NULL &&
8303 !strEqual(chunk_name, chunk_info[i].name))
8306 if (chunk_info[i].name == NULL)
8308 Warn("unknown chunk '%s' in tape file '%s'",
8309 chunk_name, filename);
8311 ReadUnusedBytesFromFile(file, chunk_size);
8313 else if (chunk_info[i].size != -1 &&
8314 chunk_info[i].size != chunk_size)
8316 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8317 chunk_size, chunk_name, filename);
8319 ReadUnusedBytesFromFile(file, chunk_size);
8323 // call function to load this tape chunk
8324 int chunk_size_expected =
8325 (chunk_info[i].loader)(file, chunk_size, &tape);
8327 // the size of some chunks cannot be checked before reading other
8328 // chunks first (like "HEAD" and "BODY") that contain some header
8329 // information, so check them here
8330 if (chunk_size_expected != chunk_size)
8332 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8333 chunk_size, chunk_name, filename);
8341 tape.length_frames = GetTapeLengthFrames();
8342 tape.length_seconds = GetTapeLengthSeconds();
8345 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8347 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8349 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8350 tape.engine_version);
8354 void LoadTape(int nr)
8356 char *filename = getTapeFilename(nr);
8358 LoadTapeFromFilename(filename);
8361 void LoadSolutionTape(int nr)
8363 char *filename = getSolutionTapeFilename(nr);
8365 LoadTapeFromFilename(filename);
8367 if (TAPE_IS_EMPTY(tape) &&
8368 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8369 level.native_sp_level->demo.is_available)
8370 CopyNativeTape_SP_to_RND(&level);
8373 void LoadScoreTape(char *score_tape_basename, int nr)
8375 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8377 LoadTapeFromFilename(filename);
8380 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8382 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8384 LoadTapeFromFilename(filename);
8387 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8389 // chunk required for team mode tapes with non-default screen size
8390 return (tape->num_participating_players > 1 &&
8391 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8392 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8395 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8397 putFileVersion(file, tape->file_version);
8398 putFileVersion(file, tape->game_version);
8401 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8404 byte store_participating_players = 0;
8406 // set bits for participating players for compact storage
8407 for (i = 0; i < MAX_PLAYERS; i++)
8408 if (tape->player_participates[i])
8409 store_participating_players |= (1 << i);
8411 putFile32BitBE(file, tape->random_seed);
8412 putFile32BitBE(file, tape->date);
8413 putFile32BitBE(file, tape->length);
8415 putFile8Bit(file, store_participating_players);
8417 putFile8Bit(file, getTapeActionValue(tape));
8419 putFile8Bit(file, tape->property_bits);
8421 // unused bytes not at the end here for 4-byte alignment of engine_version
8422 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8424 putFileVersion(file, tape->engine_version);
8427 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8429 putFile8Bit(file, tape->scr_fieldx);
8430 putFile8Bit(file, tape->scr_fieldy);
8433 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8435 int level_identifier_size = strlen(tape->level_identifier) + 1;
8438 putFile16BitBE(file, level_identifier_size);
8440 for (i = 0; i < level_identifier_size; i++)
8441 putFile8Bit(file, tape->level_identifier[i]);
8443 putFile16BitBE(file, tape->level_nr);
8446 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8450 for (i = 0; i < tape->length; i++)
8452 if (tape->use_key_actions)
8454 for (j = 0; j < MAX_PLAYERS; j++)
8455 if (tape->player_participates[j])
8456 putFile8Bit(file, tape->pos[i].action[j]);
8459 if (tape->use_mouse_actions)
8461 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8462 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8463 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8466 putFile8Bit(file, tape->pos[i].delay);
8470 void SaveTapeToFilename(char *filename)
8474 int info_chunk_size;
8475 int body_chunk_size;
8477 if (!(file = fopen(filename, MODE_WRITE)))
8479 Warn("cannot save level recording file '%s'", filename);
8484 tape_pos_size = getTapePosSize(&tape);
8486 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8487 body_chunk_size = tape_pos_size * tape.length;
8489 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8490 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8492 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8493 SaveTape_VERS(file, &tape);
8495 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8496 SaveTape_HEAD(file, &tape);
8498 if (checkSaveTape_SCRN(&tape))
8500 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8501 SaveTape_SCRN(file, &tape);
8504 putFileChunkBE(file, "INFO", info_chunk_size);
8505 SaveTape_INFO(file, &tape);
8507 putFileChunkBE(file, "BODY", body_chunk_size);
8508 SaveTape_BODY(file, &tape);
8512 SetFilePermissions(filename, PERMS_PRIVATE);
8515 static void SaveTapeExt(char *filename)
8519 tape.file_version = FILE_VERSION_ACTUAL;
8520 tape.game_version = GAME_VERSION_ACTUAL;
8522 tape.num_participating_players = 0;
8524 // count number of participating players
8525 for (i = 0; i < MAX_PLAYERS; i++)
8526 if (tape.player_participates[i])
8527 tape.num_participating_players++;
8529 SaveTapeToFilename(filename);
8531 tape.changed = FALSE;
8534 void SaveTape(int nr)
8536 char *filename = getTapeFilename(nr);
8538 InitTapeDirectory(leveldir_current->subdir);
8540 SaveTapeExt(filename);
8543 void SaveScoreTape(int nr)
8545 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8547 // used instead of "leveldir_current->subdir" (for network games)
8548 InitScoreTapeDirectory(levelset.identifier, nr);
8550 SaveTapeExt(filename);
8553 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8554 unsigned int req_state_added)
8556 char *filename = getTapeFilename(nr);
8557 boolean new_tape = !fileExists(filename);
8558 boolean tape_saved = FALSE;
8560 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8565 Request(msg_saved, REQ_CONFIRM | req_state_added);
8573 boolean SaveTapeChecked(int nr)
8575 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8578 boolean SaveTapeChecked_LevelSolved(int nr)
8580 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8581 "Level solved! Tape saved!", REQ_STAY_OPEN);
8584 void DumpTape(struct TapeInfo *tape)
8586 int tape_frame_counter;
8589 if (tape->no_valid_file)
8591 Warn("cannot dump -- no valid tape file found");
8598 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8599 tape->level_nr, tape->file_version, tape->game_version);
8600 Print(" (effective engine version %08d)\n",
8601 tape->engine_version);
8602 Print("Level series identifier: '%s'\n", tape->level_identifier);
8604 Print("Special tape properties: ");
8605 if (tape->property_bits == TAPE_PROPERTY_NONE)
8607 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8608 Print("[em_random_bug]");
8609 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8610 Print("[game_speed]");
8611 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8613 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8614 Print("[single_step]");
8615 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8616 Print("[snapshot]");
8617 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8618 Print("[replayed]");
8619 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8620 Print("[tas_keys]");
8621 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8622 Print("[small_graphics]");
8625 int year2 = tape->date / 10000;
8626 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8627 int month_index_raw = (tape->date / 100) % 100;
8628 int month_index = month_index_raw % 12; // prevent invalid index
8629 int month = month_index + 1;
8630 int day = tape->date % 100;
8632 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8636 tape_frame_counter = 0;
8638 for (i = 0; i < tape->length; i++)
8640 if (i >= MAX_TAPE_LEN)
8645 for (j = 0; j < MAX_PLAYERS; j++)
8647 if (tape->player_participates[j])
8649 int action = tape->pos[i].action[j];
8651 Print("%d:%02x ", j, action);
8652 Print("[%c%c%c%c|%c%c] - ",
8653 (action & JOY_LEFT ? '<' : ' '),
8654 (action & JOY_RIGHT ? '>' : ' '),
8655 (action & JOY_UP ? '^' : ' '),
8656 (action & JOY_DOWN ? 'v' : ' '),
8657 (action & JOY_BUTTON_1 ? '1' : ' '),
8658 (action & JOY_BUTTON_2 ? '2' : ' '));
8662 Print("(%03d) ", tape->pos[i].delay);
8663 Print("[%05d]\n", tape_frame_counter);
8665 tape_frame_counter += tape->pos[i].delay;
8671 void DumpTapes(void)
8673 static LevelDirTree *dumptape_leveldir = NULL;
8675 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8676 global.dumptape_leveldir);
8678 if (dumptape_leveldir == NULL)
8679 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8681 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8682 global.dumptape_level_nr > dumptape_leveldir->last_level)
8683 Fail("no such level number: %d", global.dumptape_level_nr);
8685 leveldir_current = dumptape_leveldir;
8687 if (options.mytapes)
8688 LoadTape(global.dumptape_level_nr);
8690 LoadSolutionTape(global.dumptape_level_nr);
8698 // ============================================================================
8699 // score file functions
8700 // ============================================================================
8702 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8706 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8708 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8709 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8710 scores->entry[i].score = 0;
8711 scores->entry[i].time = 0;
8713 scores->entry[i].id = -1;
8714 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8715 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8716 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8717 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8718 strcpy(scores->entry[i].country_code, "??");
8721 scores->num_entries = 0;
8722 scores->last_added = -1;
8723 scores->last_added_local = -1;
8725 scores->updated = FALSE;
8726 scores->uploaded = FALSE;
8727 scores->tape_downloaded = FALSE;
8728 scores->force_last_added = FALSE;
8730 // The following values are intentionally not reset here:
8734 // - continue_playing
8735 // - continue_on_return
8738 static void setScoreInfoToDefaults(void)
8740 setScoreInfoToDefaultsExt(&scores);
8743 static void setServerScoreInfoToDefaults(void)
8745 setScoreInfoToDefaultsExt(&server_scores);
8748 static void LoadScore_OLD(int nr)
8751 char *filename = getScoreFilename(nr);
8752 char cookie[MAX_LINE_LEN];
8753 char line[MAX_LINE_LEN];
8757 if (!(file = fopen(filename, MODE_READ)))
8760 // check file identifier
8761 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8763 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8764 cookie[strlen(cookie) - 1] = '\0';
8766 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8768 Warn("unknown format of score file '%s'", filename);
8775 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8777 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8778 Warn("fscanf() failed; %s", strerror(errno));
8780 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8783 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8784 line[strlen(line) - 1] = '\0';
8786 for (line_ptr = line; *line_ptr; line_ptr++)
8788 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8790 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8791 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8800 static void ConvertScore_OLD(void)
8802 // only convert score to time for levels that rate playing time over score
8803 if (!level.rate_time_over_score)
8806 // convert old score to playing time for score-less levels (like Supaplex)
8807 int time_final_max = 999;
8810 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8812 int score = scores.entry[i].score;
8814 if (score > 0 && score < time_final_max)
8815 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8819 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8821 scores->file_version = getFileVersion(file);
8822 scores->game_version = getFileVersion(file);
8827 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8829 char *level_identifier = NULL;
8830 int level_identifier_size;
8833 level_identifier_size = getFile16BitBE(file);
8835 level_identifier = checked_malloc(level_identifier_size);
8837 for (i = 0; i < level_identifier_size; i++)
8838 level_identifier[i] = getFile8Bit(file);
8840 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8841 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8843 checked_free(level_identifier);
8845 scores->level_nr = getFile16BitBE(file);
8846 scores->num_entries = getFile16BitBE(file);
8848 chunk_size = 2 + level_identifier_size + 2 + 2;
8853 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8857 for (i = 0; i < scores->num_entries; i++)
8859 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8860 scores->entry[i].name[j] = getFile8Bit(file);
8862 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8865 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8870 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8874 for (i = 0; i < scores->num_entries; i++)
8875 scores->entry[i].score = getFile16BitBE(file);
8877 chunk_size = scores->num_entries * 2;
8882 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8886 for (i = 0; i < scores->num_entries; i++)
8887 scores->entry[i].score = getFile32BitBE(file);
8889 chunk_size = scores->num_entries * 4;
8894 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8898 for (i = 0; i < scores->num_entries; i++)
8899 scores->entry[i].time = getFile32BitBE(file);
8901 chunk_size = scores->num_entries * 4;
8906 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8910 for (i = 0; i < scores->num_entries; i++)
8912 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8913 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8915 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8918 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8923 void LoadScore(int nr)
8925 char *filename = getScoreFilename(nr);
8926 char cookie[MAX_LINE_LEN];
8927 char chunk_name[CHUNK_ID_LEN + 1];
8929 boolean old_score_file_format = FALSE;
8932 // always start with reliable default values
8933 setScoreInfoToDefaults();
8935 if (!(file = openFile(filename, MODE_READ)))
8938 getFileChunkBE(file, chunk_name, NULL);
8939 if (strEqual(chunk_name, "RND1"))
8941 getFile32BitBE(file); // not used
8943 getFileChunkBE(file, chunk_name, NULL);
8944 if (!strEqual(chunk_name, "SCOR"))
8946 Warn("unknown format of score file '%s'", filename);
8953 else // check for old file format with cookie string
8955 strcpy(cookie, chunk_name);
8956 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8958 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8959 cookie[strlen(cookie) - 1] = '\0';
8961 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8963 Warn("unknown format of score file '%s'", filename);
8970 old_score_file_format = TRUE;
8973 if (old_score_file_format)
8975 // score files from versions before 4.2.4.0 without chunk structure
8978 // convert score to time, if possible (mainly for Supaplex levels)
8987 int (*loader)(File *, int, struct ScoreInfo *);
8991 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8992 { "INFO", -1, LoadScore_INFO },
8993 { "NAME", -1, LoadScore_NAME },
8994 { "SCOR", -1, LoadScore_SCOR },
8995 { "SC4R", -1, LoadScore_SC4R },
8996 { "TIME", -1, LoadScore_TIME },
8997 { "TAPE", -1, LoadScore_TAPE },
9002 while (getFileChunkBE(file, chunk_name, &chunk_size))
9006 while (chunk_info[i].name != NULL &&
9007 !strEqual(chunk_name, chunk_info[i].name))
9010 if (chunk_info[i].name == NULL)
9012 Warn("unknown chunk '%s' in score file '%s'",
9013 chunk_name, filename);
9015 ReadUnusedBytesFromFile(file, chunk_size);
9017 else if (chunk_info[i].size != -1 &&
9018 chunk_info[i].size != chunk_size)
9020 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9021 chunk_size, chunk_name, filename);
9023 ReadUnusedBytesFromFile(file, chunk_size);
9027 // call function to load this score chunk
9028 int chunk_size_expected =
9029 (chunk_info[i].loader)(file, chunk_size, &scores);
9031 // the size of some chunks cannot be checked before reading other
9032 // chunks first (like "HEAD" and "BODY") that contain some header
9033 // information, so check them here
9034 if (chunk_size_expected != chunk_size)
9036 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9037 chunk_size, chunk_name, filename);
9046 #if ENABLE_HISTORIC_CHUNKS
9047 void SaveScore_OLD(int nr)
9050 char *filename = getScoreFilename(nr);
9053 // used instead of "leveldir_current->subdir" (for network games)
9054 InitScoreDirectory(levelset.identifier);
9056 if (!(file = fopen(filename, MODE_WRITE)))
9058 Warn("cannot save score for level %d", nr);
9063 fprintf(file, "%s\n\n", SCORE_COOKIE);
9065 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9066 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9070 SetFilePermissions(filename, PERMS_PRIVATE);
9074 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9076 putFileVersion(file, scores->file_version);
9077 putFileVersion(file, scores->game_version);
9080 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9082 int level_identifier_size = strlen(scores->level_identifier) + 1;
9085 putFile16BitBE(file, level_identifier_size);
9087 for (i = 0; i < level_identifier_size; i++)
9088 putFile8Bit(file, scores->level_identifier[i]);
9090 putFile16BitBE(file, scores->level_nr);
9091 putFile16BitBE(file, scores->num_entries);
9094 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9098 for (i = 0; i < scores->num_entries; i++)
9100 int name_size = strlen(scores->entry[i].name);
9102 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9103 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9107 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9111 for (i = 0; i < scores->num_entries; i++)
9112 putFile16BitBE(file, scores->entry[i].score);
9115 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9119 for (i = 0; i < scores->num_entries; i++)
9120 putFile32BitBE(file, scores->entry[i].score);
9123 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9127 for (i = 0; i < scores->num_entries; i++)
9128 putFile32BitBE(file, scores->entry[i].time);
9131 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9135 for (i = 0; i < scores->num_entries; i++)
9137 int size = strlen(scores->entry[i].tape_basename);
9139 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9140 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9144 static void SaveScoreToFilename(char *filename)
9147 int info_chunk_size;
9148 int name_chunk_size;
9149 int scor_chunk_size;
9150 int sc4r_chunk_size;
9151 int time_chunk_size;
9152 int tape_chunk_size;
9153 boolean has_large_score_values;
9156 if (!(file = fopen(filename, MODE_WRITE)))
9158 Warn("cannot save score file '%s'", filename);
9163 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9164 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9165 scor_chunk_size = scores.num_entries * 2;
9166 sc4r_chunk_size = scores.num_entries * 4;
9167 time_chunk_size = scores.num_entries * 4;
9168 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9170 has_large_score_values = FALSE;
9171 for (i = 0; i < scores.num_entries; i++)
9172 if (scores.entry[i].score > 0xffff)
9173 has_large_score_values = TRUE;
9175 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9176 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9178 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9179 SaveScore_VERS(file, &scores);
9181 putFileChunkBE(file, "INFO", info_chunk_size);
9182 SaveScore_INFO(file, &scores);
9184 putFileChunkBE(file, "NAME", name_chunk_size);
9185 SaveScore_NAME(file, &scores);
9187 if (has_large_score_values)
9189 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9190 SaveScore_SC4R(file, &scores);
9194 putFileChunkBE(file, "SCOR", scor_chunk_size);
9195 SaveScore_SCOR(file, &scores);
9198 putFileChunkBE(file, "TIME", time_chunk_size);
9199 SaveScore_TIME(file, &scores);
9201 putFileChunkBE(file, "TAPE", tape_chunk_size);
9202 SaveScore_TAPE(file, &scores);
9206 SetFilePermissions(filename, PERMS_PRIVATE);
9209 void SaveScore(int nr)
9211 char *filename = getScoreFilename(nr);
9214 // used instead of "leveldir_current->subdir" (for network games)
9215 InitScoreDirectory(levelset.identifier);
9217 scores.file_version = FILE_VERSION_ACTUAL;
9218 scores.game_version = GAME_VERSION_ACTUAL;
9220 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9221 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9222 scores.level_nr = level_nr;
9224 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9225 if (scores.entry[i].score == 0 &&
9226 scores.entry[i].time == 0 &&
9227 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9230 scores.num_entries = i;
9232 if (scores.num_entries == 0)
9235 SaveScoreToFilename(filename);
9238 static void LoadServerScoreFromCache(int nr)
9240 struct ScoreEntry score_entry;
9249 { &score_entry.score, FALSE, 0 },
9250 { &score_entry.time, FALSE, 0 },
9251 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9252 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9253 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9254 { &score_entry.id, FALSE, 0 },
9255 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9256 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9257 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9258 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9262 char *filename = getScoreCacheFilename(nr);
9263 SetupFileHash *score_hash = loadSetupFileHash(filename);
9266 server_scores.num_entries = 0;
9268 if (score_hash == NULL)
9271 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9273 score_entry = server_scores.entry[i];
9275 for (j = 0; score_mapping[j].value != NULL; j++)
9279 sprintf(token, "%02d.%d", i, j);
9281 char *value = getHashEntry(score_hash, token);
9286 if (score_mapping[j].is_string)
9288 char *score_value = (char *)score_mapping[j].value;
9289 int value_size = score_mapping[j].string_size;
9291 strncpy(score_value, value, value_size);
9292 score_value[value_size] = '\0';
9296 int *score_value = (int *)score_mapping[j].value;
9298 *score_value = atoi(value);
9301 server_scores.num_entries = i + 1;
9304 server_scores.entry[i] = score_entry;
9307 freeSetupFileHash(score_hash);
9310 void LoadServerScore(int nr, boolean download_score)
9312 if (!setup.use_api_server)
9315 // always start with reliable default values
9316 setServerScoreInfoToDefaults();
9318 // 1st step: load server scores from cache file (which may not exist)
9319 // (this should prevent reading it while the thread is writing to it)
9320 LoadServerScoreFromCache(nr);
9322 if (download_score && runtime.use_api_server)
9324 // 2nd step: download server scores from score server to cache file
9325 // (as thread, as it might time out if the server is not reachable)
9326 ApiGetScoreAsThread(nr);
9330 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9332 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9334 // if score tape not uploaded, ask for uploading missing tapes later
9335 if (!setup.has_remaining_tapes)
9336 setup.ask_for_remaining_tapes = TRUE;
9338 setup.provide_uploading_tapes = TRUE;
9339 setup.has_remaining_tapes = TRUE;
9341 SaveSetup_ServerSetup();
9344 void SaveServerScore(int nr, boolean tape_saved)
9346 if (!runtime.use_api_server)
9348 PrepareScoreTapesForUpload(leveldir_current->subdir);
9353 ApiAddScoreAsThread(nr, tape_saved, NULL);
9356 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9357 char *score_tape_filename)
9359 if (!runtime.use_api_server)
9362 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9365 void LoadLocalAndServerScore(int nr, boolean download_score)
9367 int last_added_local = scores.last_added_local;
9368 boolean force_last_added = scores.force_last_added;
9370 // needed if only showing server scores
9371 setScoreInfoToDefaults();
9373 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9376 // restore last added local score entry (before merging server scores)
9377 scores.last_added = scores.last_added_local = last_added_local;
9379 if (setup.use_api_server &&
9380 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9382 // load server scores from cache file and trigger update from server
9383 LoadServerScore(nr, download_score);
9385 // merge local scores with scores from server
9389 if (force_last_added)
9390 scores.force_last_added = force_last_added;
9394 // ============================================================================
9395 // setup file functions
9396 // ============================================================================
9398 #define TOKEN_STR_PLAYER_PREFIX "player_"
9401 static struct TokenInfo global_setup_tokens[] =
9405 &setup.player_name, "player_name"
9409 &setup.multiple_users, "multiple_users"
9413 &setup.sound, "sound"
9417 &setup.sound_loops, "repeating_sound_loops"
9421 &setup.sound_music, "background_music"
9425 &setup.sound_simple, "simple_sound_effects"
9429 &setup.toons, "toons"
9433 &setup.scroll_delay, "scroll_delay"
9437 &setup.forced_scroll_delay, "forced_scroll_delay"
9441 &setup.scroll_delay_value, "scroll_delay_value"
9445 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9449 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9453 &setup.fade_screens, "fade_screens"
9457 &setup.autorecord, "automatic_tape_recording"
9461 &setup.auto_pause_on_start, "auto_pause_on_start"
9465 &setup.show_titlescreen, "show_titlescreen"
9469 &setup.quick_doors, "quick_doors"
9473 &setup.team_mode, "team_mode"
9477 &setup.handicap, "handicap"
9481 &setup.skip_levels, "skip_levels"
9485 &setup.increment_levels, "increment_levels"
9489 &setup.auto_play_next_level, "auto_play_next_level"
9493 &setup.count_score_after_game, "count_score_after_game"
9497 &setup.show_scores_after_game, "show_scores_after_game"
9501 &setup.time_limit, "time_limit"
9505 &setup.fullscreen, "fullscreen"
9509 &setup.window_scaling_percent, "window_scaling_percent"
9513 &setup.window_scaling_quality, "window_scaling_quality"
9517 &setup.screen_rendering_mode, "screen_rendering_mode"
9521 &setup.vsync_mode, "vsync_mode"
9525 &setup.ask_on_escape, "ask_on_escape"
9529 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9533 &setup.ask_on_game_over, "ask_on_game_over"
9537 &setup.ask_on_quit_game, "ask_on_quit_game"
9541 &setup.ask_on_quit_program, "ask_on_quit_program"
9545 &setup.quick_switch, "quick_player_switch"
9549 &setup.input_on_focus, "input_on_focus"
9553 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9557 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9561 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9565 &setup.game_speed_extended, "game_speed_extended"
9569 &setup.game_frame_delay, "game_frame_delay"
9573 &setup.sp_show_border_elements, "sp_show_border_elements"
9577 &setup.small_game_graphics, "small_game_graphics"
9581 &setup.show_load_save_buttons, "show_load_save_buttons"
9585 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9589 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9593 &setup.graphics_set, "graphics_set"
9597 &setup.sounds_set, "sounds_set"
9601 &setup.music_set, "music_set"
9605 &setup.override_level_graphics, "override_level_graphics"
9609 &setup.override_level_sounds, "override_level_sounds"
9613 &setup.override_level_music, "override_level_music"
9617 &setup.volume_simple, "volume_simple"
9621 &setup.volume_loops, "volume_loops"
9625 &setup.volume_music, "volume_music"
9629 &setup.network_mode, "network_mode"
9633 &setup.network_player_nr, "network_player"
9637 &setup.network_server_hostname, "network_server_hostname"
9641 &setup.touch.control_type, "touch.control_type"
9645 &setup.touch.move_distance, "touch.move_distance"
9649 &setup.touch.drop_distance, "touch.drop_distance"
9653 &setup.touch.transparency, "touch.transparency"
9657 &setup.touch.draw_outlined, "touch.draw_outlined"
9661 &setup.touch.draw_pressed, "touch.draw_pressed"
9665 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9669 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9673 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9677 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9681 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9685 static struct TokenInfo auto_setup_tokens[] =
9689 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9693 static struct TokenInfo server_setup_tokens[] =
9697 &setup.player_uuid, "player_uuid"
9701 &setup.player_version, "player_version"
9705 &setup.use_api_server, TEST_PREFIX "use_api_server"
9709 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9713 &setup.api_server_password, TEST_PREFIX "api_server_password"
9717 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9721 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9725 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9729 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9733 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9737 static struct TokenInfo editor_setup_tokens[] =
9741 &setup.editor.el_classic, "editor.el_classic"
9745 &setup.editor.el_custom, "editor.el_custom"
9749 &setup.editor.el_user_defined, "editor.el_user_defined"
9753 &setup.editor.el_dynamic, "editor.el_dynamic"
9757 &setup.editor.el_headlines, "editor.el_headlines"
9761 &setup.editor.show_element_token, "editor.show_element_token"
9765 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9769 static struct TokenInfo editor_cascade_setup_tokens[] =
9773 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9777 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9781 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9785 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9789 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9793 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9797 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9801 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9805 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9809 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9813 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9817 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9821 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9825 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9829 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9833 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9837 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9841 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9845 static struct TokenInfo shortcut_setup_tokens[] =
9849 &setup.shortcut.save_game, "shortcut.save_game"
9853 &setup.shortcut.load_game, "shortcut.load_game"
9857 &setup.shortcut.restart_game, "shortcut.restart_game"
9861 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9865 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9869 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9873 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9877 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9881 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9885 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9889 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9893 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9897 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9901 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9905 &setup.shortcut.tape_record, "shortcut.tape_record"
9909 &setup.shortcut.tape_play, "shortcut.tape_play"
9913 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9917 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9921 &setup.shortcut.sound_music, "shortcut.sound_music"
9925 &setup.shortcut.snap_left, "shortcut.snap_left"
9929 &setup.shortcut.snap_right, "shortcut.snap_right"
9933 &setup.shortcut.snap_up, "shortcut.snap_up"
9937 &setup.shortcut.snap_down, "shortcut.snap_down"
9941 static struct SetupInputInfo setup_input;
9942 static struct TokenInfo player_setup_tokens[] =
9946 &setup_input.use_joystick, ".use_joystick"
9950 &setup_input.joy.device_name, ".joy.device_name"
9954 &setup_input.joy.xleft, ".joy.xleft"
9958 &setup_input.joy.xmiddle, ".joy.xmiddle"
9962 &setup_input.joy.xright, ".joy.xright"
9966 &setup_input.joy.yupper, ".joy.yupper"
9970 &setup_input.joy.ymiddle, ".joy.ymiddle"
9974 &setup_input.joy.ylower, ".joy.ylower"
9978 &setup_input.joy.snap, ".joy.snap_field"
9982 &setup_input.joy.drop, ".joy.place_bomb"
9986 &setup_input.key.left, ".key.move_left"
9990 &setup_input.key.right, ".key.move_right"
9994 &setup_input.key.up, ".key.move_up"
9998 &setup_input.key.down, ".key.move_down"
10002 &setup_input.key.snap, ".key.snap_field"
10006 &setup_input.key.drop, ".key.place_bomb"
10010 static struct TokenInfo system_setup_tokens[] =
10014 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10018 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10022 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10026 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10030 static struct TokenInfo internal_setup_tokens[] =
10034 &setup.internal.program_title, "program_title"
10038 &setup.internal.program_version, "program_version"
10042 &setup.internal.program_author, "program_author"
10046 &setup.internal.program_email, "program_email"
10050 &setup.internal.program_website, "program_website"
10054 &setup.internal.program_copyright, "program_copyright"
10058 &setup.internal.program_company, "program_company"
10062 &setup.internal.program_icon_file, "program_icon_file"
10066 &setup.internal.default_graphics_set, "default_graphics_set"
10070 &setup.internal.default_sounds_set, "default_sounds_set"
10074 &setup.internal.default_music_set, "default_music_set"
10078 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10082 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10086 &setup.internal.fallback_music_file, "fallback_music_file"
10090 &setup.internal.default_level_series, "default_level_series"
10094 &setup.internal.default_window_width, "default_window_width"
10098 &setup.internal.default_window_height, "default_window_height"
10102 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10106 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10110 &setup.internal.create_user_levelset, "create_user_levelset"
10114 &setup.internal.menu_game, "menu_game"
10118 &setup.internal.menu_editor, "menu_editor"
10122 &setup.internal.menu_graphics, "menu_graphics"
10126 &setup.internal.menu_sound, "menu_sound"
10130 &setup.internal.menu_artwork, "menu_artwork"
10134 &setup.internal.menu_input, "menu_input"
10138 &setup.internal.menu_touch, "menu_touch"
10142 &setup.internal.menu_shortcuts, "menu_shortcuts"
10146 &setup.internal.menu_exit, "menu_exit"
10150 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10154 &setup.internal.info_title, "info_title"
10158 &setup.internal.info_elements, "info_elements"
10162 &setup.internal.info_music, "info_music"
10166 &setup.internal.info_credits, "info_credits"
10170 &setup.internal.info_program, "info_program"
10174 &setup.internal.info_version, "info_version"
10178 &setup.internal.info_levelset, "info_levelset"
10182 &setup.internal.info_exit, "info_exit"
10186 static struct TokenInfo debug_setup_tokens[] =
10190 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10194 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10198 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10202 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10206 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10210 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10214 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10218 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10222 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10226 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10230 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10234 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10238 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10242 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10246 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10250 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10254 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10258 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10262 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10266 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10270 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10273 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10277 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10281 &setup.debug.xsn_mode, "debug.xsn_mode"
10285 &setup.debug.xsn_percent, "debug.xsn_percent"
10289 static struct TokenInfo options_setup_tokens[] =
10293 &setup.options.verbose, "options.verbose"
10297 static void setSetupInfoToDefaults(struct SetupInfo *si)
10301 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10303 si->multiple_users = TRUE;
10306 si->sound_loops = TRUE;
10307 si->sound_music = TRUE;
10308 si->sound_simple = TRUE;
10310 si->scroll_delay = TRUE;
10311 si->forced_scroll_delay = FALSE;
10312 si->scroll_delay_value = STD_SCROLL_DELAY;
10313 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10314 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10315 si->fade_screens = TRUE;
10316 si->autorecord = TRUE;
10317 si->auto_pause_on_start = FALSE;
10318 si->show_titlescreen = TRUE;
10319 si->quick_doors = FALSE;
10320 si->team_mode = FALSE;
10321 si->handicap = TRUE;
10322 si->skip_levels = TRUE;
10323 si->increment_levels = TRUE;
10324 si->auto_play_next_level = TRUE;
10325 si->count_score_after_game = TRUE;
10326 si->show_scores_after_game = TRUE;
10327 si->time_limit = TRUE;
10328 si->fullscreen = FALSE;
10329 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10330 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10331 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10332 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10333 si->ask_on_escape = TRUE;
10334 si->ask_on_escape_editor = TRUE;
10335 si->ask_on_game_over = TRUE;
10336 si->ask_on_quit_game = TRUE;
10337 si->ask_on_quit_program = TRUE;
10338 si->quick_switch = FALSE;
10339 si->input_on_focus = FALSE;
10340 si->prefer_aga_graphics = TRUE;
10341 si->prefer_lowpass_sounds = FALSE;
10342 si->prefer_extra_panel_items = TRUE;
10343 si->game_speed_extended = FALSE;
10344 si->game_frame_delay = GAME_FRAME_DELAY;
10345 si->sp_show_border_elements = FALSE;
10346 si->small_game_graphics = FALSE;
10347 si->show_load_save_buttons = FALSE;
10348 si->show_undo_redo_buttons = FALSE;
10349 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10351 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10352 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10353 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10355 si->override_level_graphics = FALSE;
10356 si->override_level_sounds = FALSE;
10357 si->override_level_music = FALSE;
10359 si->volume_simple = 100; // percent
10360 si->volume_loops = 100; // percent
10361 si->volume_music = 100; // percent
10363 si->network_mode = FALSE;
10364 si->network_player_nr = 0; // first player
10365 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10367 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10368 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10369 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10370 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10371 si->touch.draw_outlined = TRUE;
10372 si->touch.draw_pressed = TRUE;
10374 for (i = 0; i < 2; i++)
10376 char *default_grid_button[6][2] =
10382 { "111222", " vv " },
10383 { "111222", " vv " }
10385 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10386 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10387 int min_xsize = MIN(6, grid_xsize);
10388 int min_ysize = MIN(6, grid_ysize);
10389 int startx = grid_xsize - min_xsize;
10390 int starty = grid_ysize - min_ysize;
10393 // virtual buttons grid can only be set to defaults if video is initialized
10394 // (this will be repeated if virtual buttons are not loaded from setup file)
10395 if (video.initialized)
10397 si->touch.grid_xsize[i] = grid_xsize;
10398 si->touch.grid_ysize[i] = grid_ysize;
10402 si->touch.grid_xsize[i] = -1;
10403 si->touch.grid_ysize[i] = -1;
10406 for (x = 0; x < MAX_GRID_XSIZE; x++)
10407 for (y = 0; y < MAX_GRID_YSIZE; y++)
10408 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10410 for (x = 0; x < min_xsize; x++)
10411 for (y = 0; y < min_ysize; y++)
10412 si->touch.grid_button[i][x][starty + y] =
10413 default_grid_button[y][0][x];
10415 for (x = 0; x < min_xsize; x++)
10416 for (y = 0; y < min_ysize; y++)
10417 si->touch.grid_button[i][startx + x][starty + y] =
10418 default_grid_button[y][1][x];
10421 si->touch.grid_initialized = video.initialized;
10423 si->touch.overlay_buttons = FALSE;
10425 si->editor.el_boulderdash = TRUE;
10426 si->editor.el_emerald_mine = TRUE;
10427 si->editor.el_emerald_mine_club = TRUE;
10428 si->editor.el_more = TRUE;
10429 si->editor.el_sokoban = TRUE;
10430 si->editor.el_supaplex = TRUE;
10431 si->editor.el_diamond_caves = TRUE;
10432 si->editor.el_dx_boulderdash = TRUE;
10434 si->editor.el_mirror_magic = TRUE;
10435 si->editor.el_deflektor = TRUE;
10437 si->editor.el_chars = TRUE;
10438 si->editor.el_steel_chars = TRUE;
10440 si->editor.el_classic = TRUE;
10441 si->editor.el_custom = TRUE;
10443 si->editor.el_user_defined = FALSE;
10444 si->editor.el_dynamic = TRUE;
10446 si->editor.el_headlines = TRUE;
10448 si->editor.show_element_token = FALSE;
10450 si->editor.show_read_only_warning = TRUE;
10452 si->editor.use_template_for_new_levels = TRUE;
10454 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10455 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10456 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10457 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10458 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10460 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10461 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10462 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10463 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10464 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10466 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10467 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10468 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10469 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10470 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10471 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10473 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10474 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10475 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10477 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10478 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10479 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10480 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10482 for (i = 0; i < MAX_PLAYERS; i++)
10484 si->input[i].use_joystick = FALSE;
10485 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10486 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10487 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10488 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10489 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10490 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10491 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10492 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10493 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10494 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10495 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10496 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10497 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10498 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10499 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10502 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10503 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10504 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10505 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10507 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10508 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10509 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10510 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10511 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10512 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10513 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10515 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10517 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10518 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10519 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10521 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10522 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10523 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10525 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10526 si->internal.choose_from_top_leveldir = FALSE;
10527 si->internal.show_scaling_in_title = TRUE;
10528 si->internal.create_user_levelset = TRUE;
10530 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10531 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10533 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10534 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10535 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10536 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10537 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10538 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10539 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10540 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10541 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10542 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10544 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10545 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10546 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10547 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10548 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10549 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10550 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10551 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10552 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10553 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10555 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10556 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10558 si->debug.show_frames_per_second = FALSE;
10560 si->debug.xsn_mode = AUTO;
10561 si->debug.xsn_percent = 0;
10563 si->options.verbose = FALSE;
10565 #if defined(PLATFORM_ANDROID)
10566 si->fullscreen = TRUE;
10567 si->touch.overlay_buttons = TRUE;
10570 setHideSetupEntry(&setup.debug.xsn_mode);
10573 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10575 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10578 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10580 si->player_uuid = NULL; // (will be set later)
10581 si->player_version = 1; // (will be set later)
10583 si->use_api_server = TRUE;
10584 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10585 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10586 si->ask_for_uploading_tapes = TRUE;
10587 si->ask_for_remaining_tapes = FALSE;
10588 si->provide_uploading_tapes = TRUE;
10589 si->ask_for_using_api_server = TRUE;
10590 si->has_remaining_tapes = FALSE;
10593 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10595 si->editor_cascade.el_bd = TRUE;
10596 si->editor_cascade.el_em = TRUE;
10597 si->editor_cascade.el_emc = TRUE;
10598 si->editor_cascade.el_rnd = TRUE;
10599 si->editor_cascade.el_sb = TRUE;
10600 si->editor_cascade.el_sp = TRUE;
10601 si->editor_cascade.el_dc = TRUE;
10602 si->editor_cascade.el_dx = TRUE;
10604 si->editor_cascade.el_mm = TRUE;
10605 si->editor_cascade.el_df = TRUE;
10607 si->editor_cascade.el_chars = FALSE;
10608 si->editor_cascade.el_steel_chars = FALSE;
10609 si->editor_cascade.el_ce = FALSE;
10610 si->editor_cascade.el_ge = FALSE;
10611 si->editor_cascade.el_es = FALSE;
10612 si->editor_cascade.el_ref = FALSE;
10613 si->editor_cascade.el_user = FALSE;
10614 si->editor_cascade.el_dynamic = FALSE;
10617 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10619 static char *getHideSetupToken(void *setup_value)
10621 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10623 if (setup_value != NULL)
10624 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10626 return hide_setup_token;
10629 void setHideSetupEntry(void *setup_value)
10631 char *hide_setup_token = getHideSetupToken(setup_value);
10633 if (hide_setup_hash == NULL)
10634 hide_setup_hash = newSetupFileHash();
10636 if (setup_value != NULL)
10637 setHashEntry(hide_setup_hash, hide_setup_token, "");
10640 void removeHideSetupEntry(void *setup_value)
10642 char *hide_setup_token = getHideSetupToken(setup_value);
10644 if (setup_value != NULL)
10645 removeHashEntry(hide_setup_hash, hide_setup_token);
10648 boolean hideSetupEntry(void *setup_value)
10650 char *hide_setup_token = getHideSetupToken(setup_value);
10652 return (setup_value != NULL &&
10653 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10656 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10657 struct TokenInfo *token_info,
10658 int token_nr, char *token_text)
10660 char *token_hide_text = getStringCat2(token_text, ".hide");
10661 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10663 // set the value of this setup option in the setup option structure
10664 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10666 // check if this setup option should be hidden in the setup menu
10667 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10668 setHideSetupEntry(token_info[token_nr].value);
10670 free(token_hide_text);
10673 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10674 struct TokenInfo *token_info,
10677 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10678 token_info[token_nr].text);
10681 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10685 if (!setup_file_hash)
10688 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10689 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10691 setup.touch.grid_initialized = TRUE;
10692 for (i = 0; i < 2; i++)
10694 int grid_xsize = setup.touch.grid_xsize[i];
10695 int grid_ysize = setup.touch.grid_ysize[i];
10698 // if virtual buttons are not loaded from setup file, repeat initializing
10699 // virtual buttons grid with default values later when video is initialized
10700 if (grid_xsize == -1 ||
10703 setup.touch.grid_initialized = FALSE;
10708 for (y = 0; y < grid_ysize; y++)
10710 char token_string[MAX_LINE_LEN];
10712 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10714 char *value_string = getHashEntry(setup_file_hash, token_string);
10716 if (value_string == NULL)
10719 for (x = 0; x < grid_xsize; x++)
10721 char c = value_string[x];
10723 setup.touch.grid_button[i][x][y] =
10724 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10729 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10730 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10732 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10733 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10735 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10739 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10741 setup_input = setup.input[pnr];
10742 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10744 char full_token[100];
10746 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10747 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10750 setup.input[pnr] = setup_input;
10753 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10754 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10756 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10757 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10759 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10760 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10762 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10763 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10765 setHideRelatedSetupEntries();
10768 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10772 if (!setup_file_hash)
10775 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10776 setSetupInfo(auto_setup_tokens, i,
10777 getHashEntry(setup_file_hash,
10778 auto_setup_tokens[i].text));
10781 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10785 if (!setup_file_hash)
10788 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10789 setSetupInfo(server_setup_tokens, i,
10790 getHashEntry(setup_file_hash,
10791 server_setup_tokens[i].text));
10794 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10798 if (!setup_file_hash)
10801 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10802 setSetupInfo(editor_cascade_setup_tokens, i,
10803 getHashEntry(setup_file_hash,
10804 editor_cascade_setup_tokens[i].text));
10807 void LoadUserNames(void)
10809 int last_user_nr = user.nr;
10812 if (global.user_names != NULL)
10814 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10815 checked_free(global.user_names[i]);
10817 checked_free(global.user_names);
10820 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10822 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10826 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10828 if (setup_file_hash)
10830 char *player_name = getHashEntry(setup_file_hash, "player_name");
10832 global.user_names[i] = getFixedUserName(player_name);
10834 freeSetupFileHash(setup_file_hash);
10837 if (global.user_names[i] == NULL)
10838 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10841 user.nr = last_user_nr;
10844 void LoadSetupFromFilename(char *filename)
10846 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10848 if (setup_file_hash)
10850 decodeSetupFileHash_Default(setup_file_hash);
10852 freeSetupFileHash(setup_file_hash);
10856 Debug("setup", "using default setup values");
10860 static void LoadSetup_SpecialPostProcessing(void)
10862 char *player_name_new;
10864 // needed to work around problems with fixed length strings
10865 player_name_new = getFixedUserName(setup.player_name);
10866 free(setup.player_name);
10867 setup.player_name = player_name_new;
10869 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10870 if (setup.scroll_delay == FALSE)
10872 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10873 setup.scroll_delay = TRUE; // now always "on"
10876 // make sure that scroll delay value stays inside valid range
10877 setup.scroll_delay_value =
10878 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10881 void LoadSetup_Default(void)
10885 // always start with reliable default values
10886 setSetupInfoToDefaults(&setup);
10888 // try to load setup values from default setup file
10889 filename = getDefaultSetupFilename();
10891 if (fileExists(filename))
10892 LoadSetupFromFilename(filename);
10894 // try to load setup values from platform setup file
10895 filename = getPlatformSetupFilename();
10897 if (fileExists(filename))
10898 LoadSetupFromFilename(filename);
10900 // try to load setup values from user setup file
10901 filename = getSetupFilename();
10903 LoadSetupFromFilename(filename);
10905 LoadSetup_SpecialPostProcessing();
10908 void LoadSetup_AutoSetup(void)
10910 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10911 SetupFileHash *setup_file_hash = NULL;
10913 // always start with reliable default values
10914 setSetupInfoToDefaults_AutoSetup(&setup);
10916 setup_file_hash = loadSetupFileHash(filename);
10918 if (setup_file_hash)
10920 decodeSetupFileHash_AutoSetup(setup_file_hash);
10922 freeSetupFileHash(setup_file_hash);
10928 void LoadSetup_ServerSetup(void)
10930 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
10931 SetupFileHash *setup_file_hash = NULL;
10933 // always start with reliable default values
10934 setSetupInfoToDefaults_ServerSetup(&setup);
10936 setup_file_hash = loadSetupFileHash(filename);
10938 if (setup_file_hash)
10940 decodeSetupFileHash_ServerSetup(setup_file_hash);
10942 freeSetupFileHash(setup_file_hash);
10947 if (setup.player_uuid == NULL)
10949 // player UUID does not yet exist in setup file
10950 setup.player_uuid = getStringCopy(getUUID());
10951 setup.player_version = 2;
10953 SaveSetup_ServerSetup();
10957 void LoadSetup_EditorCascade(void)
10959 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10960 SetupFileHash *setup_file_hash = NULL;
10962 // always start with reliable default values
10963 setSetupInfoToDefaults_EditorCascade(&setup);
10965 setup_file_hash = loadSetupFileHash(filename);
10967 if (setup_file_hash)
10969 decodeSetupFileHash_EditorCascade(setup_file_hash);
10971 freeSetupFileHash(setup_file_hash);
10977 void LoadSetup(void)
10979 LoadSetup_Default();
10980 LoadSetup_AutoSetup();
10981 LoadSetup_ServerSetup();
10982 LoadSetup_EditorCascade();
10985 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
10986 char *mapping_line)
10988 char mapping_guid[MAX_LINE_LEN];
10989 char *mapping_start, *mapping_end;
10991 // get GUID from game controller mapping line: copy complete line
10992 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
10993 mapping_guid[MAX_LINE_LEN - 1] = '\0';
10995 // get GUID from game controller mapping line: cut after GUID part
10996 mapping_start = strchr(mapping_guid, ',');
10997 if (mapping_start != NULL)
10998 *mapping_start = '\0';
11000 // cut newline from game controller mapping line
11001 mapping_end = strchr(mapping_line, '\n');
11002 if (mapping_end != NULL)
11003 *mapping_end = '\0';
11005 // add mapping entry to game controller mappings hash
11006 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11009 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11014 if (!(file = fopen(filename, MODE_READ)))
11016 Warn("cannot read game controller mappings file '%s'", filename);
11021 while (!feof(file))
11023 char line[MAX_LINE_LEN];
11025 if (!fgets(line, MAX_LINE_LEN, file))
11028 addGameControllerMappingToHash(mappings_hash, line);
11034 void SaveSetup_Default(void)
11036 char *filename = getSetupFilename();
11040 InitUserDataDirectory();
11042 if (!(file = fopen(filename, MODE_WRITE)))
11044 Warn("cannot write setup file '%s'", filename);
11049 fprintFileHeader(file, SETUP_FILENAME);
11051 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11053 // just to make things nicer :)
11054 if (global_setup_tokens[i].value == &setup.multiple_users ||
11055 global_setup_tokens[i].value == &setup.sound ||
11056 global_setup_tokens[i].value == &setup.graphics_set ||
11057 global_setup_tokens[i].value == &setup.volume_simple ||
11058 global_setup_tokens[i].value == &setup.network_mode ||
11059 global_setup_tokens[i].value == &setup.touch.control_type ||
11060 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11061 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11062 fprintf(file, "\n");
11064 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11067 for (i = 0; i < 2; i++)
11069 int grid_xsize = setup.touch.grid_xsize[i];
11070 int grid_ysize = setup.touch.grid_ysize[i];
11073 fprintf(file, "\n");
11075 for (y = 0; y < grid_ysize; y++)
11077 char token_string[MAX_LINE_LEN];
11078 char value_string[MAX_LINE_LEN];
11080 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11082 for (x = 0; x < grid_xsize; x++)
11084 char c = setup.touch.grid_button[i][x][y];
11086 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11089 value_string[grid_xsize] = '\0';
11091 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11095 fprintf(file, "\n");
11096 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11097 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11099 fprintf(file, "\n");
11100 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11101 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11103 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11107 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11108 fprintf(file, "\n");
11110 setup_input = setup.input[pnr];
11111 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11112 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11115 fprintf(file, "\n");
11116 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11117 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11119 // (internal setup values not saved to user setup file)
11121 fprintf(file, "\n");
11122 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11123 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11124 setup.debug.xsn_mode != AUTO)
11125 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11127 fprintf(file, "\n");
11128 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11129 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11133 SetFilePermissions(filename, PERMS_PRIVATE);
11136 void SaveSetup_AutoSetup(void)
11138 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11142 InitUserDataDirectory();
11144 if (!(file = fopen(filename, MODE_WRITE)))
11146 Warn("cannot write auto setup file '%s'", filename);
11153 fprintFileHeader(file, AUTOSETUP_FILENAME);
11155 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11156 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11160 SetFilePermissions(filename, PERMS_PRIVATE);
11165 void SaveSetup_ServerSetup(void)
11167 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11171 InitUserDataDirectory();
11173 if (!(file = fopen(filename, MODE_WRITE)))
11175 Warn("cannot write server setup file '%s'", filename);
11182 fprintFileHeader(file, SERVERSETUP_FILENAME);
11184 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11186 // just to make things nicer :)
11187 if (server_setup_tokens[i].value == &setup.use_api_server)
11188 fprintf(file, "\n");
11190 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11195 SetFilePermissions(filename, PERMS_PRIVATE);
11200 void SaveSetup_EditorCascade(void)
11202 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11206 InitUserDataDirectory();
11208 if (!(file = fopen(filename, MODE_WRITE)))
11210 Warn("cannot write editor cascade state file '%s'", filename);
11217 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11219 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11220 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11224 SetFilePermissions(filename, PERMS_PRIVATE);
11229 void SaveSetup(void)
11231 SaveSetup_Default();
11232 SaveSetup_AutoSetup();
11233 SaveSetup_ServerSetup();
11234 SaveSetup_EditorCascade();
11237 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11242 if (!(file = fopen(filename, MODE_WRITE)))
11244 Warn("cannot write game controller mappings file '%s'", filename);
11249 BEGIN_HASH_ITERATION(mappings_hash, itr)
11251 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11253 END_HASH_ITERATION(mappings_hash, itr)
11258 void SaveSetup_AddGameControllerMapping(char *mapping)
11260 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11261 SetupFileHash *mappings_hash = newSetupFileHash();
11263 InitUserDataDirectory();
11265 // load existing personal game controller mappings
11266 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11268 // add new mapping to personal game controller mappings
11269 addGameControllerMappingToHash(mappings_hash, mapping);
11271 // save updated personal game controller mappings
11272 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11274 freeSetupFileHash(mappings_hash);
11278 void LoadCustomElementDescriptions(void)
11280 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11281 SetupFileHash *setup_file_hash;
11284 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11286 if (element_info[i].custom_description != NULL)
11288 free(element_info[i].custom_description);
11289 element_info[i].custom_description = NULL;
11293 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11296 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11298 char *token = getStringCat2(element_info[i].token_name, ".name");
11299 char *value = getHashEntry(setup_file_hash, token);
11302 element_info[i].custom_description = getStringCopy(value);
11307 freeSetupFileHash(setup_file_hash);
11310 static int getElementFromToken(char *token)
11312 char *value = getHashEntry(element_token_hash, token);
11315 return atoi(value);
11317 Warn("unknown element token '%s'", token);
11319 return EL_UNDEFINED;
11322 void FreeGlobalAnimEventInfo(void)
11324 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11326 if (gaei->event_list == NULL)
11331 for (i = 0; i < gaei->num_event_lists; i++)
11333 checked_free(gaei->event_list[i]->event_value);
11334 checked_free(gaei->event_list[i]);
11337 checked_free(gaei->event_list);
11339 gaei->event_list = NULL;
11340 gaei->num_event_lists = 0;
11343 static int AddGlobalAnimEventList(void)
11345 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11346 int list_pos = gaei->num_event_lists++;
11348 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11349 sizeof(struct GlobalAnimEventListInfo *));
11351 gaei->event_list[list_pos] =
11352 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11354 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11356 gaeli->event_value = NULL;
11357 gaeli->num_event_values = 0;
11362 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11364 // do not add empty global animation events
11365 if (event_value == ANIM_EVENT_NONE)
11368 // if list position is undefined, create new list
11369 if (list_pos == ANIM_EVENT_UNDEFINED)
11370 list_pos = AddGlobalAnimEventList();
11372 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11373 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11374 int value_pos = gaeli->num_event_values++;
11376 gaeli->event_value = checked_realloc(gaeli->event_value,
11377 gaeli->num_event_values * sizeof(int *));
11379 gaeli->event_value[value_pos] = event_value;
11384 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11386 if (list_pos == ANIM_EVENT_UNDEFINED)
11387 return ANIM_EVENT_NONE;
11389 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11390 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11392 return gaeli->event_value[value_pos];
11395 int GetGlobalAnimEventValueCount(int list_pos)
11397 if (list_pos == ANIM_EVENT_UNDEFINED)
11400 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11401 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11403 return gaeli->num_event_values;
11406 // This function checks if a string <s> of the format "string1, string2, ..."
11407 // exactly contains a string <s_contained>.
11409 static boolean string_has_parameter(char *s, char *s_contained)
11413 if (s == NULL || s_contained == NULL)
11416 if (strlen(s_contained) > strlen(s))
11419 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11421 char next_char = s[strlen(s_contained)];
11423 // check if next character is delimiter or whitespace
11424 return (next_char == ',' || next_char == '\0' ||
11425 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11428 // check if string contains another parameter string after a comma
11429 substring = strchr(s, ',');
11430 if (substring == NULL) // string does not contain a comma
11433 // advance string pointer to next character after the comma
11436 // skip potential whitespaces after the comma
11437 while (*substring == ' ' || *substring == '\t')
11440 return string_has_parameter(substring, s_contained);
11443 static int get_anim_parameter_value(char *s)
11445 int event_value[] =
11453 char *pattern_1[] =
11461 char *pattern_2 = ".part_";
11462 char *matching_char = NULL;
11464 int pattern_1_len = 0;
11465 int result = ANIM_EVENT_NONE;
11468 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11470 matching_char = strstr(s_ptr, pattern_1[i]);
11471 pattern_1_len = strlen(pattern_1[i]);
11472 result = event_value[i];
11474 if (matching_char != NULL)
11478 if (matching_char == NULL)
11479 return ANIM_EVENT_NONE;
11481 s_ptr = matching_char + pattern_1_len;
11483 // check for main animation number ("anim_X" or "anim_XX")
11484 if (*s_ptr >= '0' && *s_ptr <= '9')
11486 int gic_anim_nr = (*s_ptr++ - '0');
11488 if (*s_ptr >= '0' && *s_ptr <= '9')
11489 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11491 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11492 return ANIM_EVENT_NONE;
11494 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11498 // invalid main animation number specified
11500 return ANIM_EVENT_NONE;
11503 // check for animation part number ("part_X" or "part_XX") (optional)
11504 if (strPrefix(s_ptr, pattern_2))
11506 s_ptr += strlen(pattern_2);
11508 if (*s_ptr >= '0' && *s_ptr <= '9')
11510 int gic_part_nr = (*s_ptr++ - '0');
11512 if (*s_ptr >= '0' && *s_ptr <= '9')
11513 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11515 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11516 return ANIM_EVENT_NONE;
11518 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11522 // invalid animation part number specified
11524 return ANIM_EVENT_NONE;
11528 // discard result if next character is neither delimiter nor whitespace
11529 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11530 *s_ptr == ' ' || *s_ptr == '\t'))
11531 return ANIM_EVENT_NONE;
11536 static int get_anim_parameter_values(char *s)
11538 int list_pos = ANIM_EVENT_UNDEFINED;
11539 int event_value = ANIM_EVENT_DEFAULT;
11541 if (string_has_parameter(s, "any"))
11542 event_value |= ANIM_EVENT_ANY;
11544 if (string_has_parameter(s, "click:self") ||
11545 string_has_parameter(s, "click") ||
11546 string_has_parameter(s, "self"))
11547 event_value |= ANIM_EVENT_SELF;
11549 if (string_has_parameter(s, "unclick:any"))
11550 event_value |= ANIM_EVENT_UNCLICK_ANY;
11552 // if animation event found, add it to global animation event list
11553 if (event_value != ANIM_EVENT_NONE)
11554 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11558 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11559 event_value = get_anim_parameter_value(s);
11561 // if animation event found, add it to global animation event list
11562 if (event_value != ANIM_EVENT_NONE)
11563 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11565 // continue with next part of the string, starting with next comma
11566 s = strchr(s + 1, ',');
11572 static int get_anim_action_parameter_value(char *token)
11574 // check most common default case first to massively speed things up
11575 if (strEqual(token, ARG_UNDEFINED))
11576 return ANIM_EVENT_ACTION_NONE;
11578 int result = getImageIDFromToken(token);
11582 char *gfx_token = getStringCat2("gfx.", token);
11584 result = getImageIDFromToken(gfx_token);
11586 checked_free(gfx_token);
11591 Key key = getKeyFromX11KeyName(token);
11593 if (key != KSYM_UNDEFINED)
11594 result = -(int)key;
11601 result = get_hash_from_key(token); // unsigned int => int
11602 result = ABS(result); // may be negative now
11603 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11605 setHashEntry(anim_url_hash, int2str(result, 0), token);
11610 result = ANIM_EVENT_ACTION_NONE;
11615 int get_parameter_value(char *value_raw, char *suffix, int type)
11617 char *value = getStringToLower(value_raw);
11618 int result = 0; // probably a save default value
11620 if (strEqual(suffix, ".direction"))
11622 result = (strEqual(value, "left") ? MV_LEFT :
11623 strEqual(value, "right") ? MV_RIGHT :
11624 strEqual(value, "up") ? MV_UP :
11625 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11627 else if (strEqual(suffix, ".position"))
11629 result = (strEqual(value, "left") ? POS_LEFT :
11630 strEqual(value, "right") ? POS_RIGHT :
11631 strEqual(value, "top") ? POS_TOP :
11632 strEqual(value, "upper") ? POS_UPPER :
11633 strEqual(value, "middle") ? POS_MIDDLE :
11634 strEqual(value, "lower") ? POS_LOWER :
11635 strEqual(value, "bottom") ? POS_BOTTOM :
11636 strEqual(value, "any") ? POS_ANY :
11637 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11639 else if (strEqual(suffix, ".align"))
11641 result = (strEqual(value, "left") ? ALIGN_LEFT :
11642 strEqual(value, "right") ? ALIGN_RIGHT :
11643 strEqual(value, "center") ? ALIGN_CENTER :
11644 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11646 else if (strEqual(suffix, ".valign"))
11648 result = (strEqual(value, "top") ? VALIGN_TOP :
11649 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11650 strEqual(value, "middle") ? VALIGN_MIDDLE :
11651 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11653 else if (strEqual(suffix, ".anim_mode"))
11655 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11656 string_has_parameter(value, "loop") ? ANIM_LOOP :
11657 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11658 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11659 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11660 string_has_parameter(value, "random") ? ANIM_RANDOM :
11661 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11662 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11663 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11664 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11665 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11666 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11667 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11668 string_has_parameter(value, "all") ? ANIM_ALL :
11669 string_has_parameter(value, "tiled") ? ANIM_TILED :
11672 if (string_has_parameter(value, "once"))
11673 result |= ANIM_ONCE;
11675 if (string_has_parameter(value, "reverse"))
11676 result |= ANIM_REVERSE;
11678 if (string_has_parameter(value, "opaque_player"))
11679 result |= ANIM_OPAQUE_PLAYER;
11681 if (string_has_parameter(value, "static_panel"))
11682 result |= ANIM_STATIC_PANEL;
11684 else if (strEqual(suffix, ".init_event") ||
11685 strEqual(suffix, ".anim_event"))
11687 result = get_anim_parameter_values(value);
11689 else if (strEqual(suffix, ".init_delay_action") ||
11690 strEqual(suffix, ".anim_delay_action") ||
11691 strEqual(suffix, ".post_delay_action") ||
11692 strEqual(suffix, ".init_event_action") ||
11693 strEqual(suffix, ".anim_event_action"))
11695 result = get_anim_action_parameter_value(value_raw);
11697 else if (strEqual(suffix, ".class"))
11699 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11700 get_hash_from_key(value));
11702 else if (strEqual(suffix, ".style"))
11704 result = STYLE_DEFAULT;
11706 if (string_has_parameter(value, "accurate_borders"))
11707 result |= STYLE_ACCURATE_BORDERS;
11709 if (string_has_parameter(value, "inner_corners"))
11710 result |= STYLE_INNER_CORNERS;
11712 if (string_has_parameter(value, "reverse"))
11713 result |= STYLE_REVERSE;
11715 if (string_has_parameter(value, "leftmost_position"))
11716 result |= STYLE_LEFTMOST_POSITION;
11718 if (string_has_parameter(value, "block_clicks"))
11719 result |= STYLE_BLOCK;
11721 if (string_has_parameter(value, "passthrough_clicks"))
11722 result |= STYLE_PASSTHROUGH;
11724 if (string_has_parameter(value, "multiple_actions"))
11725 result |= STYLE_MULTIPLE_ACTIONS;
11727 else if (strEqual(suffix, ".fade_mode"))
11729 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11730 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11731 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11732 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11733 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11734 FADE_MODE_DEFAULT);
11736 else if (strEqual(suffix, ".auto_delay_unit"))
11738 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11739 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11740 AUTO_DELAY_UNIT_DEFAULT);
11742 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11744 result = gfx.get_font_from_token_function(value);
11746 else // generic parameter of type integer or boolean
11748 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11749 type == TYPE_INTEGER ? get_integer_from_string(value) :
11750 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11751 ARG_UNDEFINED_VALUE);
11759 static int get_token_parameter_value(char *token, char *value_raw)
11763 if (token == NULL || value_raw == NULL)
11764 return ARG_UNDEFINED_VALUE;
11766 suffix = strrchr(token, '.');
11767 if (suffix == NULL)
11770 if (strEqual(suffix, ".element"))
11771 return getElementFromToken(value_raw);
11773 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11774 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11777 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11778 boolean ignore_defaults)
11782 for (i = 0; image_config_vars[i].token != NULL; i++)
11784 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11786 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11787 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11791 *image_config_vars[i].value =
11792 get_token_parameter_value(image_config_vars[i].token, value);
11796 void InitMenuDesignSettings_Static(void)
11798 // always start with reliable default values from static default config
11799 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11802 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11806 // the following initializes hierarchical values from static configuration
11808 // special case: initialize "ARG_DEFAULT" values in static default config
11809 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11810 titlescreen_initial_first_default.fade_mode =
11811 title_initial_first_default.fade_mode;
11812 titlescreen_initial_first_default.fade_delay =
11813 title_initial_first_default.fade_delay;
11814 titlescreen_initial_first_default.post_delay =
11815 title_initial_first_default.post_delay;
11816 titlescreen_initial_first_default.auto_delay =
11817 title_initial_first_default.auto_delay;
11818 titlescreen_initial_first_default.auto_delay_unit =
11819 title_initial_first_default.auto_delay_unit;
11820 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
11821 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11822 titlescreen_first_default.post_delay = title_first_default.post_delay;
11823 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11824 titlescreen_first_default.auto_delay_unit =
11825 title_first_default.auto_delay_unit;
11826 titlemessage_initial_first_default.fade_mode =
11827 title_initial_first_default.fade_mode;
11828 titlemessage_initial_first_default.fade_delay =
11829 title_initial_first_default.fade_delay;
11830 titlemessage_initial_first_default.post_delay =
11831 title_initial_first_default.post_delay;
11832 titlemessage_initial_first_default.auto_delay =
11833 title_initial_first_default.auto_delay;
11834 titlemessage_initial_first_default.auto_delay_unit =
11835 title_initial_first_default.auto_delay_unit;
11836 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
11837 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11838 titlemessage_first_default.post_delay = title_first_default.post_delay;
11839 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11840 titlemessage_first_default.auto_delay_unit =
11841 title_first_default.auto_delay_unit;
11843 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
11844 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11845 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11846 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11847 titlescreen_initial_default.auto_delay_unit =
11848 title_initial_default.auto_delay_unit;
11849 titlescreen_default.fade_mode = title_default.fade_mode;
11850 titlescreen_default.fade_delay = title_default.fade_delay;
11851 titlescreen_default.post_delay = title_default.post_delay;
11852 titlescreen_default.auto_delay = title_default.auto_delay;
11853 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11854 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
11855 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11856 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11857 titlemessage_initial_default.auto_delay_unit =
11858 title_initial_default.auto_delay_unit;
11859 titlemessage_default.fade_mode = title_default.fade_mode;
11860 titlemessage_default.fade_delay = title_default.fade_delay;
11861 titlemessage_default.post_delay = title_default.post_delay;
11862 titlemessage_default.auto_delay = title_default.auto_delay;
11863 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11865 // special case: initialize "ARG_DEFAULT" values in static default config
11866 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11867 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11869 titlescreen_initial_first[i] = titlescreen_initial_first_default;
11870 titlescreen_first[i] = titlescreen_first_default;
11871 titlemessage_initial_first[i] = titlemessage_initial_first_default;
11872 titlemessage_first[i] = titlemessage_first_default;
11874 titlescreen_initial[i] = titlescreen_initial_default;
11875 titlescreen[i] = titlescreen_default;
11876 titlemessage_initial[i] = titlemessage_initial_default;
11877 titlemessage[i] = titlemessage_default;
11880 // special case: initialize "ARG_DEFAULT" values in static default config
11881 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11882 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11884 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11887 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11888 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11889 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11892 // special case: initialize "ARG_DEFAULT" values in static default config
11893 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11894 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11896 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11897 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11898 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11900 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
11903 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11907 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11911 struct XY *dst, *src;
11913 game_buttons_xy[] =
11915 { &game.button.save, &game.button.stop },
11916 { &game.button.pause2, &game.button.pause },
11917 { &game.button.load, &game.button.play },
11918 { &game.button.undo, &game.button.stop },
11919 { &game.button.redo, &game.button.play },
11925 // special case: initialize later added SETUP list size from LEVELS value
11926 if (menu.list_size[GAME_MODE_SETUP] == -1)
11927 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11929 // set default position for snapshot buttons to stop/pause/play buttons
11930 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11931 if ((*game_buttons_xy[i].dst).x == -1 &&
11932 (*game_buttons_xy[i].dst).y == -1)
11933 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11935 // --------------------------------------------------------------------------
11936 // dynamic viewports (including playfield margins, borders and alignments)
11937 // --------------------------------------------------------------------------
11939 // dynamic viewports currently only supported for landscape mode
11940 int display_width = MAX(video.display_width, video.display_height);
11941 int display_height = MIN(video.display_width, video.display_height);
11943 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11945 struct RectWithBorder *vp_window = &viewport.window[i];
11946 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11947 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
11948 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
11949 boolean dynamic_window_width = (vp_window->min_width != -1);
11950 boolean dynamic_window_height = (vp_window->min_height != -1);
11951 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
11952 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11954 // adjust window size if min/max width/height is specified
11956 if (vp_window->min_width != -1)
11958 int window_width = display_width;
11960 // when using static window height, use aspect ratio of display
11961 if (vp_window->min_height == -1)
11962 window_width = vp_window->height * display_width / display_height;
11964 vp_window->width = MAX(vp_window->min_width, window_width);
11967 if (vp_window->min_height != -1)
11969 int window_height = display_height;
11971 // when using static window width, use aspect ratio of display
11972 if (vp_window->min_width == -1)
11973 window_height = vp_window->width * display_height / display_width;
11975 vp_window->height = MAX(vp_window->min_height, window_height);
11978 if (vp_window->max_width != -1)
11979 vp_window->width = MIN(vp_window->width, vp_window->max_width);
11981 if (vp_window->max_height != -1)
11982 vp_window->height = MIN(vp_window->height, vp_window->max_height);
11984 int playfield_width = vp_window->width;
11985 int playfield_height = vp_window->height;
11987 // adjust playfield size and position according to specified margins
11989 playfield_width -= vp_playfield->margin_left;
11990 playfield_width -= vp_playfield->margin_right;
11992 playfield_height -= vp_playfield->margin_top;
11993 playfield_height -= vp_playfield->margin_bottom;
11995 // adjust playfield size if min/max width/height is specified
11997 if (vp_playfield->min_width != -1)
11998 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12000 if (vp_playfield->min_height != -1)
12001 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12003 if (vp_playfield->max_width != -1)
12004 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12006 if (vp_playfield->max_height != -1)
12007 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12009 // adjust playfield position according to specified alignment
12011 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12012 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12013 else if (vp_playfield->align == ALIGN_CENTER)
12014 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12015 else if (vp_playfield->align == ALIGN_RIGHT)
12016 vp_playfield->x += playfield_width - vp_playfield->width;
12018 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12019 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12020 else if (vp_playfield->valign == VALIGN_MIDDLE)
12021 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12022 else if (vp_playfield->valign == VALIGN_BOTTOM)
12023 vp_playfield->y += playfield_height - vp_playfield->height;
12025 vp_playfield->x += vp_playfield->margin_left;
12026 vp_playfield->y += vp_playfield->margin_top;
12028 // adjust individual playfield borders if only default border is specified
12030 if (vp_playfield->border_left == -1)
12031 vp_playfield->border_left = vp_playfield->border_size;
12032 if (vp_playfield->border_right == -1)
12033 vp_playfield->border_right = vp_playfield->border_size;
12034 if (vp_playfield->border_top == -1)
12035 vp_playfield->border_top = vp_playfield->border_size;
12036 if (vp_playfield->border_bottom == -1)
12037 vp_playfield->border_bottom = vp_playfield->border_size;
12039 // set dynamic playfield borders if borders are specified as undefined
12040 // (but only if window size was dynamic and playfield size was static)
12042 if (dynamic_window_width && !dynamic_playfield_width)
12044 if (vp_playfield->border_left == -1)
12046 vp_playfield->border_left = (vp_playfield->x -
12047 vp_playfield->margin_left);
12048 vp_playfield->x -= vp_playfield->border_left;
12049 vp_playfield->width += vp_playfield->border_left;
12052 if (vp_playfield->border_right == -1)
12054 vp_playfield->border_right = (vp_window->width -
12056 vp_playfield->width -
12057 vp_playfield->margin_right);
12058 vp_playfield->width += vp_playfield->border_right;
12062 if (dynamic_window_height && !dynamic_playfield_height)
12064 if (vp_playfield->border_top == -1)
12066 vp_playfield->border_top = (vp_playfield->y -
12067 vp_playfield->margin_top);
12068 vp_playfield->y -= vp_playfield->border_top;
12069 vp_playfield->height += vp_playfield->border_top;
12072 if (vp_playfield->border_bottom == -1)
12074 vp_playfield->border_bottom = (vp_window->height -
12076 vp_playfield->height -
12077 vp_playfield->margin_bottom);
12078 vp_playfield->height += vp_playfield->border_bottom;
12082 // adjust playfield size to be a multiple of a defined alignment tile size
12084 int align_size = vp_playfield->align_size;
12085 int playfield_xtiles = vp_playfield->width / align_size;
12086 int playfield_ytiles = vp_playfield->height / align_size;
12087 int playfield_width_corrected = playfield_xtiles * align_size;
12088 int playfield_height_corrected = playfield_ytiles * align_size;
12089 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12090 i == GFX_SPECIAL_ARG_EDITOR);
12092 if (is_playfield_mode &&
12093 dynamic_playfield_width &&
12094 vp_playfield->width != playfield_width_corrected)
12096 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12098 vp_playfield->width = playfield_width_corrected;
12100 if (vp_playfield->align == ALIGN_LEFT)
12102 vp_playfield->border_left += playfield_xdiff;
12104 else if (vp_playfield->align == ALIGN_RIGHT)
12106 vp_playfield->border_right += playfield_xdiff;
12108 else if (vp_playfield->align == ALIGN_CENTER)
12110 int border_left_diff = playfield_xdiff / 2;
12111 int border_right_diff = playfield_xdiff - border_left_diff;
12113 vp_playfield->border_left += border_left_diff;
12114 vp_playfield->border_right += border_right_diff;
12118 if (is_playfield_mode &&
12119 dynamic_playfield_height &&
12120 vp_playfield->height != playfield_height_corrected)
12122 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12124 vp_playfield->height = playfield_height_corrected;
12126 if (vp_playfield->valign == VALIGN_TOP)
12128 vp_playfield->border_top += playfield_ydiff;
12130 else if (vp_playfield->align == VALIGN_BOTTOM)
12132 vp_playfield->border_right += playfield_ydiff;
12134 else if (vp_playfield->align == VALIGN_MIDDLE)
12136 int border_top_diff = playfield_ydiff / 2;
12137 int border_bottom_diff = playfield_ydiff - border_top_diff;
12139 vp_playfield->border_top += border_top_diff;
12140 vp_playfield->border_bottom += border_bottom_diff;
12144 // adjust door positions according to specified alignment
12146 for (j = 0; j < 2; j++)
12148 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12150 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12151 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12152 else if (vp_door->align == ALIGN_CENTER)
12153 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12154 else if (vp_door->align == ALIGN_RIGHT)
12155 vp_door->x += vp_window->width - vp_door->width;
12157 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12158 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12159 else if (vp_door->valign == VALIGN_MIDDLE)
12160 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12161 else if (vp_door->valign == VALIGN_BOTTOM)
12162 vp_door->y += vp_window->height - vp_door->height;
12167 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12171 struct XYTileSize *dst, *src;
12174 editor_buttons_xy[] =
12177 &editor.button.element_left, &editor.palette.element_left,
12178 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12181 &editor.button.element_middle, &editor.palette.element_middle,
12182 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12185 &editor.button.element_right, &editor.palette.element_right,
12186 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12193 // set default position for element buttons to element graphics
12194 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12196 if ((*editor_buttons_xy[i].dst).x == -1 &&
12197 (*editor_buttons_xy[i].dst).y == -1)
12199 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12201 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12203 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12207 // adjust editor palette rows and columns if specified to be dynamic
12209 if (editor.palette.cols == -1)
12211 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12212 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12213 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12215 editor.palette.cols = (vp_width - sc_width) / bt_width;
12217 if (editor.palette.x == -1)
12219 int palette_width = editor.palette.cols * bt_width + sc_width;
12221 editor.palette.x = (vp_width - palette_width) / 2;
12225 if (editor.palette.rows == -1)
12227 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12228 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12229 int tx_height = getFontHeight(FONT_TEXT_2);
12231 editor.palette.rows = (vp_height - tx_height) / bt_height;
12233 if (editor.palette.y == -1)
12235 int palette_height = editor.palette.rows * bt_height + tx_height;
12237 editor.palette.y = (vp_height - palette_height) / 2;
12242 static void LoadMenuDesignSettingsFromFilename(char *filename)
12244 static struct TitleFadingInfo tfi;
12245 static struct TitleMessageInfo tmi;
12246 static struct TokenInfo title_tokens[] =
12248 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12249 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12250 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12251 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12252 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12256 static struct TokenInfo titlemessage_tokens[] =
12258 { TYPE_INTEGER, &tmi.x, ".x" },
12259 { TYPE_INTEGER, &tmi.y, ".y" },
12260 { TYPE_INTEGER, &tmi.width, ".width" },
12261 { TYPE_INTEGER, &tmi.height, ".height" },
12262 { TYPE_INTEGER, &tmi.chars, ".chars" },
12263 { TYPE_INTEGER, &tmi.lines, ".lines" },
12264 { TYPE_INTEGER, &tmi.align, ".align" },
12265 { TYPE_INTEGER, &tmi.valign, ".valign" },
12266 { TYPE_INTEGER, &tmi.font, ".font" },
12267 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12268 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12269 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12270 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12271 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12272 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12273 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12274 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12275 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12281 struct TitleFadingInfo *info;
12286 // initialize first titles from "enter screen" definitions, if defined
12287 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12288 { &title_first_default, "menu.enter_screen.TITLE" },
12290 // initialize title screens from "next screen" definitions, if defined
12291 { &title_initial_default, "menu.next_screen.TITLE" },
12292 { &title_default, "menu.next_screen.TITLE" },
12298 struct TitleMessageInfo *array;
12301 titlemessage_arrays[] =
12303 // initialize first titles from "enter screen" definitions, if defined
12304 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12305 { titlescreen_first, "menu.enter_screen.TITLE" },
12306 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12307 { titlemessage_first, "menu.enter_screen.TITLE" },
12309 // initialize titles from "next screen" definitions, if defined
12310 { titlescreen_initial, "menu.next_screen.TITLE" },
12311 { titlescreen, "menu.next_screen.TITLE" },
12312 { titlemessage_initial, "menu.next_screen.TITLE" },
12313 { titlemessage, "menu.next_screen.TITLE" },
12315 // overwrite titles with title definitions, if defined
12316 { titlescreen_initial_first, "[title_initial]" },
12317 { titlescreen_first, "[title]" },
12318 { titlemessage_initial_first, "[title_initial]" },
12319 { titlemessage_first, "[title]" },
12321 { titlescreen_initial, "[title_initial]" },
12322 { titlescreen, "[title]" },
12323 { titlemessage_initial, "[title_initial]" },
12324 { titlemessage, "[title]" },
12326 // overwrite titles with title screen/message definitions, if defined
12327 { titlescreen_initial_first, "[titlescreen_initial]" },
12328 { titlescreen_first, "[titlescreen]" },
12329 { titlemessage_initial_first, "[titlemessage_initial]" },
12330 { titlemessage_first, "[titlemessage]" },
12332 { titlescreen_initial, "[titlescreen_initial]" },
12333 { titlescreen, "[titlescreen]" },
12334 { titlemessage_initial, "[titlemessage_initial]" },
12335 { titlemessage, "[titlemessage]" },
12339 SetupFileHash *setup_file_hash;
12342 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12345 // the following initializes hierarchical values from dynamic configuration
12347 // special case: initialize with default values that may be overwritten
12348 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12349 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12351 struct TokenIntPtrInfo menu_config[] =
12353 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12354 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12355 { "menu.list_size", &menu.list_size[i] }
12358 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12360 char *token = menu_config[j].token;
12361 char *value = getHashEntry(setup_file_hash, token);
12364 *menu_config[j].value = get_integer_from_string(value);
12368 // special case: initialize with default values that may be overwritten
12369 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12370 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12372 struct TokenIntPtrInfo menu_config[] =
12374 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12375 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12376 { "menu.list_size.INFO", &menu.list_size_info[i] }
12379 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12381 char *token = menu_config[j].token;
12382 char *value = getHashEntry(setup_file_hash, token);
12385 *menu_config[j].value = get_integer_from_string(value);
12389 // special case: initialize with default values that may be overwritten
12390 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12391 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12393 struct TokenIntPtrInfo menu_config[] =
12395 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12396 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12399 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12401 char *token = menu_config[j].token;
12402 char *value = getHashEntry(setup_file_hash, token);
12405 *menu_config[j].value = get_integer_from_string(value);
12409 // special case: initialize with default values that may be overwritten
12410 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12411 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12413 struct TokenIntPtrInfo menu_config[] =
12415 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12416 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12417 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12418 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12419 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12420 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12421 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12422 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12423 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12426 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12428 char *token = menu_config[j].token;
12429 char *value = getHashEntry(setup_file_hash, token);
12432 *menu_config[j].value = get_integer_from_string(value);
12436 // special case: initialize with default values that may be overwritten
12437 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12438 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12440 struct TokenIntPtrInfo menu_config[] =
12442 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12443 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12444 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12445 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12446 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12447 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12448 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12449 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12450 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12453 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12455 char *token = menu_config[j].token;
12456 char *value = getHashEntry(setup_file_hash, token);
12459 *menu_config[j].value = get_token_parameter_value(token, value);
12463 // special case: initialize with default values that may be overwritten
12464 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12465 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12469 char *token_prefix;
12470 struct RectWithBorder *struct_ptr;
12474 { "viewport.window", &viewport.window[i] },
12475 { "viewport.playfield", &viewport.playfield[i] },
12476 { "viewport.door_1", &viewport.door_1[i] },
12477 { "viewport.door_2", &viewport.door_2[i] }
12480 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12482 struct TokenIntPtrInfo vp_config[] =
12484 { ".x", &vp_struct[j].struct_ptr->x },
12485 { ".y", &vp_struct[j].struct_ptr->y },
12486 { ".width", &vp_struct[j].struct_ptr->width },
12487 { ".height", &vp_struct[j].struct_ptr->height },
12488 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12489 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12490 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12491 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12492 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12493 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12494 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12495 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12496 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12497 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12498 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12499 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12500 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12501 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12502 { ".align", &vp_struct[j].struct_ptr->align },
12503 { ".valign", &vp_struct[j].struct_ptr->valign }
12506 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12508 char *token = getStringCat2(vp_struct[j].token_prefix,
12509 vp_config[k].token);
12510 char *value = getHashEntry(setup_file_hash, token);
12513 *vp_config[k].value = get_token_parameter_value(token, value);
12520 // special case: initialize with default values that may be overwritten
12521 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12522 for (i = 0; title_info[i].info != NULL; i++)
12524 struct TitleFadingInfo *info = title_info[i].info;
12525 char *base_token = title_info[i].text;
12527 for (j = 0; title_tokens[j].type != -1; j++)
12529 char *token = getStringCat2(base_token, title_tokens[j].text);
12530 char *value = getHashEntry(setup_file_hash, token);
12534 int parameter_value = get_token_parameter_value(token, value);
12538 *(int *)title_tokens[j].value = (int)parameter_value;
12547 // special case: initialize with default values that may be overwritten
12548 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12549 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12551 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12552 char *base_token = titlemessage_arrays[i].text;
12554 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12556 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12557 char *value = getHashEntry(setup_file_hash, token);
12561 int parameter_value = get_token_parameter_value(token, value);
12563 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12567 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12568 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12570 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12580 // special case: check if network and preview player positions are redefined,
12581 // to compare this later against the main menu level preview being redefined
12582 struct TokenIntPtrInfo menu_config_players[] =
12584 { "main.network_players.x", &menu.main.network_players.redefined },
12585 { "main.network_players.y", &menu.main.network_players.redefined },
12586 { "main.preview_players.x", &menu.main.preview_players.redefined },
12587 { "main.preview_players.y", &menu.main.preview_players.redefined },
12588 { "preview.x", &preview.redefined },
12589 { "preview.y", &preview.redefined }
12592 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12593 *menu_config_players[i].value = FALSE;
12595 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12596 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12597 *menu_config_players[i].value = TRUE;
12599 // read (and overwrite with) values that may be specified in config file
12600 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12602 freeSetupFileHash(setup_file_hash);
12605 void LoadMenuDesignSettings(void)
12607 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12609 InitMenuDesignSettings_Static();
12610 InitMenuDesignSettings_SpecialPreProcessing();
12612 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12614 // first look for special settings configured in level series config
12615 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12617 if (fileExists(filename_base))
12618 LoadMenuDesignSettingsFromFilename(filename_base);
12621 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12623 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12624 LoadMenuDesignSettingsFromFilename(filename_local);
12626 InitMenuDesignSettings_SpecialPostProcessing();
12629 void LoadMenuDesignSettings_AfterGraphics(void)
12631 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12634 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12636 char *filename = getEditorSetupFilename();
12637 SetupFileList *setup_file_list, *list;
12638 SetupFileHash *element_hash;
12639 int num_unknown_tokens = 0;
12642 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12645 element_hash = newSetupFileHash();
12647 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12648 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12650 // determined size may be larger than needed (due to unknown elements)
12652 for (list = setup_file_list; list != NULL; list = list->next)
12655 // add space for up to 3 more elements for padding that may be needed
12656 *num_elements += 3;
12658 // free memory for old list of elements, if needed
12659 checked_free(*elements);
12661 // allocate memory for new list of elements
12662 *elements = checked_malloc(*num_elements * sizeof(int));
12665 for (list = setup_file_list; list != NULL; list = list->next)
12667 char *value = getHashEntry(element_hash, list->token);
12669 if (value == NULL) // try to find obsolete token mapping
12671 char *mapped_token = get_mapped_token(list->token);
12673 if (mapped_token != NULL)
12675 value = getHashEntry(element_hash, mapped_token);
12677 free(mapped_token);
12683 (*elements)[(*num_elements)++] = atoi(value);
12687 if (num_unknown_tokens == 0)
12690 Warn("unknown token(s) found in config file:");
12691 Warn("- config file: '%s'", filename);
12693 num_unknown_tokens++;
12696 Warn("- token: '%s'", list->token);
12700 if (num_unknown_tokens > 0)
12703 while (*num_elements % 4) // pad with empty elements, if needed
12704 (*elements)[(*num_elements)++] = EL_EMPTY;
12706 freeSetupFileList(setup_file_list);
12707 freeSetupFileHash(element_hash);
12710 for (i = 0; i < *num_elements; i++)
12711 Debug("editor", "element '%s' [%d]\n",
12712 element_info[(*elements)[i]].token_name, (*elements)[i]);
12716 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12719 SetupFileHash *setup_file_hash = NULL;
12720 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12721 char *filename_music, *filename_prefix, *filename_info;
12727 token_to_value_ptr[] =
12729 { "title_header", &tmp_music_file_info.title_header },
12730 { "artist_header", &tmp_music_file_info.artist_header },
12731 { "album_header", &tmp_music_file_info.album_header },
12732 { "year_header", &tmp_music_file_info.year_header },
12734 { "title", &tmp_music_file_info.title },
12735 { "artist", &tmp_music_file_info.artist },
12736 { "album", &tmp_music_file_info.album },
12737 { "year", &tmp_music_file_info.year },
12743 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12744 getCustomMusicFilename(basename));
12746 if (filename_music == NULL)
12749 // ---------- try to replace file extension ----------
12751 filename_prefix = getStringCopy(filename_music);
12752 if (strrchr(filename_prefix, '.') != NULL)
12753 *strrchr(filename_prefix, '.') = '\0';
12754 filename_info = getStringCat2(filename_prefix, ".txt");
12756 if (fileExists(filename_info))
12757 setup_file_hash = loadSetupFileHash(filename_info);
12759 free(filename_prefix);
12760 free(filename_info);
12762 if (setup_file_hash == NULL)
12764 // ---------- try to add file extension ----------
12766 filename_prefix = getStringCopy(filename_music);
12767 filename_info = getStringCat2(filename_prefix, ".txt");
12769 if (fileExists(filename_info))
12770 setup_file_hash = loadSetupFileHash(filename_info);
12772 free(filename_prefix);
12773 free(filename_info);
12776 if (setup_file_hash == NULL)
12779 // ---------- music file info found ----------
12781 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12783 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12785 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12787 *token_to_value_ptr[i].value_ptr =
12788 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12791 tmp_music_file_info.basename = getStringCopy(basename);
12792 tmp_music_file_info.music = music;
12793 tmp_music_file_info.is_sound = is_sound;
12795 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12796 *new_music_file_info = tmp_music_file_info;
12798 return new_music_file_info;
12801 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12803 return get_music_file_info_ext(basename, music, FALSE);
12806 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12808 return get_music_file_info_ext(basename, sound, TRUE);
12811 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12812 char *basename, boolean is_sound)
12814 for (; list != NULL; list = list->next)
12815 if (list->is_sound == is_sound && strEqual(list->basename, basename))
12821 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12823 return music_info_listed_ext(list, basename, FALSE);
12826 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12828 return music_info_listed_ext(list, basename, TRUE);
12831 void LoadMusicInfo(void)
12833 char *music_directory = getCustomMusicDirectory();
12834 int num_music = getMusicListSize();
12835 int num_music_noconf = 0;
12836 int num_sounds = getSoundListSize();
12838 DirectoryEntry *dir_entry;
12839 struct FileInfo *music, *sound;
12840 struct MusicFileInfo *next, **new;
12843 while (music_file_info != NULL)
12845 next = music_file_info->next;
12847 checked_free(music_file_info->basename);
12849 checked_free(music_file_info->title_header);
12850 checked_free(music_file_info->artist_header);
12851 checked_free(music_file_info->album_header);
12852 checked_free(music_file_info->year_header);
12854 checked_free(music_file_info->title);
12855 checked_free(music_file_info->artist);
12856 checked_free(music_file_info->album);
12857 checked_free(music_file_info->year);
12859 free(music_file_info);
12861 music_file_info = next;
12864 new = &music_file_info;
12866 for (i = 0; i < num_music; i++)
12868 music = getMusicListEntry(i);
12870 if (music->filename == NULL)
12873 if (strEqual(music->filename, UNDEFINED_FILENAME))
12876 // a configured file may be not recognized as music
12877 if (!FileIsMusic(music->filename))
12880 if (!music_info_listed(music_file_info, music->filename))
12882 *new = get_music_file_info(music->filename, i);
12885 new = &(*new)->next;
12889 if ((dir = openDirectory(music_directory)) == NULL)
12891 Warn("cannot read music directory '%s'", music_directory);
12896 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12898 char *basename = dir_entry->basename;
12899 boolean music_already_used = FALSE;
12902 // skip all music files that are configured in music config file
12903 for (i = 0; i < num_music; i++)
12905 music = getMusicListEntry(i);
12907 if (music->filename == NULL)
12910 if (strEqual(basename, music->filename))
12912 music_already_used = TRUE;
12917 if (music_already_used)
12920 if (!FileIsMusic(dir_entry->filename))
12923 if (!music_info_listed(music_file_info, basename))
12925 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12928 new = &(*new)->next;
12931 num_music_noconf++;
12934 closeDirectory(dir);
12936 for (i = 0; i < num_sounds; i++)
12938 sound = getSoundListEntry(i);
12940 if (sound->filename == NULL)
12943 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12946 // a configured file may be not recognized as sound
12947 if (!FileIsSound(sound->filename))
12950 if (!sound_info_listed(music_file_info, sound->filename))
12952 *new = get_sound_file_info(sound->filename, i);
12954 new = &(*new)->next;
12958 // add pointers to previous list nodes
12960 struct MusicFileInfo *node = music_file_info;
12962 while (node != NULL)
12965 node->next->prev = node;
12971 static void add_helpanim_entry(int element, int action, int direction,
12972 int delay, int *num_list_entries)
12974 struct HelpAnimInfo *new_list_entry;
12975 (*num_list_entries)++;
12978 checked_realloc(helpanim_info,
12979 *num_list_entries * sizeof(struct HelpAnimInfo));
12980 new_list_entry = &helpanim_info[*num_list_entries - 1];
12982 new_list_entry->element = element;
12983 new_list_entry->action = action;
12984 new_list_entry->direction = direction;
12985 new_list_entry->delay = delay;
12988 static void print_unknown_token(char *filename, char *token, int token_nr)
12993 Warn("unknown token(s) found in config file:");
12994 Warn("- config file: '%s'", filename);
12997 Warn("- token: '%s'", token);
13000 static void print_unknown_token_end(int token_nr)
13006 void LoadHelpAnimInfo(void)
13008 char *filename = getHelpAnimFilename();
13009 SetupFileList *setup_file_list = NULL, *list;
13010 SetupFileHash *element_hash, *action_hash, *direction_hash;
13011 int num_list_entries = 0;
13012 int num_unknown_tokens = 0;
13015 if (fileExists(filename))
13016 setup_file_list = loadSetupFileList(filename);
13018 if (setup_file_list == NULL)
13020 // use reliable default values from static configuration
13021 SetupFileList *insert_ptr;
13023 insert_ptr = setup_file_list =
13024 newSetupFileList(helpanim_config[0].token,
13025 helpanim_config[0].value);
13027 for (i = 1; helpanim_config[i].token; i++)
13028 insert_ptr = addListEntry(insert_ptr,
13029 helpanim_config[i].token,
13030 helpanim_config[i].value);
13033 element_hash = newSetupFileHash();
13034 action_hash = newSetupFileHash();
13035 direction_hash = newSetupFileHash();
13037 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13038 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13040 for (i = 0; i < NUM_ACTIONS; i++)
13041 setHashEntry(action_hash, element_action_info[i].suffix,
13042 i_to_a(element_action_info[i].value));
13044 // do not store direction index (bit) here, but direction value!
13045 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13046 setHashEntry(direction_hash, element_direction_info[i].suffix,
13047 i_to_a(1 << element_direction_info[i].value));
13049 for (list = setup_file_list; list != NULL; list = list->next)
13051 char *element_token, *action_token, *direction_token;
13052 char *element_value, *action_value, *direction_value;
13053 int delay = atoi(list->value);
13055 if (strEqual(list->token, "end"))
13057 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13062 /* first try to break element into element/action/direction parts;
13063 if this does not work, also accept combined "element[.act][.dir]"
13064 elements (like "dynamite.active"), which are unique elements */
13066 if (strchr(list->token, '.') == NULL) // token contains no '.'
13068 element_value = getHashEntry(element_hash, list->token);
13069 if (element_value != NULL) // element found
13070 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13071 &num_list_entries);
13074 // no further suffixes found -- this is not an element
13075 print_unknown_token(filename, list->token, num_unknown_tokens++);
13081 // token has format "<prefix>.<something>"
13083 action_token = strchr(list->token, '.'); // suffix may be action ...
13084 direction_token = action_token; // ... or direction
13086 element_token = getStringCopy(list->token);
13087 *strchr(element_token, '.') = '\0';
13089 element_value = getHashEntry(element_hash, element_token);
13091 if (element_value == NULL) // this is no element
13093 element_value = getHashEntry(element_hash, list->token);
13094 if (element_value != NULL) // combined element found
13095 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13096 &num_list_entries);
13098 print_unknown_token(filename, list->token, num_unknown_tokens++);
13100 free(element_token);
13105 action_value = getHashEntry(action_hash, action_token);
13107 if (action_value != NULL) // action found
13109 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13110 &num_list_entries);
13112 free(element_token);
13117 direction_value = getHashEntry(direction_hash, direction_token);
13119 if (direction_value != NULL) // direction found
13121 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13122 &num_list_entries);
13124 free(element_token);
13129 if (strchr(action_token + 1, '.') == NULL)
13131 // no further suffixes found -- this is not an action nor direction
13133 element_value = getHashEntry(element_hash, list->token);
13134 if (element_value != NULL) // combined element found
13135 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13136 &num_list_entries);
13138 print_unknown_token(filename, list->token, num_unknown_tokens++);
13140 free(element_token);
13145 // token has format "<prefix>.<suffix>.<something>"
13147 direction_token = strchr(action_token + 1, '.');
13149 action_token = getStringCopy(action_token);
13150 *strchr(action_token + 1, '.') = '\0';
13152 action_value = getHashEntry(action_hash, action_token);
13154 if (action_value == NULL) // this is no action
13156 element_value = getHashEntry(element_hash, list->token);
13157 if (element_value != NULL) // combined element found
13158 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13159 &num_list_entries);
13161 print_unknown_token(filename, list->token, num_unknown_tokens++);
13163 free(element_token);
13164 free(action_token);
13169 direction_value = getHashEntry(direction_hash, direction_token);
13171 if (direction_value != NULL) // direction found
13173 add_helpanim_entry(atoi(element_value), atoi(action_value),
13174 atoi(direction_value), delay, &num_list_entries);
13176 free(element_token);
13177 free(action_token);
13182 // this is no direction
13184 element_value = getHashEntry(element_hash, list->token);
13185 if (element_value != NULL) // combined element found
13186 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13187 &num_list_entries);
13189 print_unknown_token(filename, list->token, num_unknown_tokens++);
13191 free(element_token);
13192 free(action_token);
13195 print_unknown_token_end(num_unknown_tokens);
13197 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13198 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13200 freeSetupFileList(setup_file_list);
13201 freeSetupFileHash(element_hash);
13202 freeSetupFileHash(action_hash);
13203 freeSetupFileHash(direction_hash);
13206 for (i = 0; i < num_list_entries; i++)
13207 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13208 EL_NAME(helpanim_info[i].element),
13209 helpanim_info[i].element,
13210 helpanim_info[i].action,
13211 helpanim_info[i].direction,
13212 helpanim_info[i].delay);
13216 void LoadHelpTextInfo(void)
13218 char *filename = getHelpTextFilename();
13221 if (helptext_info != NULL)
13223 freeSetupFileHash(helptext_info);
13224 helptext_info = NULL;
13227 if (fileExists(filename))
13228 helptext_info = loadSetupFileHash(filename);
13230 if (helptext_info == NULL)
13232 // use reliable default values from static configuration
13233 helptext_info = newSetupFileHash();
13235 for (i = 0; helptext_config[i].token; i++)
13236 setHashEntry(helptext_info,
13237 helptext_config[i].token,
13238 helptext_config[i].value);
13242 BEGIN_HASH_ITERATION(helptext_info, itr)
13244 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13245 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13247 END_HASH_ITERATION(hash, itr)
13252 // ----------------------------------------------------------------------------
13254 // ----------------------------------------------------------------------------
13256 #define MAX_NUM_CONVERT_LEVELS 1000
13258 void ConvertLevels(void)
13260 static LevelDirTree *convert_leveldir = NULL;
13261 static int convert_level_nr = -1;
13262 static int num_levels_handled = 0;
13263 static int num_levels_converted = 0;
13264 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13267 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13268 global.convert_leveldir);
13270 if (convert_leveldir == NULL)
13271 Fail("no such level identifier: '%s'", global.convert_leveldir);
13273 leveldir_current = convert_leveldir;
13275 if (global.convert_level_nr != -1)
13277 convert_leveldir->first_level = global.convert_level_nr;
13278 convert_leveldir->last_level = global.convert_level_nr;
13281 convert_level_nr = convert_leveldir->first_level;
13283 PrintLine("=", 79);
13284 Print("Converting levels\n");
13285 PrintLine("-", 79);
13286 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13287 Print("Level series name: '%s'\n", convert_leveldir->name);
13288 Print("Level series author: '%s'\n", convert_leveldir->author);
13289 Print("Number of levels: %d\n", convert_leveldir->levels);
13290 PrintLine("=", 79);
13293 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13294 levels_failed[i] = FALSE;
13296 while (convert_level_nr <= convert_leveldir->last_level)
13298 char *level_filename;
13301 level_nr = convert_level_nr++;
13303 Print("Level %03d: ", level_nr);
13305 LoadLevel(level_nr);
13306 if (level.no_level_file || level.no_valid_file)
13308 Print("(no level)\n");
13312 Print("converting level ... ");
13315 // special case: conversion of some EMC levels as requested by ACME
13316 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13319 level_filename = getDefaultLevelFilename(level_nr);
13320 new_level = !fileExists(level_filename);
13324 SaveLevel(level_nr);
13326 num_levels_converted++;
13328 Print("converted.\n");
13332 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13333 levels_failed[level_nr] = TRUE;
13335 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13338 num_levels_handled++;
13342 PrintLine("=", 79);
13343 Print("Number of levels handled: %d\n", num_levels_handled);
13344 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13345 (num_levels_handled ?
13346 num_levels_converted * 100 / num_levels_handled : 0));
13347 PrintLine("-", 79);
13348 Print("Summary (for automatic parsing by scripts):\n");
13349 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13350 convert_leveldir->identifier, num_levels_converted,
13351 num_levels_handled,
13352 (num_levels_handled ?
13353 num_levels_converted * 100 / num_levels_handled : 0));
13355 if (num_levels_handled != num_levels_converted)
13357 Print(", FAILED:");
13358 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13359 if (levels_failed[i])
13364 PrintLine("=", 79);
13366 CloseAllAndExit(0);
13370 // ----------------------------------------------------------------------------
13371 // create and save images for use in level sketches (raw BMP format)
13372 // ----------------------------------------------------------------------------
13374 void CreateLevelSketchImages(void)
13380 InitElementPropertiesGfxElement();
13382 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13383 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13385 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13387 int element = getMappedElement(i);
13388 char basename1[16];
13389 char basename2[16];
13393 sprintf(basename1, "%04d.bmp", i);
13394 sprintf(basename2, "%04ds.bmp", i);
13396 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13397 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13399 DrawSizedElement(0, 0, element, TILESIZE);
13400 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13402 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13403 Fail("cannot save level sketch image file '%s'", filename1);
13405 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13406 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13408 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13409 Fail("cannot save level sketch image file '%s'", filename2);
13414 // create corresponding SQL statements (for normal and small images)
13417 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13418 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13421 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13422 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13424 // optional: create content for forum level sketch demonstration post
13426 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13429 FreeBitmap(bitmap1);
13430 FreeBitmap(bitmap2);
13433 fprintf(stderr, "\n");
13435 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13437 CloseAllAndExit(0);
13441 // ----------------------------------------------------------------------------
13442 // create and save images for element collecting animations (raw BMP format)
13443 // ----------------------------------------------------------------------------
13445 static boolean createCollectImage(int element)
13447 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13450 void CreateCollectElementImages(void)
13454 int anim_frames = num_steps - 1;
13455 int tile_size = TILESIZE;
13456 int anim_width = tile_size * anim_frames;
13457 int anim_height = tile_size;
13458 int num_collect_images = 0;
13459 int pos_collect_images = 0;
13461 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13462 if (createCollectImage(i))
13463 num_collect_images++;
13465 Info("Creating %d element collecting animation images ...",
13466 num_collect_images);
13468 int dst_width = anim_width * 2;
13469 int dst_height = anim_height * num_collect_images / 2;
13470 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13471 char *basename_bmp = "RocksCollect.bmp";
13472 char *basename_png = "RocksCollect.png";
13473 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13474 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13475 int len_filename_bmp = strlen(filename_bmp);
13476 int len_filename_png = strlen(filename_png);
13477 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13478 char cmd_convert[max_command_len];
13480 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13484 // force using RGBA surface for destination bitmap
13485 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13486 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13488 dst_bitmap->surface =
13489 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_RGBA32, 0);
13491 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13493 if (!createCollectImage(i))
13496 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13497 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13498 int graphic = el2img(i);
13499 char *token_name = element_info[i].token_name;
13500 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13501 Bitmap *src_bitmap;
13504 Info("- creating collecting image for '%s' ...", token_name);
13506 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13508 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13509 tile_size, tile_size, 0, 0);
13511 // force using RGBA surface for temporary bitmap (using transparent black)
13512 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13513 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13515 tmp_bitmap->surface =
13516 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_RGBA32, 0);
13518 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13520 for (j = 0; j < anim_frames; j++)
13522 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13523 int frame_size = frame_size_final * num_steps;
13524 int offset = (tile_size - frame_size_final) / 2;
13525 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13527 while (frame_size > frame_size_final)
13531 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13533 FreeBitmap(frame_bitmap);
13535 frame_bitmap = half_bitmap;
13538 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13539 frame_size_final, frame_size_final,
13540 dst_x + j * tile_size + offset, dst_y + offset);
13542 FreeBitmap(frame_bitmap);
13545 tmp_bitmap->surface_masked = NULL;
13547 FreeBitmap(tmp_bitmap);
13549 pos_collect_images++;
13552 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13553 Fail("cannot save element collecting image file '%s'", filename_bmp);
13555 FreeBitmap(dst_bitmap);
13557 Info("Converting image file from BMP to PNG ...");
13559 system(cmd_convert);
13560 unlink(filename_bmp);
13564 CloseAllAndExit(0);
13568 // ----------------------------------------------------------------------------
13569 // create and save images for custom and group elements (raw BMP format)
13570 // ----------------------------------------------------------------------------
13572 void CreateCustomElementImages(char *directory)
13574 char *src_basename = "RocksCE-template.ilbm";
13575 char *dst_basename = "RocksCE.bmp";
13576 char *src_filename = getPath2(directory, src_basename);
13577 char *dst_filename = getPath2(directory, dst_basename);
13578 Bitmap *src_bitmap;
13580 int yoffset_ce = 0;
13581 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13584 InitVideoDefaults();
13586 ReCreateBitmap(&backbuffer, video.width, video.height);
13588 src_bitmap = LoadImage(src_filename);
13590 bitmap = CreateBitmap(TILEX * 16 * 2,
13591 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13594 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13601 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13602 TILEX * x, TILEY * y + yoffset_ce);
13604 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13606 TILEX * x + TILEX * 16,
13607 TILEY * y + yoffset_ce);
13609 for (j = 2; j >= 0; j--)
13613 BlitBitmap(src_bitmap, bitmap,
13614 TILEX + c * 7, 0, 6, 10,
13615 TILEX * x + 6 + j * 7,
13616 TILEY * y + 11 + yoffset_ce);
13618 BlitBitmap(src_bitmap, bitmap,
13619 TILEX + c * 8, TILEY, 6, 10,
13620 TILEX * 16 + TILEX * x + 6 + j * 8,
13621 TILEY * y + 10 + yoffset_ce);
13627 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13634 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13635 TILEX * x, TILEY * y + yoffset_ge);
13637 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13639 TILEX * x + TILEX * 16,
13640 TILEY * y + yoffset_ge);
13642 for (j = 1; j >= 0; j--)
13646 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13647 TILEX * x + 6 + j * 10,
13648 TILEY * y + 11 + yoffset_ge);
13650 BlitBitmap(src_bitmap, bitmap,
13651 TILEX + c * 8, TILEY + 12, 6, 10,
13652 TILEX * 16 + TILEX * x + 10 + j * 8,
13653 TILEY * y + 10 + yoffset_ge);
13659 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13660 Fail("cannot save CE graphics file '%s'", dst_filename);
13662 FreeBitmap(bitmap);
13664 CloseAllAndExit(0);