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 // the size of some chunks cannot be checked before reading other
3595 // chunks first (like "HEAD" and "BODY") that contain some header
3596 // information, so check them here
3597 if (chunk_size_expected != chunk_size)
3599 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3600 chunk_size, chunk_name, filename);
3610 // ----------------------------------------------------------------------------
3611 // functions for loading EM level
3612 // ----------------------------------------------------------------------------
3614 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3616 static int ball_xy[8][2] =
3627 struct LevelInfo_EM *level_em = level->native_em_level;
3628 struct CAVE *cav = level_em->cav;
3631 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3632 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3634 cav->time_seconds = level->time;
3635 cav->gems_needed = level->gems_needed;
3637 cav->emerald_score = level->score[SC_EMERALD];
3638 cav->diamond_score = level->score[SC_DIAMOND];
3639 cav->alien_score = level->score[SC_ROBOT];
3640 cav->tank_score = level->score[SC_SPACESHIP];
3641 cav->bug_score = level->score[SC_BUG];
3642 cav->eater_score = level->score[SC_YAMYAM];
3643 cav->nut_score = level->score[SC_NUT];
3644 cav->dynamite_score = level->score[SC_DYNAMITE];
3645 cav->key_score = level->score[SC_KEY];
3646 cav->exit_score = level->score[SC_TIME_BONUS];
3648 cav->num_eater_arrays = level->num_yamyam_contents;
3650 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3651 for (y = 0; y < 3; y++)
3652 for (x = 0; x < 3; x++)
3653 cav->eater_array[i][y * 3 + x] =
3654 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3656 cav->amoeba_time = level->amoeba_speed;
3657 cav->wonderwall_time = level->time_magic_wall;
3658 cav->wheel_time = level->time_wheel;
3660 cav->android_move_time = level->android_move_time;
3661 cav->android_clone_time = level->android_clone_time;
3662 cav->ball_random = level->ball_random;
3663 cav->ball_active = level->ball_active_initial;
3664 cav->ball_time = level->ball_time;
3665 cav->num_ball_arrays = level->num_ball_contents;
3667 cav->lenses_score = level->lenses_score;
3668 cav->magnify_score = level->magnify_score;
3669 cav->slurp_score = level->slurp_score;
3671 cav->lenses_time = level->lenses_time;
3672 cav->magnify_time = level->magnify_time;
3674 cav->wind_direction =
3675 map_direction_RND_to_EM(level->wind_direction_initial);
3677 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3678 for (j = 0; j < 8; j++)
3679 cav->ball_array[i][j] =
3680 map_element_RND_to_EM_cave(level->ball_content[i].
3681 e[ball_xy[j][0]][ball_xy[j][1]]);
3683 map_android_clone_elements_RND_to_EM(level);
3685 // first fill the complete playfield with the empty space element
3686 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3687 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3688 cav->cave[x][y] = Cblank;
3690 // then copy the real level contents from level file into the playfield
3691 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3693 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3695 if (level->field[x][y] == EL_AMOEBA_DEAD)
3696 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3698 cav->cave[x][y] = new_element;
3701 for (i = 0; i < MAX_PLAYERS; i++)
3703 cav->player_x[i] = -1;
3704 cav->player_y[i] = -1;
3707 // initialize player positions and delete players from the playfield
3708 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3710 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3712 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3714 cav->player_x[player_nr] = x;
3715 cav->player_y[player_nr] = y;
3717 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3722 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3724 static int ball_xy[8][2] =
3735 struct LevelInfo_EM *level_em = level->native_em_level;
3736 struct CAVE *cav = level_em->cav;
3739 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3740 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3742 level->time = cav->time_seconds;
3743 level->gems_needed = cav->gems_needed;
3745 sprintf(level->name, "Level %d", level->file_info.nr);
3747 level->score[SC_EMERALD] = cav->emerald_score;
3748 level->score[SC_DIAMOND] = cav->diamond_score;
3749 level->score[SC_ROBOT] = cav->alien_score;
3750 level->score[SC_SPACESHIP] = cav->tank_score;
3751 level->score[SC_BUG] = cav->bug_score;
3752 level->score[SC_YAMYAM] = cav->eater_score;
3753 level->score[SC_NUT] = cav->nut_score;
3754 level->score[SC_DYNAMITE] = cav->dynamite_score;
3755 level->score[SC_KEY] = cav->key_score;
3756 level->score[SC_TIME_BONUS] = cav->exit_score;
3758 level->num_yamyam_contents = cav->num_eater_arrays;
3760 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3761 for (y = 0; y < 3; y++)
3762 for (x = 0; x < 3; x++)
3763 level->yamyam_content[i].e[x][y] =
3764 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3766 level->amoeba_speed = cav->amoeba_time;
3767 level->time_magic_wall = cav->wonderwall_time;
3768 level->time_wheel = cav->wheel_time;
3770 level->android_move_time = cav->android_move_time;
3771 level->android_clone_time = cav->android_clone_time;
3772 level->ball_random = cav->ball_random;
3773 level->ball_active_initial = cav->ball_active;
3774 level->ball_time = cav->ball_time;
3775 level->num_ball_contents = cav->num_ball_arrays;
3777 level->lenses_score = cav->lenses_score;
3778 level->magnify_score = cav->magnify_score;
3779 level->slurp_score = cav->slurp_score;
3781 level->lenses_time = cav->lenses_time;
3782 level->magnify_time = cav->magnify_time;
3784 level->wind_direction_initial =
3785 map_direction_EM_to_RND(cav->wind_direction);
3787 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3788 for (j = 0; j < 8; j++)
3789 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3790 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3792 map_android_clone_elements_EM_to_RND(level);
3794 // convert the playfield (some elements need special treatment)
3795 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3797 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3799 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3800 new_element = EL_AMOEBA_DEAD;
3802 level->field[x][y] = new_element;
3805 for (i = 0; i < MAX_PLAYERS; i++)
3807 // in case of all players set to the same field, use the first player
3808 int nr = MAX_PLAYERS - i - 1;
3809 int jx = cav->player_x[nr];
3810 int jy = cav->player_y[nr];
3812 if (jx != -1 && jy != -1)
3813 level->field[jx][jy] = EL_PLAYER_1 + nr;
3816 // time score is counted for each 10 seconds left in Emerald Mine levels
3817 level->time_score_base = 10;
3821 // ----------------------------------------------------------------------------
3822 // functions for loading SP level
3823 // ----------------------------------------------------------------------------
3825 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3827 struct LevelInfo_SP *level_sp = level->native_sp_level;
3828 LevelInfoType *header = &level_sp->header;
3831 level_sp->width = level->fieldx;
3832 level_sp->height = level->fieldy;
3834 for (x = 0; x < level->fieldx; x++)
3835 for (y = 0; y < level->fieldy; y++)
3836 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3838 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3840 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3841 header->LevelTitle[i] = level->name[i];
3842 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3844 header->InfotronsNeeded = level->gems_needed;
3846 header->SpecialPortCount = 0;
3848 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3850 boolean gravity_port_found = FALSE;
3851 boolean gravity_port_valid = FALSE;
3852 int gravity_port_flag;
3853 int gravity_port_base_element;
3854 int element = level->field[x][y];
3856 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3857 element <= EL_SP_GRAVITY_ON_PORT_UP)
3859 gravity_port_found = TRUE;
3860 gravity_port_valid = TRUE;
3861 gravity_port_flag = 1;
3862 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3864 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3865 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3867 gravity_port_found = TRUE;
3868 gravity_port_valid = TRUE;
3869 gravity_port_flag = 0;
3870 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3872 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3873 element <= EL_SP_GRAVITY_PORT_UP)
3875 // change R'n'D style gravity inverting special port to normal port
3876 // (there are no gravity inverting ports in native Supaplex engine)
3878 gravity_port_found = TRUE;
3879 gravity_port_valid = FALSE;
3880 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3883 if (gravity_port_found)
3885 if (gravity_port_valid &&
3886 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3888 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3890 port->PortLocation = (y * level->fieldx + x) * 2;
3891 port->Gravity = gravity_port_flag;
3893 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3895 header->SpecialPortCount++;
3899 // change special gravity port to normal port
3901 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3904 level_sp->playfield[x][y] = element - EL_SP_START;
3909 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3911 struct LevelInfo_SP *level_sp = level->native_sp_level;
3912 LevelInfoType *header = &level_sp->header;
3913 boolean num_invalid_elements = 0;
3916 level->fieldx = level_sp->width;
3917 level->fieldy = level_sp->height;
3919 for (x = 0; x < level->fieldx; x++)
3921 for (y = 0; y < level->fieldy; y++)
3923 int element_old = level_sp->playfield[x][y];
3924 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3926 if (element_new == EL_UNKNOWN)
3928 num_invalid_elements++;
3930 Debug("level:native:SP", "invalid element %d at position %d, %d",
3934 level->field[x][y] = element_new;
3938 if (num_invalid_elements > 0)
3939 Warn("found %d invalid elements%s", num_invalid_elements,
3940 (!options.debug ? " (use '--debug' for more details)" : ""));
3942 for (i = 0; i < MAX_PLAYERS; i++)
3943 level->initial_player_gravity[i] =
3944 (header->InitialGravity == 1 ? TRUE : FALSE);
3946 // skip leading spaces
3947 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3948 if (header->LevelTitle[i] != ' ')
3952 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3953 level->name[j] = header->LevelTitle[i];
3954 level->name[j] = '\0';
3956 // cut trailing spaces
3958 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3959 level->name[j - 1] = '\0';
3961 level->gems_needed = header->InfotronsNeeded;
3963 for (i = 0; i < header->SpecialPortCount; i++)
3965 SpecialPortType *port = &header->SpecialPort[i];
3966 int port_location = port->PortLocation;
3967 int gravity = port->Gravity;
3968 int port_x, port_y, port_element;
3970 port_x = (port_location / 2) % level->fieldx;
3971 port_y = (port_location / 2) / level->fieldx;
3973 if (port_x < 0 || port_x >= level->fieldx ||
3974 port_y < 0 || port_y >= level->fieldy)
3976 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3981 port_element = level->field[port_x][port_y];
3983 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3984 port_element > EL_SP_GRAVITY_PORT_UP)
3986 Warn("no special port at position (%d, %d)", port_x, port_y);
3991 // change previous (wrong) gravity inverting special port to either
3992 // gravity enabling special port or gravity disabling special port
3993 level->field[port_x][port_y] +=
3994 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3995 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3998 // change special gravity ports without database entries to normal ports
3999 for (x = 0; x < level->fieldx; x++)
4000 for (y = 0; y < level->fieldy; y++)
4001 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4002 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4003 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4005 level->time = 0; // no time limit
4006 level->amoeba_speed = 0;
4007 level->time_magic_wall = 0;
4008 level->time_wheel = 0;
4009 level->amoeba_content = EL_EMPTY;
4011 // original Supaplex does not use score values -- rate by playing time
4012 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4013 level->score[i] = 0;
4015 level->rate_time_over_score = TRUE;
4017 // there are no yamyams in supaplex levels
4018 for (i = 0; i < level->num_yamyam_contents; i++)
4019 for (x = 0; x < 3; x++)
4020 for (y = 0; y < 3; y++)
4021 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4024 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4026 struct LevelInfo_SP *level_sp = level->native_sp_level;
4027 struct DemoInfo_SP *demo = &level_sp->demo;
4030 // always start with reliable default values
4031 demo->is_available = FALSE;
4034 if (TAPE_IS_EMPTY(tape))
4037 demo->level_nr = tape.level_nr; // (currently not used)
4039 level_sp->header.DemoRandomSeed = tape.random_seed;
4043 for (i = 0; i < tape.length; i++)
4045 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4046 int demo_repeat = tape.pos[i].delay;
4047 int demo_entries = (demo_repeat + 15) / 16;
4049 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4051 Warn("tape truncated: size exceeds maximum SP demo size %d",
4057 for (j = 0; j < demo_repeat / 16; j++)
4058 demo->data[demo->length++] = 0xf0 | demo_action;
4060 if (demo_repeat % 16)
4061 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4064 demo->is_available = TRUE;
4067 static void setTapeInfoToDefaults(void);
4069 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4071 struct LevelInfo_SP *level_sp = level->native_sp_level;
4072 struct DemoInfo_SP *demo = &level_sp->demo;
4073 char *filename = level->file_info.filename;
4076 // always start with reliable default values
4077 setTapeInfoToDefaults();
4079 if (!demo->is_available)
4082 tape.level_nr = demo->level_nr; // (currently not used)
4083 tape.random_seed = level_sp->header.DemoRandomSeed;
4085 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4088 tape.pos[tape.counter].delay = 0;
4090 for (i = 0; i < demo->length; i++)
4092 int demo_action = demo->data[i] & 0x0f;
4093 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4094 int tape_action = map_key_SP_to_RND(demo_action);
4095 int tape_repeat = demo_repeat + 1;
4096 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4097 boolean success = 0;
4100 for (j = 0; j < tape_repeat; j++)
4101 success = TapeAddAction(action);
4105 Warn("SP demo truncated: size exceeds maximum tape size %d",
4112 TapeHaltRecording();
4116 // ----------------------------------------------------------------------------
4117 // functions for loading MM level
4118 // ----------------------------------------------------------------------------
4120 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4122 struct LevelInfo_MM *level_mm = level->native_mm_level;
4125 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4126 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4128 level_mm->time = level->time;
4129 level_mm->kettles_needed = level->gems_needed;
4130 level_mm->auto_count_kettles = level->auto_count_gems;
4132 level_mm->laser_red = level->mm_laser_red;
4133 level_mm->laser_green = level->mm_laser_green;
4134 level_mm->laser_blue = level->mm_laser_blue;
4136 strcpy(level_mm->name, level->name);
4137 strcpy(level_mm->author, level->author);
4139 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4140 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4141 level_mm->score[SC_KEY] = level->score[SC_KEY];
4142 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4143 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4145 level_mm->amoeba_speed = level->amoeba_speed;
4146 level_mm->time_fuse = level->mm_time_fuse;
4147 level_mm->time_bomb = level->mm_time_bomb;
4148 level_mm->time_ball = level->mm_time_ball;
4149 level_mm->time_block = level->mm_time_block;
4151 for (x = 0; x < level->fieldx; x++)
4152 for (y = 0; y < level->fieldy; y++)
4154 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4157 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4159 struct LevelInfo_MM *level_mm = level->native_mm_level;
4162 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4163 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4165 level->time = level_mm->time;
4166 level->gems_needed = level_mm->kettles_needed;
4167 level->auto_count_gems = level_mm->auto_count_kettles;
4169 level->mm_laser_red = level_mm->laser_red;
4170 level->mm_laser_green = level_mm->laser_green;
4171 level->mm_laser_blue = level_mm->laser_blue;
4173 strcpy(level->name, level_mm->name);
4175 // only overwrite author from 'levelinfo.conf' if author defined in level
4176 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4177 strcpy(level->author, level_mm->author);
4179 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4180 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4181 level->score[SC_KEY] = level_mm->score[SC_KEY];
4182 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4183 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4185 level->amoeba_speed = level_mm->amoeba_speed;
4186 level->mm_time_fuse = level_mm->time_fuse;
4187 level->mm_time_bomb = level_mm->time_bomb;
4188 level->mm_time_ball = level_mm->time_ball;
4189 level->mm_time_block = level_mm->time_block;
4191 for (x = 0; x < level->fieldx; x++)
4192 for (y = 0; y < level->fieldy; y++)
4193 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4197 // ----------------------------------------------------------------------------
4198 // functions for loading DC level
4199 // ----------------------------------------------------------------------------
4201 #define DC_LEVEL_HEADER_SIZE 344
4203 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4206 static int last_data_encoded;
4210 int diff_hi, diff_lo;
4211 int data_hi, data_lo;
4212 unsigned short data_decoded;
4216 last_data_encoded = 0;
4223 diff = data_encoded - last_data_encoded;
4224 diff_hi = diff & ~0xff;
4225 diff_lo = diff & 0xff;
4229 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4230 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4231 data_hi = data_hi & 0xff00;
4233 data_decoded = data_hi | data_lo;
4235 last_data_encoded = data_encoded;
4237 offset1 = (offset1 + 1) % 31;
4238 offset2 = offset2 & 0xff;
4240 return data_decoded;
4243 static int getMappedElement_DC(int element)
4251 // 0x0117 - 0x036e: (?)
4254 // 0x042d - 0x0684: (?)
4270 element = EL_CRYSTAL;
4273 case 0x0e77: // quicksand (boulder)
4274 element = EL_QUICKSAND_FAST_FULL;
4277 case 0x0e99: // slow quicksand (boulder)
4278 element = EL_QUICKSAND_FULL;
4282 element = EL_EM_EXIT_OPEN;
4286 element = EL_EM_EXIT_CLOSED;
4290 element = EL_EM_STEEL_EXIT_OPEN;
4294 element = EL_EM_STEEL_EXIT_CLOSED;
4297 case 0x0f4f: // dynamite (lit 1)
4298 element = EL_EM_DYNAMITE_ACTIVE;
4301 case 0x0f57: // dynamite (lit 2)
4302 element = EL_EM_DYNAMITE_ACTIVE;
4305 case 0x0f5f: // dynamite (lit 3)
4306 element = EL_EM_DYNAMITE_ACTIVE;
4309 case 0x0f67: // dynamite (lit 4)
4310 element = EL_EM_DYNAMITE_ACTIVE;
4317 element = EL_AMOEBA_WET;
4321 element = EL_AMOEBA_DROP;
4325 element = EL_DC_MAGIC_WALL;
4329 element = EL_SPACESHIP_UP;
4333 element = EL_SPACESHIP_DOWN;
4337 element = EL_SPACESHIP_LEFT;
4341 element = EL_SPACESHIP_RIGHT;
4345 element = EL_BUG_UP;
4349 element = EL_BUG_DOWN;
4353 element = EL_BUG_LEFT;
4357 element = EL_BUG_RIGHT;
4361 element = EL_MOLE_UP;
4365 element = EL_MOLE_DOWN;
4369 element = EL_MOLE_LEFT;
4373 element = EL_MOLE_RIGHT;
4381 element = EL_YAMYAM_UP;
4385 element = EL_SWITCHGATE_OPEN;
4389 element = EL_SWITCHGATE_CLOSED;
4393 element = EL_DC_SWITCHGATE_SWITCH_UP;
4397 element = EL_TIMEGATE_CLOSED;
4400 case 0x144c: // conveyor belt switch (green)
4401 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4404 case 0x144f: // conveyor belt switch (red)
4405 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4408 case 0x1452: // conveyor belt switch (blue)
4409 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4413 element = EL_CONVEYOR_BELT_3_MIDDLE;
4417 element = EL_CONVEYOR_BELT_3_LEFT;
4421 element = EL_CONVEYOR_BELT_3_RIGHT;
4425 element = EL_CONVEYOR_BELT_1_MIDDLE;
4429 element = EL_CONVEYOR_BELT_1_LEFT;
4433 element = EL_CONVEYOR_BELT_1_RIGHT;
4437 element = EL_CONVEYOR_BELT_4_MIDDLE;
4441 element = EL_CONVEYOR_BELT_4_LEFT;
4445 element = EL_CONVEYOR_BELT_4_RIGHT;
4449 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4453 element = EL_EXPANDABLE_WALL_VERTICAL;
4457 element = EL_EXPANDABLE_WALL_ANY;
4460 case 0x14ce: // growing steel wall (left/right)
4461 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4464 case 0x14df: // growing steel wall (up/down)
4465 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4468 case 0x14e8: // growing steel wall (up/down/left/right)
4469 element = EL_EXPANDABLE_STEELWALL_ANY;
4473 element = EL_SHIELD_DEADLY;
4477 element = EL_EXTRA_TIME;
4485 element = EL_EMPTY_SPACE;
4488 case 0x1578: // quicksand (empty)
4489 element = EL_QUICKSAND_FAST_EMPTY;
4492 case 0x1579: // slow quicksand (empty)
4493 element = EL_QUICKSAND_EMPTY;
4503 element = EL_EM_DYNAMITE;
4506 case 0x15a1: // key (red)
4507 element = EL_EM_KEY_1;
4510 case 0x15a2: // key (yellow)
4511 element = EL_EM_KEY_2;
4514 case 0x15a3: // key (blue)
4515 element = EL_EM_KEY_4;
4518 case 0x15a4: // key (green)
4519 element = EL_EM_KEY_3;
4522 case 0x15a5: // key (white)
4523 element = EL_DC_KEY_WHITE;
4527 element = EL_WALL_SLIPPERY;
4534 case 0x15a8: // wall (not round)
4538 case 0x15a9: // (blue)
4539 element = EL_CHAR_A;
4542 case 0x15aa: // (blue)
4543 element = EL_CHAR_B;
4546 case 0x15ab: // (blue)
4547 element = EL_CHAR_C;
4550 case 0x15ac: // (blue)
4551 element = EL_CHAR_D;
4554 case 0x15ad: // (blue)
4555 element = EL_CHAR_E;
4558 case 0x15ae: // (blue)
4559 element = EL_CHAR_F;
4562 case 0x15af: // (blue)
4563 element = EL_CHAR_G;
4566 case 0x15b0: // (blue)
4567 element = EL_CHAR_H;
4570 case 0x15b1: // (blue)
4571 element = EL_CHAR_I;
4574 case 0x15b2: // (blue)
4575 element = EL_CHAR_J;
4578 case 0x15b3: // (blue)
4579 element = EL_CHAR_K;
4582 case 0x15b4: // (blue)
4583 element = EL_CHAR_L;
4586 case 0x15b5: // (blue)
4587 element = EL_CHAR_M;
4590 case 0x15b6: // (blue)
4591 element = EL_CHAR_N;
4594 case 0x15b7: // (blue)
4595 element = EL_CHAR_O;
4598 case 0x15b8: // (blue)
4599 element = EL_CHAR_P;
4602 case 0x15b9: // (blue)
4603 element = EL_CHAR_Q;
4606 case 0x15ba: // (blue)
4607 element = EL_CHAR_R;
4610 case 0x15bb: // (blue)
4611 element = EL_CHAR_S;
4614 case 0x15bc: // (blue)
4615 element = EL_CHAR_T;
4618 case 0x15bd: // (blue)
4619 element = EL_CHAR_U;
4622 case 0x15be: // (blue)
4623 element = EL_CHAR_V;
4626 case 0x15bf: // (blue)
4627 element = EL_CHAR_W;
4630 case 0x15c0: // (blue)
4631 element = EL_CHAR_X;
4634 case 0x15c1: // (blue)
4635 element = EL_CHAR_Y;
4638 case 0x15c2: // (blue)
4639 element = EL_CHAR_Z;
4642 case 0x15c3: // (blue)
4643 element = EL_CHAR_AUMLAUT;
4646 case 0x15c4: // (blue)
4647 element = EL_CHAR_OUMLAUT;
4650 case 0x15c5: // (blue)
4651 element = EL_CHAR_UUMLAUT;
4654 case 0x15c6: // (blue)
4655 element = EL_CHAR_0;
4658 case 0x15c7: // (blue)
4659 element = EL_CHAR_1;
4662 case 0x15c8: // (blue)
4663 element = EL_CHAR_2;
4666 case 0x15c9: // (blue)
4667 element = EL_CHAR_3;
4670 case 0x15ca: // (blue)
4671 element = EL_CHAR_4;
4674 case 0x15cb: // (blue)
4675 element = EL_CHAR_5;
4678 case 0x15cc: // (blue)
4679 element = EL_CHAR_6;
4682 case 0x15cd: // (blue)
4683 element = EL_CHAR_7;
4686 case 0x15ce: // (blue)
4687 element = EL_CHAR_8;
4690 case 0x15cf: // (blue)
4691 element = EL_CHAR_9;
4694 case 0x15d0: // (blue)
4695 element = EL_CHAR_PERIOD;
4698 case 0x15d1: // (blue)
4699 element = EL_CHAR_EXCLAM;
4702 case 0x15d2: // (blue)
4703 element = EL_CHAR_COLON;
4706 case 0x15d3: // (blue)
4707 element = EL_CHAR_LESS;
4710 case 0x15d4: // (blue)
4711 element = EL_CHAR_GREATER;
4714 case 0x15d5: // (blue)
4715 element = EL_CHAR_QUESTION;
4718 case 0x15d6: // (blue)
4719 element = EL_CHAR_COPYRIGHT;
4722 case 0x15d7: // (blue)
4723 element = EL_CHAR_UP;
4726 case 0x15d8: // (blue)
4727 element = EL_CHAR_DOWN;
4730 case 0x15d9: // (blue)
4731 element = EL_CHAR_BUTTON;
4734 case 0x15da: // (blue)
4735 element = EL_CHAR_PLUS;
4738 case 0x15db: // (blue)
4739 element = EL_CHAR_MINUS;
4742 case 0x15dc: // (blue)
4743 element = EL_CHAR_APOSTROPHE;
4746 case 0x15dd: // (blue)
4747 element = EL_CHAR_PARENLEFT;
4750 case 0x15de: // (blue)
4751 element = EL_CHAR_PARENRIGHT;
4754 case 0x15df: // (green)
4755 element = EL_CHAR_A;
4758 case 0x15e0: // (green)
4759 element = EL_CHAR_B;
4762 case 0x15e1: // (green)
4763 element = EL_CHAR_C;
4766 case 0x15e2: // (green)
4767 element = EL_CHAR_D;
4770 case 0x15e3: // (green)
4771 element = EL_CHAR_E;
4774 case 0x15e4: // (green)
4775 element = EL_CHAR_F;
4778 case 0x15e5: // (green)
4779 element = EL_CHAR_G;
4782 case 0x15e6: // (green)
4783 element = EL_CHAR_H;
4786 case 0x15e7: // (green)
4787 element = EL_CHAR_I;
4790 case 0x15e8: // (green)
4791 element = EL_CHAR_J;
4794 case 0x15e9: // (green)
4795 element = EL_CHAR_K;
4798 case 0x15ea: // (green)
4799 element = EL_CHAR_L;
4802 case 0x15eb: // (green)
4803 element = EL_CHAR_M;
4806 case 0x15ec: // (green)
4807 element = EL_CHAR_N;
4810 case 0x15ed: // (green)
4811 element = EL_CHAR_O;
4814 case 0x15ee: // (green)
4815 element = EL_CHAR_P;
4818 case 0x15ef: // (green)
4819 element = EL_CHAR_Q;
4822 case 0x15f0: // (green)
4823 element = EL_CHAR_R;
4826 case 0x15f1: // (green)
4827 element = EL_CHAR_S;
4830 case 0x15f2: // (green)
4831 element = EL_CHAR_T;
4834 case 0x15f3: // (green)
4835 element = EL_CHAR_U;
4838 case 0x15f4: // (green)
4839 element = EL_CHAR_V;
4842 case 0x15f5: // (green)
4843 element = EL_CHAR_W;
4846 case 0x15f6: // (green)
4847 element = EL_CHAR_X;
4850 case 0x15f7: // (green)
4851 element = EL_CHAR_Y;
4854 case 0x15f8: // (green)
4855 element = EL_CHAR_Z;
4858 case 0x15f9: // (green)
4859 element = EL_CHAR_AUMLAUT;
4862 case 0x15fa: // (green)
4863 element = EL_CHAR_OUMLAUT;
4866 case 0x15fb: // (green)
4867 element = EL_CHAR_UUMLAUT;
4870 case 0x15fc: // (green)
4871 element = EL_CHAR_0;
4874 case 0x15fd: // (green)
4875 element = EL_CHAR_1;
4878 case 0x15fe: // (green)
4879 element = EL_CHAR_2;
4882 case 0x15ff: // (green)
4883 element = EL_CHAR_3;
4886 case 0x1600: // (green)
4887 element = EL_CHAR_4;
4890 case 0x1601: // (green)
4891 element = EL_CHAR_5;
4894 case 0x1602: // (green)
4895 element = EL_CHAR_6;
4898 case 0x1603: // (green)
4899 element = EL_CHAR_7;
4902 case 0x1604: // (green)
4903 element = EL_CHAR_8;
4906 case 0x1605: // (green)
4907 element = EL_CHAR_9;
4910 case 0x1606: // (green)
4911 element = EL_CHAR_PERIOD;
4914 case 0x1607: // (green)
4915 element = EL_CHAR_EXCLAM;
4918 case 0x1608: // (green)
4919 element = EL_CHAR_COLON;
4922 case 0x1609: // (green)
4923 element = EL_CHAR_LESS;
4926 case 0x160a: // (green)
4927 element = EL_CHAR_GREATER;
4930 case 0x160b: // (green)
4931 element = EL_CHAR_QUESTION;
4934 case 0x160c: // (green)
4935 element = EL_CHAR_COPYRIGHT;
4938 case 0x160d: // (green)
4939 element = EL_CHAR_UP;
4942 case 0x160e: // (green)
4943 element = EL_CHAR_DOWN;
4946 case 0x160f: // (green)
4947 element = EL_CHAR_BUTTON;
4950 case 0x1610: // (green)
4951 element = EL_CHAR_PLUS;
4954 case 0x1611: // (green)
4955 element = EL_CHAR_MINUS;
4958 case 0x1612: // (green)
4959 element = EL_CHAR_APOSTROPHE;
4962 case 0x1613: // (green)
4963 element = EL_CHAR_PARENLEFT;
4966 case 0x1614: // (green)
4967 element = EL_CHAR_PARENRIGHT;
4970 case 0x1615: // (blue steel)
4971 element = EL_STEEL_CHAR_A;
4974 case 0x1616: // (blue steel)
4975 element = EL_STEEL_CHAR_B;
4978 case 0x1617: // (blue steel)
4979 element = EL_STEEL_CHAR_C;
4982 case 0x1618: // (blue steel)
4983 element = EL_STEEL_CHAR_D;
4986 case 0x1619: // (blue steel)
4987 element = EL_STEEL_CHAR_E;
4990 case 0x161a: // (blue steel)
4991 element = EL_STEEL_CHAR_F;
4994 case 0x161b: // (blue steel)
4995 element = EL_STEEL_CHAR_G;
4998 case 0x161c: // (blue steel)
4999 element = EL_STEEL_CHAR_H;
5002 case 0x161d: // (blue steel)
5003 element = EL_STEEL_CHAR_I;
5006 case 0x161e: // (blue steel)
5007 element = EL_STEEL_CHAR_J;
5010 case 0x161f: // (blue steel)
5011 element = EL_STEEL_CHAR_K;
5014 case 0x1620: // (blue steel)
5015 element = EL_STEEL_CHAR_L;
5018 case 0x1621: // (blue steel)
5019 element = EL_STEEL_CHAR_M;
5022 case 0x1622: // (blue steel)
5023 element = EL_STEEL_CHAR_N;
5026 case 0x1623: // (blue steel)
5027 element = EL_STEEL_CHAR_O;
5030 case 0x1624: // (blue steel)
5031 element = EL_STEEL_CHAR_P;
5034 case 0x1625: // (blue steel)
5035 element = EL_STEEL_CHAR_Q;
5038 case 0x1626: // (blue steel)
5039 element = EL_STEEL_CHAR_R;
5042 case 0x1627: // (blue steel)
5043 element = EL_STEEL_CHAR_S;
5046 case 0x1628: // (blue steel)
5047 element = EL_STEEL_CHAR_T;
5050 case 0x1629: // (blue steel)
5051 element = EL_STEEL_CHAR_U;
5054 case 0x162a: // (blue steel)
5055 element = EL_STEEL_CHAR_V;
5058 case 0x162b: // (blue steel)
5059 element = EL_STEEL_CHAR_W;
5062 case 0x162c: // (blue steel)
5063 element = EL_STEEL_CHAR_X;
5066 case 0x162d: // (blue steel)
5067 element = EL_STEEL_CHAR_Y;
5070 case 0x162e: // (blue steel)
5071 element = EL_STEEL_CHAR_Z;
5074 case 0x162f: // (blue steel)
5075 element = EL_STEEL_CHAR_AUMLAUT;
5078 case 0x1630: // (blue steel)
5079 element = EL_STEEL_CHAR_OUMLAUT;
5082 case 0x1631: // (blue steel)
5083 element = EL_STEEL_CHAR_UUMLAUT;
5086 case 0x1632: // (blue steel)
5087 element = EL_STEEL_CHAR_0;
5090 case 0x1633: // (blue steel)
5091 element = EL_STEEL_CHAR_1;
5094 case 0x1634: // (blue steel)
5095 element = EL_STEEL_CHAR_2;
5098 case 0x1635: // (blue steel)
5099 element = EL_STEEL_CHAR_3;
5102 case 0x1636: // (blue steel)
5103 element = EL_STEEL_CHAR_4;
5106 case 0x1637: // (blue steel)
5107 element = EL_STEEL_CHAR_5;
5110 case 0x1638: // (blue steel)
5111 element = EL_STEEL_CHAR_6;
5114 case 0x1639: // (blue steel)
5115 element = EL_STEEL_CHAR_7;
5118 case 0x163a: // (blue steel)
5119 element = EL_STEEL_CHAR_8;
5122 case 0x163b: // (blue steel)
5123 element = EL_STEEL_CHAR_9;
5126 case 0x163c: // (blue steel)
5127 element = EL_STEEL_CHAR_PERIOD;
5130 case 0x163d: // (blue steel)
5131 element = EL_STEEL_CHAR_EXCLAM;
5134 case 0x163e: // (blue steel)
5135 element = EL_STEEL_CHAR_COLON;
5138 case 0x163f: // (blue steel)
5139 element = EL_STEEL_CHAR_LESS;
5142 case 0x1640: // (blue steel)
5143 element = EL_STEEL_CHAR_GREATER;
5146 case 0x1641: // (blue steel)
5147 element = EL_STEEL_CHAR_QUESTION;
5150 case 0x1642: // (blue steel)
5151 element = EL_STEEL_CHAR_COPYRIGHT;
5154 case 0x1643: // (blue steel)
5155 element = EL_STEEL_CHAR_UP;
5158 case 0x1644: // (blue steel)
5159 element = EL_STEEL_CHAR_DOWN;
5162 case 0x1645: // (blue steel)
5163 element = EL_STEEL_CHAR_BUTTON;
5166 case 0x1646: // (blue steel)
5167 element = EL_STEEL_CHAR_PLUS;
5170 case 0x1647: // (blue steel)
5171 element = EL_STEEL_CHAR_MINUS;
5174 case 0x1648: // (blue steel)
5175 element = EL_STEEL_CHAR_APOSTROPHE;
5178 case 0x1649: // (blue steel)
5179 element = EL_STEEL_CHAR_PARENLEFT;
5182 case 0x164a: // (blue steel)
5183 element = EL_STEEL_CHAR_PARENRIGHT;
5186 case 0x164b: // (green steel)
5187 element = EL_STEEL_CHAR_A;
5190 case 0x164c: // (green steel)
5191 element = EL_STEEL_CHAR_B;
5194 case 0x164d: // (green steel)
5195 element = EL_STEEL_CHAR_C;
5198 case 0x164e: // (green steel)
5199 element = EL_STEEL_CHAR_D;
5202 case 0x164f: // (green steel)
5203 element = EL_STEEL_CHAR_E;
5206 case 0x1650: // (green steel)
5207 element = EL_STEEL_CHAR_F;
5210 case 0x1651: // (green steel)
5211 element = EL_STEEL_CHAR_G;
5214 case 0x1652: // (green steel)
5215 element = EL_STEEL_CHAR_H;
5218 case 0x1653: // (green steel)
5219 element = EL_STEEL_CHAR_I;
5222 case 0x1654: // (green steel)
5223 element = EL_STEEL_CHAR_J;
5226 case 0x1655: // (green steel)
5227 element = EL_STEEL_CHAR_K;
5230 case 0x1656: // (green steel)
5231 element = EL_STEEL_CHAR_L;
5234 case 0x1657: // (green steel)
5235 element = EL_STEEL_CHAR_M;
5238 case 0x1658: // (green steel)
5239 element = EL_STEEL_CHAR_N;
5242 case 0x1659: // (green steel)
5243 element = EL_STEEL_CHAR_O;
5246 case 0x165a: // (green steel)
5247 element = EL_STEEL_CHAR_P;
5250 case 0x165b: // (green steel)
5251 element = EL_STEEL_CHAR_Q;
5254 case 0x165c: // (green steel)
5255 element = EL_STEEL_CHAR_R;
5258 case 0x165d: // (green steel)
5259 element = EL_STEEL_CHAR_S;
5262 case 0x165e: // (green steel)
5263 element = EL_STEEL_CHAR_T;
5266 case 0x165f: // (green steel)
5267 element = EL_STEEL_CHAR_U;
5270 case 0x1660: // (green steel)
5271 element = EL_STEEL_CHAR_V;
5274 case 0x1661: // (green steel)
5275 element = EL_STEEL_CHAR_W;
5278 case 0x1662: // (green steel)
5279 element = EL_STEEL_CHAR_X;
5282 case 0x1663: // (green steel)
5283 element = EL_STEEL_CHAR_Y;
5286 case 0x1664: // (green steel)
5287 element = EL_STEEL_CHAR_Z;
5290 case 0x1665: // (green steel)
5291 element = EL_STEEL_CHAR_AUMLAUT;
5294 case 0x1666: // (green steel)
5295 element = EL_STEEL_CHAR_OUMLAUT;
5298 case 0x1667: // (green steel)
5299 element = EL_STEEL_CHAR_UUMLAUT;
5302 case 0x1668: // (green steel)
5303 element = EL_STEEL_CHAR_0;
5306 case 0x1669: // (green steel)
5307 element = EL_STEEL_CHAR_1;
5310 case 0x166a: // (green steel)
5311 element = EL_STEEL_CHAR_2;
5314 case 0x166b: // (green steel)
5315 element = EL_STEEL_CHAR_3;
5318 case 0x166c: // (green steel)
5319 element = EL_STEEL_CHAR_4;
5322 case 0x166d: // (green steel)
5323 element = EL_STEEL_CHAR_5;
5326 case 0x166e: // (green steel)
5327 element = EL_STEEL_CHAR_6;
5330 case 0x166f: // (green steel)
5331 element = EL_STEEL_CHAR_7;
5334 case 0x1670: // (green steel)
5335 element = EL_STEEL_CHAR_8;
5338 case 0x1671: // (green steel)
5339 element = EL_STEEL_CHAR_9;
5342 case 0x1672: // (green steel)
5343 element = EL_STEEL_CHAR_PERIOD;
5346 case 0x1673: // (green steel)
5347 element = EL_STEEL_CHAR_EXCLAM;
5350 case 0x1674: // (green steel)
5351 element = EL_STEEL_CHAR_COLON;
5354 case 0x1675: // (green steel)
5355 element = EL_STEEL_CHAR_LESS;
5358 case 0x1676: // (green steel)
5359 element = EL_STEEL_CHAR_GREATER;
5362 case 0x1677: // (green steel)
5363 element = EL_STEEL_CHAR_QUESTION;
5366 case 0x1678: // (green steel)
5367 element = EL_STEEL_CHAR_COPYRIGHT;
5370 case 0x1679: // (green steel)
5371 element = EL_STEEL_CHAR_UP;
5374 case 0x167a: // (green steel)
5375 element = EL_STEEL_CHAR_DOWN;
5378 case 0x167b: // (green steel)
5379 element = EL_STEEL_CHAR_BUTTON;
5382 case 0x167c: // (green steel)
5383 element = EL_STEEL_CHAR_PLUS;
5386 case 0x167d: // (green steel)
5387 element = EL_STEEL_CHAR_MINUS;
5390 case 0x167e: // (green steel)
5391 element = EL_STEEL_CHAR_APOSTROPHE;
5394 case 0x167f: // (green steel)
5395 element = EL_STEEL_CHAR_PARENLEFT;
5398 case 0x1680: // (green steel)
5399 element = EL_STEEL_CHAR_PARENRIGHT;
5402 case 0x1681: // gate (red)
5403 element = EL_EM_GATE_1;
5406 case 0x1682: // secret gate (red)
5407 element = EL_EM_GATE_1_GRAY;
5410 case 0x1683: // gate (yellow)
5411 element = EL_EM_GATE_2;
5414 case 0x1684: // secret gate (yellow)
5415 element = EL_EM_GATE_2_GRAY;
5418 case 0x1685: // gate (blue)
5419 element = EL_EM_GATE_4;
5422 case 0x1686: // secret gate (blue)
5423 element = EL_EM_GATE_4_GRAY;
5426 case 0x1687: // gate (green)
5427 element = EL_EM_GATE_3;
5430 case 0x1688: // secret gate (green)
5431 element = EL_EM_GATE_3_GRAY;
5434 case 0x1689: // gate (white)
5435 element = EL_DC_GATE_WHITE;
5438 case 0x168a: // secret gate (white)
5439 element = EL_DC_GATE_WHITE_GRAY;
5442 case 0x168b: // secret gate (no key)
5443 element = EL_DC_GATE_FAKE_GRAY;
5447 element = EL_ROBOT_WHEEL;
5451 element = EL_DC_TIMEGATE_SWITCH;
5455 element = EL_ACID_POOL_BOTTOM;
5459 element = EL_ACID_POOL_TOPLEFT;
5463 element = EL_ACID_POOL_TOPRIGHT;
5467 element = EL_ACID_POOL_BOTTOMLEFT;
5471 element = EL_ACID_POOL_BOTTOMRIGHT;
5475 element = EL_STEELWALL;
5479 element = EL_STEELWALL_SLIPPERY;
5482 case 0x1695: // steel wall (not round)
5483 element = EL_STEELWALL;
5486 case 0x1696: // steel wall (left)
5487 element = EL_DC_STEELWALL_1_LEFT;
5490 case 0x1697: // steel wall (bottom)
5491 element = EL_DC_STEELWALL_1_BOTTOM;
5494 case 0x1698: // steel wall (right)
5495 element = EL_DC_STEELWALL_1_RIGHT;
5498 case 0x1699: // steel wall (top)
5499 element = EL_DC_STEELWALL_1_TOP;
5502 case 0x169a: // steel wall (left/bottom)
5503 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5506 case 0x169b: // steel wall (right/bottom)
5507 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5510 case 0x169c: // steel wall (right/top)
5511 element = EL_DC_STEELWALL_1_TOPRIGHT;
5514 case 0x169d: // steel wall (left/top)
5515 element = EL_DC_STEELWALL_1_TOPLEFT;
5518 case 0x169e: // steel wall (right/bottom small)
5519 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5522 case 0x169f: // steel wall (left/bottom small)
5523 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5526 case 0x16a0: // steel wall (right/top small)
5527 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5530 case 0x16a1: // steel wall (left/top small)
5531 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5534 case 0x16a2: // steel wall (left/right)
5535 element = EL_DC_STEELWALL_1_VERTICAL;
5538 case 0x16a3: // steel wall (top/bottom)
5539 element = EL_DC_STEELWALL_1_HORIZONTAL;
5542 case 0x16a4: // steel wall 2 (left end)
5543 element = EL_DC_STEELWALL_2_LEFT;
5546 case 0x16a5: // steel wall 2 (right end)
5547 element = EL_DC_STEELWALL_2_RIGHT;
5550 case 0x16a6: // steel wall 2 (top end)
5551 element = EL_DC_STEELWALL_2_TOP;
5554 case 0x16a7: // steel wall 2 (bottom end)
5555 element = EL_DC_STEELWALL_2_BOTTOM;
5558 case 0x16a8: // steel wall 2 (left/right)
5559 element = EL_DC_STEELWALL_2_HORIZONTAL;
5562 case 0x16a9: // steel wall 2 (up/down)
5563 element = EL_DC_STEELWALL_2_VERTICAL;
5566 case 0x16aa: // steel wall 2 (mid)
5567 element = EL_DC_STEELWALL_2_MIDDLE;
5571 element = EL_SIGN_EXCLAMATION;
5575 element = EL_SIGN_RADIOACTIVITY;
5579 element = EL_SIGN_STOP;
5583 element = EL_SIGN_WHEELCHAIR;
5587 element = EL_SIGN_PARKING;
5591 element = EL_SIGN_NO_ENTRY;
5595 element = EL_SIGN_HEART;
5599 element = EL_SIGN_GIVE_WAY;
5603 element = EL_SIGN_ENTRY_FORBIDDEN;
5607 element = EL_SIGN_EMERGENCY_EXIT;
5611 element = EL_SIGN_YIN_YANG;
5615 element = EL_WALL_EMERALD;
5619 element = EL_WALL_DIAMOND;
5623 element = EL_WALL_PEARL;
5627 element = EL_WALL_CRYSTAL;
5631 element = EL_INVISIBLE_WALL;
5635 element = EL_INVISIBLE_STEELWALL;
5639 // EL_INVISIBLE_SAND
5642 element = EL_LIGHT_SWITCH;
5646 element = EL_ENVELOPE_1;
5650 if (element >= 0x0117 && element <= 0x036e) // (?)
5651 element = EL_DIAMOND;
5652 else if (element >= 0x042d && element <= 0x0684) // (?)
5653 element = EL_EMERALD;
5654 else if (element >= 0x157c && element <= 0x158b)
5656 else if (element >= 0x1590 && element <= 0x159f)
5657 element = EL_DC_LANDMINE;
5658 else if (element >= 0x16bc && element <= 0x16cb)
5659 element = EL_INVISIBLE_SAND;
5662 Warn("unknown Diamond Caves element 0x%04x", element);
5664 element = EL_UNKNOWN;
5669 return getMappedElement(element);
5672 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5675 byte header[DC_LEVEL_HEADER_SIZE];
5677 int envelope_header_pos = 62;
5678 int envelope_content_pos = 94;
5679 int level_name_pos = 251;
5680 int level_author_pos = 292;
5681 int envelope_header_len;
5682 int envelope_content_len;
5684 int level_author_len;
5686 int num_yamyam_contents;
5689 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5691 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5693 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5695 header[i * 2 + 0] = header_word >> 8;
5696 header[i * 2 + 1] = header_word & 0xff;
5699 // read some values from level header to check level decoding integrity
5700 fieldx = header[6] | (header[7] << 8);
5701 fieldy = header[8] | (header[9] << 8);
5702 num_yamyam_contents = header[60] | (header[61] << 8);
5704 // do some simple sanity checks to ensure that level was correctly decoded
5705 if (fieldx < 1 || fieldx > 256 ||
5706 fieldy < 1 || fieldy > 256 ||
5707 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5709 level->no_valid_file = TRUE;
5711 Warn("cannot decode level from stream -- using empty level");
5716 // maximum envelope header size is 31 bytes
5717 envelope_header_len = header[envelope_header_pos];
5718 // maximum envelope content size is 110 (156?) bytes
5719 envelope_content_len = header[envelope_content_pos];
5721 // maximum level title size is 40 bytes
5722 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5723 // maximum level author size is 30 (51?) bytes
5724 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5728 for (i = 0; i < envelope_header_len; i++)
5729 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5730 level->envelope[0].text[envelope_size++] =
5731 header[envelope_header_pos + 1 + i];
5733 if (envelope_header_len > 0 && envelope_content_len > 0)
5735 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5736 level->envelope[0].text[envelope_size++] = '\n';
5737 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5738 level->envelope[0].text[envelope_size++] = '\n';
5741 for (i = 0; i < envelope_content_len; i++)
5742 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5743 level->envelope[0].text[envelope_size++] =
5744 header[envelope_content_pos + 1 + i];
5746 level->envelope[0].text[envelope_size] = '\0';
5748 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5749 level->envelope[0].ysize = 10;
5750 level->envelope[0].autowrap = TRUE;
5751 level->envelope[0].centered = TRUE;
5753 for (i = 0; i < level_name_len; i++)
5754 level->name[i] = header[level_name_pos + 1 + i];
5755 level->name[level_name_len] = '\0';
5757 for (i = 0; i < level_author_len; i++)
5758 level->author[i] = header[level_author_pos + 1 + i];
5759 level->author[level_author_len] = '\0';
5761 num_yamyam_contents = header[60] | (header[61] << 8);
5762 level->num_yamyam_contents =
5763 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5765 for (i = 0; i < num_yamyam_contents; i++)
5767 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5769 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5770 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5772 if (i < MAX_ELEMENT_CONTENTS)
5773 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5777 fieldx = header[6] | (header[7] << 8);
5778 fieldy = header[8] | (header[9] << 8);
5779 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5780 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5782 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5784 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5785 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5787 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5788 level->field[x][y] = getMappedElement_DC(element_dc);
5791 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5792 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5793 level->field[x][y] = EL_PLAYER_1;
5795 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5796 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5797 level->field[x][y] = EL_PLAYER_2;
5799 level->gems_needed = header[18] | (header[19] << 8);
5801 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5802 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5803 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5804 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5805 level->score[SC_NUT] = header[28] | (header[29] << 8);
5806 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5807 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5808 level->score[SC_BUG] = header[34] | (header[35] << 8);
5809 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5810 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5811 level->score[SC_KEY] = header[40] | (header[41] << 8);
5812 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5814 level->time = header[44] | (header[45] << 8);
5816 level->amoeba_speed = header[46] | (header[47] << 8);
5817 level->time_light = header[48] | (header[49] << 8);
5818 level->time_timegate = header[50] | (header[51] << 8);
5819 level->time_wheel = header[52] | (header[53] << 8);
5820 level->time_magic_wall = header[54] | (header[55] << 8);
5821 level->extra_time = header[56] | (header[57] << 8);
5822 level->shield_normal_time = header[58] | (header[59] << 8);
5824 // shield and extra time elements do not have a score
5825 level->score[SC_SHIELD] = 0;
5826 level->extra_time_score = 0;
5828 // set time for normal and deadly shields to the same value
5829 level->shield_deadly_time = level->shield_normal_time;
5831 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5832 // can slip down from flat walls, like normal walls and steel walls
5833 level->em_slippery_gems = TRUE;
5835 // time score is counted for each 10 seconds left in Diamond Caves levels
5836 level->time_score_base = 10;
5839 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5840 struct LevelFileInfo *level_file_info,
5841 boolean level_info_only)
5843 char *filename = level_file_info->filename;
5845 int num_magic_bytes = 8;
5846 char magic_bytes[num_magic_bytes + 1];
5847 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5849 if (!(file = openFile(filename, MODE_READ)))
5851 level->no_valid_file = TRUE;
5853 if (!level_info_only)
5854 Warn("cannot read level '%s' -- using empty level", filename);
5859 // fseek(file, 0x0000, SEEK_SET);
5861 if (level_file_info->packed)
5863 // read "magic bytes" from start of file
5864 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5865 magic_bytes[0] = '\0';
5867 // check "magic bytes" for correct file format
5868 if (!strPrefix(magic_bytes, "DC2"))
5870 level->no_valid_file = TRUE;
5872 Warn("unknown DC level file '%s' -- using empty level", filename);
5877 if (strPrefix(magic_bytes, "DC2Win95") ||
5878 strPrefix(magic_bytes, "DC2Win98"))
5880 int position_first_level = 0x00fa;
5881 int extra_bytes = 4;
5884 // advance file stream to first level inside the level package
5885 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5887 // each block of level data is followed by block of non-level data
5888 num_levels_to_skip *= 2;
5890 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5891 while (num_levels_to_skip >= 0)
5893 // advance file stream to next level inside the level package
5894 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5896 level->no_valid_file = TRUE;
5898 Warn("cannot fseek in file '%s' -- using empty level", filename);
5903 // skip apparently unused extra bytes following each level
5904 ReadUnusedBytesFromFile(file, extra_bytes);
5906 // read size of next level in level package
5907 skip_bytes = getFile32BitLE(file);
5909 num_levels_to_skip--;
5914 level->no_valid_file = TRUE;
5916 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5922 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5928 // ----------------------------------------------------------------------------
5929 // functions for loading SB level
5930 // ----------------------------------------------------------------------------
5932 int getMappedElement_SB(int element_ascii, boolean use_ces)
5940 sb_element_mapping[] =
5942 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5943 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5944 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5945 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5946 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5947 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5948 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5949 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5956 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5957 if (element_ascii == sb_element_mapping[i].ascii)
5958 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5960 return EL_UNDEFINED;
5963 static void SetLevelSettings_SB(struct LevelInfo *level)
5967 level->use_step_counter = TRUE;
5970 level->score[SC_TIME_BONUS] = 0;
5971 level->time_score_base = 1;
5972 level->rate_time_over_score = TRUE;
5975 level->auto_exit_sokoban = TRUE;
5978 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5979 struct LevelFileInfo *level_file_info,
5980 boolean level_info_only)
5982 char *filename = level_file_info->filename;
5983 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5984 char last_comment[MAX_LINE_LEN];
5985 char level_name[MAX_LINE_LEN];
5988 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5989 boolean read_continued_line = FALSE;
5990 boolean reading_playfield = FALSE;
5991 boolean got_valid_playfield_line = FALSE;
5992 boolean invalid_playfield_char = FALSE;
5993 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5994 int file_level_nr = 0;
5996 int x = 0, y = 0; // initialized to make compilers happy
5998 last_comment[0] = '\0';
5999 level_name[0] = '\0';
6001 if (!(file = openFile(filename, MODE_READ)))
6003 level->no_valid_file = TRUE;
6005 if (!level_info_only)
6006 Warn("cannot read level '%s' -- using empty level", filename);
6011 while (!checkEndOfFile(file))
6013 // level successfully read, but next level may follow here
6014 if (!got_valid_playfield_line && reading_playfield)
6016 // read playfield from single level file -- skip remaining file
6017 if (!level_file_info->packed)
6020 if (file_level_nr >= num_levels_to_skip)
6025 last_comment[0] = '\0';
6026 level_name[0] = '\0';
6028 reading_playfield = FALSE;
6031 got_valid_playfield_line = FALSE;
6033 // read next line of input file
6034 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6037 // check if line was completely read and is terminated by line break
6038 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6041 // cut trailing line break (this can be newline and/or carriage return)
6042 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6043 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6046 // copy raw input line for later use (mainly debugging output)
6047 strcpy(line_raw, line);
6049 if (read_continued_line)
6051 // append new line to existing line, if there is enough space
6052 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6053 strcat(previous_line, line_ptr);
6055 strcpy(line, previous_line); // copy storage buffer to line
6057 read_continued_line = FALSE;
6060 // if the last character is '\', continue at next line
6061 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6063 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6064 strcpy(previous_line, line); // copy line to storage buffer
6066 read_continued_line = TRUE;
6072 if (line[0] == '\0')
6075 // extract comment text from comment line
6078 for (line_ptr = line; *line_ptr; line_ptr++)
6079 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6082 strcpy(last_comment, line_ptr);
6087 // extract level title text from line containing level title
6088 if (line[0] == '\'')
6090 strcpy(level_name, &line[1]);
6092 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6093 level_name[strlen(level_name) - 1] = '\0';
6098 // skip lines containing only spaces (or empty lines)
6099 for (line_ptr = line; *line_ptr; line_ptr++)
6100 if (*line_ptr != ' ')
6102 if (*line_ptr == '\0')
6105 // at this point, we have found a line containing part of a playfield
6107 got_valid_playfield_line = TRUE;
6109 if (!reading_playfield)
6111 reading_playfield = TRUE;
6112 invalid_playfield_char = FALSE;
6114 for (x = 0; x < MAX_LEV_FIELDX; x++)
6115 for (y = 0; y < MAX_LEV_FIELDY; y++)
6116 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6121 // start with topmost tile row
6125 // skip playfield line if larger row than allowed
6126 if (y >= MAX_LEV_FIELDY)
6129 // start with leftmost tile column
6132 // read playfield elements from line
6133 for (line_ptr = line; *line_ptr; line_ptr++)
6135 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6137 // stop parsing playfield line if larger column than allowed
6138 if (x >= MAX_LEV_FIELDX)
6141 if (mapped_sb_element == EL_UNDEFINED)
6143 invalid_playfield_char = TRUE;
6148 level->field[x][y] = mapped_sb_element;
6150 // continue with next tile column
6153 level->fieldx = MAX(x, level->fieldx);
6156 if (invalid_playfield_char)
6158 // if first playfield line, treat invalid lines as comment lines
6160 reading_playfield = FALSE;
6165 // continue with next tile row
6173 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6174 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6176 if (!reading_playfield)
6178 level->no_valid_file = TRUE;
6180 Warn("cannot read level '%s' -- using empty level", filename);
6185 if (*level_name != '\0')
6187 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6188 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6190 else if (*last_comment != '\0')
6192 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6193 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6197 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6200 // set all empty fields beyond the border walls to invisible steel wall
6201 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6203 if ((x == 0 || x == level->fieldx - 1 ||
6204 y == 0 || y == level->fieldy - 1) &&
6205 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6206 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6207 level->field, level->fieldx, level->fieldy);
6210 // set special level settings for Sokoban levels
6211 SetLevelSettings_SB(level);
6213 if (load_xsb_to_ces)
6215 // special global settings can now be set in level template
6216 level->use_custom_template = TRUE;
6221 // -------------------------------------------------------------------------
6222 // functions for handling native levels
6223 // -------------------------------------------------------------------------
6225 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6226 struct LevelFileInfo *level_file_info,
6227 boolean level_info_only)
6229 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6230 level->no_valid_file = TRUE;
6233 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6234 struct LevelFileInfo *level_file_info,
6235 boolean level_info_only)
6239 // determine position of requested level inside level package
6240 if (level_file_info->packed)
6241 pos = level_file_info->nr - leveldir_current->first_level;
6243 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6244 level->no_valid_file = TRUE;
6247 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6248 struct LevelFileInfo *level_file_info,
6249 boolean level_info_only)
6251 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6252 level->no_valid_file = TRUE;
6255 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6257 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6258 CopyNativeLevel_RND_to_EM(level);
6259 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6260 CopyNativeLevel_RND_to_SP(level);
6261 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6262 CopyNativeLevel_RND_to_MM(level);
6265 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6267 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6268 CopyNativeLevel_EM_to_RND(level);
6269 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6270 CopyNativeLevel_SP_to_RND(level);
6271 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6272 CopyNativeLevel_MM_to_RND(level);
6275 void SaveNativeLevel(struct LevelInfo *level)
6277 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6279 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6280 char *filename = getLevelFilenameFromBasename(basename);
6282 CopyNativeLevel_RND_to_SP(level);
6283 CopyNativeTape_RND_to_SP(level);
6285 SaveNativeLevel_SP(filename);
6290 // ----------------------------------------------------------------------------
6291 // functions for loading generic level
6292 // ----------------------------------------------------------------------------
6294 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6295 struct LevelFileInfo *level_file_info,
6296 boolean level_info_only)
6298 // always start with reliable default values
6299 setLevelInfoToDefaults(level, level_info_only, TRUE);
6301 switch (level_file_info->type)
6303 case LEVEL_FILE_TYPE_RND:
6304 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6307 case LEVEL_FILE_TYPE_EM:
6308 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6309 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6312 case LEVEL_FILE_TYPE_SP:
6313 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6314 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6317 case LEVEL_FILE_TYPE_MM:
6318 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6319 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6322 case LEVEL_FILE_TYPE_DC:
6323 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6326 case LEVEL_FILE_TYPE_SB:
6327 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6331 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6335 // if level file is invalid, restore level structure to default values
6336 if (level->no_valid_file)
6337 setLevelInfoToDefaults(level, level_info_only, FALSE);
6339 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6340 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6342 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6343 CopyNativeLevel_Native_to_RND(level);
6346 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6348 static struct LevelFileInfo level_file_info;
6350 // always start with reliable default values
6351 setFileInfoToDefaults(&level_file_info);
6353 level_file_info.nr = 0; // unknown level number
6354 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6356 setString(&level_file_info.filename, filename);
6358 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6361 static void LoadLevel_InitVersion(struct LevelInfo *level)
6365 if (leveldir_current == NULL) // only when dumping level
6368 // all engine modifications also valid for levels which use latest engine
6369 if (level->game_version < VERSION_IDENT(3,2,0,5))
6371 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6372 level->time_score_base = 10;
6375 if (leveldir_current->latest_engine)
6377 // ---------- use latest game engine --------------------------------------
6379 /* For all levels which are forced to use the latest game engine version
6380 (normally all but user contributed, private and undefined levels), set
6381 the game engine version to the actual version; this allows for actual
6382 corrections in the game engine to take effect for existing, converted
6383 levels (from "classic" or other existing games) to make the emulation
6384 of the corresponding game more accurate, while (hopefully) not breaking
6385 existing levels created from other players. */
6387 level->game_version = GAME_VERSION_ACTUAL;
6389 /* Set special EM style gems behaviour: EM style gems slip down from
6390 normal, steel and growing wall. As this is a more fundamental change,
6391 it seems better to set the default behaviour to "off" (as it is more
6392 natural) and make it configurable in the level editor (as a property
6393 of gem style elements). Already existing converted levels (neither
6394 private nor contributed levels) are changed to the new behaviour. */
6396 if (level->file_version < FILE_VERSION_2_0)
6397 level->em_slippery_gems = TRUE;
6402 // ---------- use game engine the level was created with --------------------
6404 /* For all levels which are not forced to use the latest game engine
6405 version (normally user contributed, private and undefined levels),
6406 use the version of the game engine the levels were created for.
6408 Since 2.0.1, the game engine version is now directly stored
6409 in the level file (chunk "VERS"), so there is no need anymore
6410 to set the game version from the file version (except for old,
6411 pre-2.0 levels, where the game version is still taken from the
6412 file format version used to store the level -- see above). */
6414 // player was faster than enemies in 1.0.0 and before
6415 if (level->file_version == FILE_VERSION_1_0)
6416 for (i = 0; i < MAX_PLAYERS; i++)
6417 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6419 // default behaviour for EM style gems was "slippery" only in 2.0.1
6420 if (level->game_version == VERSION_IDENT(2,0,1,0))
6421 level->em_slippery_gems = TRUE;
6423 // springs could be pushed over pits before (pre-release version) 2.2.0
6424 if (level->game_version < VERSION_IDENT(2,2,0,0))
6425 level->use_spring_bug = TRUE;
6427 if (level->game_version < VERSION_IDENT(3,2,0,5))
6429 // time orb caused limited time in endless time levels before 3.2.0-5
6430 level->use_time_orb_bug = TRUE;
6432 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6433 level->block_snap_field = FALSE;
6435 // extra time score was same value as time left score before 3.2.0-5
6436 level->extra_time_score = level->score[SC_TIME_BONUS];
6439 if (level->game_version < VERSION_IDENT(3,2,0,7))
6441 // default behaviour for snapping was "not continuous" before 3.2.0-7
6442 level->continuous_snapping = FALSE;
6445 // only few elements were able to actively move into acid before 3.1.0
6446 // trigger settings did not exist before 3.1.0; set to default "any"
6447 if (level->game_version < VERSION_IDENT(3,1,0,0))
6449 // correct "can move into acid" settings (all zero in old levels)
6451 level->can_move_into_acid_bits = 0; // nothing can move into acid
6452 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6454 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6455 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6456 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6457 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6459 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6460 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6462 // correct trigger settings (stored as zero == "none" in old levels)
6464 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6466 int element = EL_CUSTOM_START + i;
6467 struct ElementInfo *ei = &element_info[element];
6469 for (j = 0; j < ei->num_change_pages; j++)
6471 struct ElementChangeInfo *change = &ei->change_page[j];
6473 change->trigger_player = CH_PLAYER_ANY;
6474 change->trigger_page = CH_PAGE_ANY;
6479 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6481 int element = EL_CUSTOM_256;
6482 struct ElementInfo *ei = &element_info[element];
6483 struct ElementChangeInfo *change = &ei->change_page[0];
6485 /* This is needed to fix a problem that was caused by a bugfix in function
6486 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6487 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6488 not replace walkable elements, but instead just placed the player on it,
6489 without placing the Sokoban field under the player). Unfortunately, this
6490 breaks "Snake Bite" style levels when the snake is halfway through a door
6491 that just closes (the snake head is still alive and can be moved in this
6492 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6493 player (without Sokoban element) which then gets killed as designed). */
6495 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6496 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6497 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6498 change->target_element = EL_PLAYER_1;
6501 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6502 if (level->game_version < VERSION_IDENT(3,2,5,0))
6504 /* This is needed to fix a problem that was caused by a bugfix in function
6505 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6506 corrects the behaviour when a custom element changes to another custom
6507 element with a higher element number that has change actions defined.
6508 Normally, only one change per frame is allowed for custom elements.
6509 Therefore, it is checked if a custom element already changed in the
6510 current frame; if it did, subsequent changes are suppressed.
6511 Unfortunately, this is only checked for element changes, but not for
6512 change actions, which are still executed. As the function above loops
6513 through all custom elements from lower to higher, an element change
6514 resulting in a lower CE number won't be checked again, while a target
6515 element with a higher number will also be checked, and potential change
6516 actions will get executed for this CE, too (which is wrong), while
6517 further changes are ignored (which is correct). As this bugfix breaks
6518 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6519 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6520 behaviour for existing levels and tapes that make use of this bug */
6522 level->use_action_after_change_bug = TRUE;
6525 // not centering level after relocating player was default only in 3.2.3
6526 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6527 level->shifted_relocation = TRUE;
6529 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6530 if (level->game_version < VERSION_IDENT(3,2,6,0))
6531 level->em_explodes_by_fire = TRUE;
6533 // levels were solved by the first player entering an exit up to 4.1.0.0
6534 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6535 level->solved_by_one_player = TRUE;
6537 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6538 if (level->game_version < VERSION_IDENT(4,1,1,1))
6539 level->use_life_bugs = TRUE;
6541 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6542 if (level->game_version < VERSION_IDENT(4,1,1,1))
6543 level->sb_objects_needed = FALSE;
6545 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6546 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6547 level->finish_dig_collect = FALSE;
6549 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6550 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6551 level->keep_walkable_ce = TRUE;
6554 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6556 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6559 // check if this level is (not) a Sokoban level
6560 for (y = 0; y < level->fieldy; y++)
6561 for (x = 0; x < level->fieldx; x++)
6562 if (!IS_SB_ELEMENT(Tile[x][y]))
6563 is_sokoban_level = FALSE;
6565 if (is_sokoban_level)
6567 // set special level settings for Sokoban levels
6568 SetLevelSettings_SB(level);
6572 static void LoadLevel_InitSettings(struct LevelInfo *level)
6574 // adjust level settings for (non-native) Sokoban-style levels
6575 LoadLevel_InitSettings_SB(level);
6577 // rename levels with title "nameless level" or if renaming is forced
6578 if (leveldir_current->empty_level_name != NULL &&
6579 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6580 leveldir_current->force_level_name))
6581 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6582 leveldir_current->empty_level_name, level_nr);
6585 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6589 // map elements that have changed in newer versions
6590 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6591 level->game_version);
6592 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6593 for (x = 0; x < 3; x++)
6594 for (y = 0; y < 3; y++)
6595 level->yamyam_content[i].e[x][y] =
6596 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6597 level->game_version);
6601 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6605 // map custom element change events that have changed in newer versions
6606 // (these following values were accidentally changed in version 3.0.1)
6607 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6608 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6610 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6612 int element = EL_CUSTOM_START + i;
6614 // order of checking and copying events to be mapped is important
6615 // (do not change the start and end value -- they are constant)
6616 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6618 if (HAS_CHANGE_EVENT(element, j - 2))
6620 SET_CHANGE_EVENT(element, j - 2, FALSE);
6621 SET_CHANGE_EVENT(element, j, TRUE);
6625 // order of checking and copying events to be mapped is important
6626 // (do not change the start and end value -- they are constant)
6627 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6629 if (HAS_CHANGE_EVENT(element, j - 1))
6631 SET_CHANGE_EVENT(element, j - 1, FALSE);
6632 SET_CHANGE_EVENT(element, j, TRUE);
6638 // initialize "can_change" field for old levels with only one change page
6639 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6641 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6643 int element = EL_CUSTOM_START + i;
6645 if (CAN_CHANGE(element))
6646 element_info[element].change->can_change = TRUE;
6650 // correct custom element values (for old levels without these options)
6651 if (level->game_version < VERSION_IDENT(3,1,1,0))
6653 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6655 int element = EL_CUSTOM_START + i;
6656 struct ElementInfo *ei = &element_info[element];
6658 if (ei->access_direction == MV_NO_DIRECTION)
6659 ei->access_direction = MV_ALL_DIRECTIONS;
6663 // correct custom element values (fix invalid values for all versions)
6666 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6668 int element = EL_CUSTOM_START + i;
6669 struct ElementInfo *ei = &element_info[element];
6671 for (j = 0; j < ei->num_change_pages; j++)
6673 struct ElementChangeInfo *change = &ei->change_page[j];
6675 if (change->trigger_player == CH_PLAYER_NONE)
6676 change->trigger_player = CH_PLAYER_ANY;
6678 if (change->trigger_side == CH_SIDE_NONE)
6679 change->trigger_side = CH_SIDE_ANY;
6684 // initialize "can_explode" field for old levels which did not store this
6685 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6686 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6688 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6690 int element = EL_CUSTOM_START + i;
6692 if (EXPLODES_1X1_OLD(element))
6693 element_info[element].explosion_type = EXPLODES_1X1;
6695 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6696 EXPLODES_SMASHED(element) ||
6697 EXPLODES_IMPACT(element)));
6701 // correct previously hard-coded move delay values for maze runner style
6702 if (level->game_version < VERSION_IDENT(3,1,1,0))
6704 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6706 int element = EL_CUSTOM_START + i;
6708 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6710 // previously hard-coded and therefore ignored
6711 element_info[element].move_delay_fixed = 9;
6712 element_info[element].move_delay_random = 0;
6717 // set some other uninitialized values of custom elements in older levels
6718 if (level->game_version < VERSION_IDENT(3,1,0,0))
6720 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6722 int element = EL_CUSTOM_START + i;
6724 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6726 element_info[element].explosion_delay = 17;
6727 element_info[element].ignition_delay = 8;
6731 // set mouse click change events to work for left/middle/right mouse button
6732 if (level->game_version < VERSION_IDENT(4,2,3,0))
6734 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6736 int element = EL_CUSTOM_START + i;
6737 struct ElementInfo *ei = &element_info[element];
6739 for (j = 0; j < ei->num_change_pages; j++)
6741 struct ElementChangeInfo *change = &ei->change_page[j];
6743 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6744 change->has_event[CE_PRESSED_BY_MOUSE] ||
6745 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6746 change->has_event[CE_MOUSE_PRESSED_ON_X])
6747 change->trigger_side = CH_SIDE_ANY;
6753 static void LoadLevel_InitElements(struct LevelInfo *level)
6755 LoadLevel_InitStandardElements(level);
6757 if (level->file_has_custom_elements)
6758 LoadLevel_InitCustomElements(level);
6760 // initialize element properties for level editor etc.
6761 InitElementPropertiesEngine(level->game_version);
6762 InitElementPropertiesGfxElement();
6765 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6769 // map elements that have changed in newer versions
6770 for (y = 0; y < level->fieldy; y++)
6771 for (x = 0; x < level->fieldx; x++)
6772 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6773 level->game_version);
6775 // clear unused playfield data (nicer if level gets resized in editor)
6776 for (x = 0; x < MAX_LEV_FIELDX; x++)
6777 for (y = 0; y < MAX_LEV_FIELDY; y++)
6778 if (x >= level->fieldx || y >= level->fieldy)
6779 level->field[x][y] = EL_EMPTY;
6781 // copy elements to runtime playfield array
6782 for (x = 0; x < MAX_LEV_FIELDX; x++)
6783 for (y = 0; y < MAX_LEV_FIELDY; y++)
6784 Tile[x][y] = level->field[x][y];
6786 // initialize level size variables for faster access
6787 lev_fieldx = level->fieldx;
6788 lev_fieldy = level->fieldy;
6790 // determine border element for this level
6791 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6792 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6797 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6799 struct LevelFileInfo *level_file_info = &level->file_info;
6801 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6802 CopyNativeLevel_RND_to_Native(level);
6805 static void LoadLevelTemplate_LoadAndInit(void)
6807 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6809 LoadLevel_InitVersion(&level_template);
6810 LoadLevel_InitElements(&level_template);
6811 LoadLevel_InitSettings(&level_template);
6813 ActivateLevelTemplate();
6816 void LoadLevelTemplate(int nr)
6818 if (!fileExists(getGlobalLevelTemplateFilename()))
6820 Warn("no level template found for this level");
6825 setLevelFileInfo(&level_template.file_info, nr);
6827 LoadLevelTemplate_LoadAndInit();
6830 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6832 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6834 LoadLevelTemplate_LoadAndInit();
6837 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6839 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6841 if (level.use_custom_template)
6843 if (network_level != NULL)
6844 LoadNetworkLevelTemplate(network_level);
6846 LoadLevelTemplate(-1);
6849 LoadLevel_InitVersion(&level);
6850 LoadLevel_InitElements(&level);
6851 LoadLevel_InitPlayfield(&level);
6852 LoadLevel_InitSettings(&level);
6854 LoadLevel_InitNativeEngines(&level);
6857 void LoadLevel(int nr)
6859 SetLevelSetInfo(leveldir_current->identifier, nr);
6861 setLevelFileInfo(&level.file_info, nr);
6863 LoadLevel_LoadAndInit(NULL);
6866 void LoadLevelInfoOnly(int nr)
6868 setLevelFileInfo(&level.file_info, nr);
6870 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6873 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6875 SetLevelSetInfo(network_level->leveldir_identifier,
6876 network_level->file_info.nr);
6878 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6880 LoadLevel_LoadAndInit(network_level);
6883 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6887 chunk_size += putFileVersion(file, level->file_version);
6888 chunk_size += putFileVersion(file, level->game_version);
6893 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6897 chunk_size += putFile16BitBE(file, level->creation_date.year);
6898 chunk_size += putFile8Bit(file, level->creation_date.month);
6899 chunk_size += putFile8Bit(file, level->creation_date.day);
6904 #if ENABLE_HISTORIC_CHUNKS
6905 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6909 putFile8Bit(file, level->fieldx);
6910 putFile8Bit(file, level->fieldy);
6912 putFile16BitBE(file, level->time);
6913 putFile16BitBE(file, level->gems_needed);
6915 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6916 putFile8Bit(file, level->name[i]);
6918 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6919 putFile8Bit(file, level->score[i]);
6921 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6922 for (y = 0; y < 3; y++)
6923 for (x = 0; x < 3; x++)
6924 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6925 level->yamyam_content[i].e[x][y]));
6926 putFile8Bit(file, level->amoeba_speed);
6927 putFile8Bit(file, level->time_magic_wall);
6928 putFile8Bit(file, level->time_wheel);
6929 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6930 level->amoeba_content));
6931 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6932 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6933 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6934 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6936 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6938 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6939 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6940 putFile32BitBE(file, level->can_move_into_acid_bits);
6941 putFile8Bit(file, level->dont_collide_with_bits);
6943 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6944 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6946 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6947 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6948 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6950 putFile8Bit(file, level->game_engine_type);
6952 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6956 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6961 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6962 chunk_size += putFile8Bit(file, level->name[i]);
6967 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6972 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6973 chunk_size += putFile8Bit(file, level->author[i]);
6978 #if ENABLE_HISTORIC_CHUNKS
6979 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6984 for (y = 0; y < level->fieldy; y++)
6985 for (x = 0; x < level->fieldx; x++)
6986 if (level->encoding_16bit_field)
6987 chunk_size += putFile16BitBE(file, level->field[x][y]);
6989 chunk_size += putFile8Bit(file, level->field[x][y]);
6995 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7000 for (y = 0; y < level->fieldy; y++)
7001 for (x = 0; x < level->fieldx; x++)
7002 chunk_size += putFile16BitBE(file, level->field[x][y]);
7007 #if ENABLE_HISTORIC_CHUNKS
7008 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7012 putFile8Bit(file, EL_YAMYAM);
7013 putFile8Bit(file, level->num_yamyam_contents);
7014 putFile8Bit(file, 0);
7015 putFile8Bit(file, 0);
7017 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7018 for (y = 0; y < 3; y++)
7019 for (x = 0; x < 3; x++)
7020 if (level->encoding_16bit_field)
7021 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7023 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7027 #if ENABLE_HISTORIC_CHUNKS
7028 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7031 int num_contents, content_xsize, content_ysize;
7032 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7034 if (element == EL_YAMYAM)
7036 num_contents = level->num_yamyam_contents;
7040 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7041 for (y = 0; y < 3; y++)
7042 for (x = 0; x < 3; x++)
7043 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7045 else if (element == EL_BD_AMOEBA)
7051 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7052 for (y = 0; y < 3; y++)
7053 for (x = 0; x < 3; x++)
7054 content_array[i][x][y] = EL_EMPTY;
7055 content_array[0][0][0] = level->amoeba_content;
7059 // chunk header already written -- write empty chunk data
7060 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7062 Warn("cannot save content for element '%d'", element);
7067 putFile16BitBE(file, element);
7068 putFile8Bit(file, num_contents);
7069 putFile8Bit(file, content_xsize);
7070 putFile8Bit(file, content_ysize);
7072 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7074 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7075 for (y = 0; y < 3; y++)
7076 for (x = 0; x < 3; x++)
7077 putFile16BitBE(file, content_array[i][x][y]);
7081 #if ENABLE_HISTORIC_CHUNKS
7082 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7084 int envelope_nr = element - EL_ENVELOPE_1;
7085 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7089 chunk_size += putFile16BitBE(file, element);
7090 chunk_size += putFile16BitBE(file, envelope_len);
7091 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7092 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7094 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7095 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7097 for (i = 0; i < envelope_len; i++)
7098 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7104 #if ENABLE_HISTORIC_CHUNKS
7105 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7106 int num_changed_custom_elements)
7110 putFile16BitBE(file, num_changed_custom_elements);
7112 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7114 int element = EL_CUSTOM_START + i;
7116 struct ElementInfo *ei = &element_info[element];
7118 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7120 if (check < num_changed_custom_elements)
7122 putFile16BitBE(file, element);
7123 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7130 if (check != num_changed_custom_elements) // should not happen
7131 Warn("inconsistent number of custom element properties");
7135 #if ENABLE_HISTORIC_CHUNKS
7136 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7137 int num_changed_custom_elements)
7141 putFile16BitBE(file, num_changed_custom_elements);
7143 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7145 int element = EL_CUSTOM_START + i;
7147 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7149 if (check < num_changed_custom_elements)
7151 putFile16BitBE(file, element);
7152 putFile16BitBE(file, element_info[element].change->target_element);
7159 if (check != num_changed_custom_elements) // should not happen
7160 Warn("inconsistent number of custom target elements");
7164 #if ENABLE_HISTORIC_CHUNKS
7165 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7166 int num_changed_custom_elements)
7168 int i, j, x, y, check = 0;
7170 putFile16BitBE(file, num_changed_custom_elements);
7172 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7174 int element = EL_CUSTOM_START + i;
7175 struct ElementInfo *ei = &element_info[element];
7177 if (ei->modified_settings)
7179 if (check < num_changed_custom_elements)
7181 putFile16BitBE(file, element);
7183 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7184 putFile8Bit(file, ei->description[j]);
7186 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7188 // some free bytes for future properties and padding
7189 WriteUnusedBytesToFile(file, 7);
7191 putFile8Bit(file, ei->use_gfx_element);
7192 putFile16BitBE(file, ei->gfx_element_initial);
7194 putFile8Bit(file, ei->collect_score_initial);
7195 putFile8Bit(file, ei->collect_count_initial);
7197 putFile16BitBE(file, ei->push_delay_fixed);
7198 putFile16BitBE(file, ei->push_delay_random);
7199 putFile16BitBE(file, ei->move_delay_fixed);
7200 putFile16BitBE(file, ei->move_delay_random);
7202 putFile16BitBE(file, ei->move_pattern);
7203 putFile8Bit(file, ei->move_direction_initial);
7204 putFile8Bit(file, ei->move_stepsize);
7206 for (y = 0; y < 3; y++)
7207 for (x = 0; x < 3; x++)
7208 putFile16BitBE(file, ei->content.e[x][y]);
7210 putFile32BitBE(file, ei->change->events);
7212 putFile16BitBE(file, ei->change->target_element);
7214 putFile16BitBE(file, ei->change->delay_fixed);
7215 putFile16BitBE(file, ei->change->delay_random);
7216 putFile16BitBE(file, ei->change->delay_frames);
7218 putFile16BitBE(file, ei->change->initial_trigger_element);
7220 putFile8Bit(file, ei->change->explode);
7221 putFile8Bit(file, ei->change->use_target_content);
7222 putFile8Bit(file, ei->change->only_if_complete);
7223 putFile8Bit(file, ei->change->use_random_replace);
7225 putFile8Bit(file, ei->change->random_percentage);
7226 putFile8Bit(file, ei->change->replace_when);
7228 for (y = 0; y < 3; y++)
7229 for (x = 0; x < 3; x++)
7230 putFile16BitBE(file, ei->change->content.e[x][y]);
7232 putFile8Bit(file, ei->slippery_type);
7234 // some free bytes for future properties and padding
7235 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7242 if (check != num_changed_custom_elements) // should not happen
7243 Warn("inconsistent number of custom element properties");
7247 #if ENABLE_HISTORIC_CHUNKS
7248 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7250 struct ElementInfo *ei = &element_info[element];
7253 // ---------- custom element base property values (96 bytes) ----------------
7255 putFile16BitBE(file, element);
7257 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7258 putFile8Bit(file, ei->description[i]);
7260 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7262 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7264 putFile8Bit(file, ei->num_change_pages);
7266 putFile16BitBE(file, ei->ce_value_fixed_initial);
7267 putFile16BitBE(file, ei->ce_value_random_initial);
7268 putFile8Bit(file, ei->use_last_ce_value);
7270 putFile8Bit(file, ei->use_gfx_element);
7271 putFile16BitBE(file, ei->gfx_element_initial);
7273 putFile8Bit(file, ei->collect_score_initial);
7274 putFile8Bit(file, ei->collect_count_initial);
7276 putFile8Bit(file, ei->drop_delay_fixed);
7277 putFile8Bit(file, ei->push_delay_fixed);
7278 putFile8Bit(file, ei->drop_delay_random);
7279 putFile8Bit(file, ei->push_delay_random);
7280 putFile16BitBE(file, ei->move_delay_fixed);
7281 putFile16BitBE(file, ei->move_delay_random);
7283 // bits 0 - 15 of "move_pattern" ...
7284 putFile16BitBE(file, ei->move_pattern & 0xffff);
7285 putFile8Bit(file, ei->move_direction_initial);
7286 putFile8Bit(file, ei->move_stepsize);
7288 putFile8Bit(file, ei->slippery_type);
7290 for (y = 0; y < 3; y++)
7291 for (x = 0; x < 3; x++)
7292 putFile16BitBE(file, ei->content.e[x][y]);
7294 putFile16BitBE(file, ei->move_enter_element);
7295 putFile16BitBE(file, ei->move_leave_element);
7296 putFile8Bit(file, ei->move_leave_type);
7298 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7299 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7301 putFile8Bit(file, ei->access_direction);
7303 putFile8Bit(file, ei->explosion_delay);
7304 putFile8Bit(file, ei->ignition_delay);
7305 putFile8Bit(file, ei->explosion_type);
7307 // some free bytes for future custom property values and padding
7308 WriteUnusedBytesToFile(file, 1);
7310 // ---------- change page property values (48 bytes) ------------------------
7312 for (i = 0; i < ei->num_change_pages; i++)
7314 struct ElementChangeInfo *change = &ei->change_page[i];
7315 unsigned int event_bits;
7317 // bits 0 - 31 of "has_event[]" ...
7319 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7320 if (change->has_event[j])
7321 event_bits |= (1 << j);
7322 putFile32BitBE(file, event_bits);
7324 putFile16BitBE(file, change->target_element);
7326 putFile16BitBE(file, change->delay_fixed);
7327 putFile16BitBE(file, change->delay_random);
7328 putFile16BitBE(file, change->delay_frames);
7330 putFile16BitBE(file, change->initial_trigger_element);
7332 putFile8Bit(file, change->explode);
7333 putFile8Bit(file, change->use_target_content);
7334 putFile8Bit(file, change->only_if_complete);
7335 putFile8Bit(file, change->use_random_replace);
7337 putFile8Bit(file, change->random_percentage);
7338 putFile8Bit(file, change->replace_when);
7340 for (y = 0; y < 3; y++)
7341 for (x = 0; x < 3; x++)
7342 putFile16BitBE(file, change->target_content.e[x][y]);
7344 putFile8Bit(file, change->can_change);
7346 putFile8Bit(file, change->trigger_side);
7348 putFile8Bit(file, change->trigger_player);
7349 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7350 log_2(change->trigger_page)));
7352 putFile8Bit(file, change->has_action);
7353 putFile8Bit(file, change->action_type);
7354 putFile8Bit(file, change->action_mode);
7355 putFile16BitBE(file, change->action_arg);
7357 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7359 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7360 if (change->has_event[j])
7361 event_bits |= (1 << (j - 32));
7362 putFile8Bit(file, event_bits);
7367 #if ENABLE_HISTORIC_CHUNKS
7368 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7370 struct ElementInfo *ei = &element_info[element];
7371 struct ElementGroupInfo *group = ei->group;
7374 putFile16BitBE(file, element);
7376 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7377 putFile8Bit(file, ei->description[i]);
7379 putFile8Bit(file, group->num_elements);
7381 putFile8Bit(file, ei->use_gfx_element);
7382 putFile16BitBE(file, ei->gfx_element_initial);
7384 putFile8Bit(file, group->choice_mode);
7386 // some free bytes for future values and padding
7387 WriteUnusedBytesToFile(file, 3);
7389 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7390 putFile16BitBE(file, group->element[i]);
7394 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7395 boolean write_element)
7397 int save_type = entry->save_type;
7398 int data_type = entry->data_type;
7399 int conf_type = entry->conf_type;
7400 int byte_mask = conf_type & CONF_MASK_BYTES;
7401 int element = entry->element;
7402 int default_value = entry->default_value;
7404 boolean modified = FALSE;
7406 if (byte_mask != CONF_MASK_MULTI_BYTES)
7408 void *value_ptr = entry->value;
7409 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7412 // check if any settings have been modified before saving them
7413 if (value != default_value)
7416 // do not save if explicitly told or if unmodified default settings
7417 if ((save_type == SAVE_CONF_NEVER) ||
7418 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7422 num_bytes += putFile16BitBE(file, element);
7424 num_bytes += putFile8Bit(file, conf_type);
7425 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7426 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7427 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7430 else if (data_type == TYPE_STRING)
7432 char *default_string = entry->default_string;
7433 char *string = (char *)(entry->value);
7434 int string_length = strlen(string);
7437 // check if any settings have been modified before saving them
7438 if (!strEqual(string, default_string))
7441 // do not save if explicitly told or if unmodified default settings
7442 if ((save_type == SAVE_CONF_NEVER) ||
7443 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7447 num_bytes += putFile16BitBE(file, element);
7449 num_bytes += putFile8Bit(file, conf_type);
7450 num_bytes += putFile16BitBE(file, string_length);
7452 for (i = 0; i < string_length; i++)
7453 num_bytes += putFile8Bit(file, string[i]);
7455 else if (data_type == TYPE_ELEMENT_LIST)
7457 int *element_array = (int *)(entry->value);
7458 int num_elements = *(int *)(entry->num_entities);
7461 // check if any settings have been modified before saving them
7462 for (i = 0; i < num_elements; i++)
7463 if (element_array[i] != default_value)
7466 // do not save if explicitly told or if unmodified default settings
7467 if ((save_type == SAVE_CONF_NEVER) ||
7468 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7472 num_bytes += putFile16BitBE(file, element);
7474 num_bytes += putFile8Bit(file, conf_type);
7475 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7477 for (i = 0; i < num_elements; i++)
7478 num_bytes += putFile16BitBE(file, element_array[i]);
7480 else if (data_type == TYPE_CONTENT_LIST)
7482 struct Content *content = (struct Content *)(entry->value);
7483 int num_contents = *(int *)(entry->num_entities);
7486 // check if any settings have been modified before saving them
7487 for (i = 0; i < num_contents; i++)
7488 for (y = 0; y < 3; y++)
7489 for (x = 0; x < 3; x++)
7490 if (content[i].e[x][y] != default_value)
7493 // do not save if explicitly told or if unmodified default settings
7494 if ((save_type == SAVE_CONF_NEVER) ||
7495 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7499 num_bytes += putFile16BitBE(file, element);
7501 num_bytes += putFile8Bit(file, conf_type);
7502 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7504 for (i = 0; i < num_contents; i++)
7505 for (y = 0; y < 3; y++)
7506 for (x = 0; x < 3; x++)
7507 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7513 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7518 li = *level; // copy level data into temporary buffer
7520 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7521 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7526 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7531 li = *level; // copy level data into temporary buffer
7533 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7534 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7539 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7541 int envelope_nr = element - EL_ENVELOPE_1;
7545 chunk_size += putFile16BitBE(file, element);
7547 // copy envelope data into temporary buffer
7548 xx_envelope = level->envelope[envelope_nr];
7550 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7551 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7556 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7558 struct ElementInfo *ei = &element_info[element];
7562 chunk_size += putFile16BitBE(file, element);
7564 xx_ei = *ei; // copy element data into temporary buffer
7566 // set default description string for this specific element
7567 strcpy(xx_default_description, getDefaultElementDescription(ei));
7569 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7570 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7572 for (i = 0; i < ei->num_change_pages; i++)
7574 struct ElementChangeInfo *change = &ei->change_page[i];
7576 xx_current_change_page = i;
7578 xx_change = *change; // copy change data into temporary buffer
7581 setEventBitsFromEventFlags(change);
7583 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7584 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7591 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7593 struct ElementInfo *ei = &element_info[element];
7594 struct ElementGroupInfo *group = ei->group;
7598 chunk_size += putFile16BitBE(file, element);
7600 xx_ei = *ei; // copy element data into temporary buffer
7601 xx_group = *group; // copy group data into temporary buffer
7603 // set default description string for this specific element
7604 strcpy(xx_default_description, getDefaultElementDescription(ei));
7606 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7607 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7612 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7614 struct ElementInfo *ei = &element_info[element];
7618 chunk_size += putFile16BitBE(file, element);
7620 xx_ei = *ei; // copy element data into temporary buffer
7622 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7623 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7628 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7629 boolean save_as_template)
7635 if (!(file = fopen(filename, MODE_WRITE)))
7637 Warn("cannot save level file '%s'", filename);
7642 level->file_version = FILE_VERSION_ACTUAL;
7643 level->game_version = GAME_VERSION_ACTUAL;
7645 level->creation_date = getCurrentDate();
7647 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7648 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7650 chunk_size = SaveLevel_VERS(NULL, level);
7651 putFileChunkBE(file, "VERS", chunk_size);
7652 SaveLevel_VERS(file, level);
7654 chunk_size = SaveLevel_DATE(NULL, level);
7655 putFileChunkBE(file, "DATE", chunk_size);
7656 SaveLevel_DATE(file, level);
7658 chunk_size = SaveLevel_NAME(NULL, level);
7659 putFileChunkBE(file, "NAME", chunk_size);
7660 SaveLevel_NAME(file, level);
7662 chunk_size = SaveLevel_AUTH(NULL, level);
7663 putFileChunkBE(file, "AUTH", chunk_size);
7664 SaveLevel_AUTH(file, level);
7666 chunk_size = SaveLevel_INFO(NULL, level);
7667 putFileChunkBE(file, "INFO", chunk_size);
7668 SaveLevel_INFO(file, level);
7670 chunk_size = SaveLevel_BODY(NULL, level);
7671 putFileChunkBE(file, "BODY", chunk_size);
7672 SaveLevel_BODY(file, level);
7674 chunk_size = SaveLevel_ELEM(NULL, level);
7675 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7677 putFileChunkBE(file, "ELEM", chunk_size);
7678 SaveLevel_ELEM(file, level);
7681 for (i = 0; i < NUM_ENVELOPES; i++)
7683 int element = EL_ENVELOPE_1 + i;
7685 chunk_size = SaveLevel_NOTE(NULL, level, element);
7686 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7688 putFileChunkBE(file, "NOTE", chunk_size);
7689 SaveLevel_NOTE(file, level, element);
7693 // if not using template level, check for non-default custom/group elements
7694 if (!level->use_custom_template || save_as_template)
7696 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7698 int element = EL_CUSTOM_START + i;
7700 chunk_size = SaveLevel_CUSX(NULL, level, element);
7701 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7703 putFileChunkBE(file, "CUSX", chunk_size);
7704 SaveLevel_CUSX(file, level, element);
7708 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7710 int element = EL_GROUP_START + i;
7712 chunk_size = SaveLevel_GRPX(NULL, level, element);
7713 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7715 putFileChunkBE(file, "GRPX", chunk_size);
7716 SaveLevel_GRPX(file, level, element);
7720 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7722 int element = GET_EMPTY_ELEMENT(i);
7724 chunk_size = SaveLevel_EMPX(NULL, level, element);
7725 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7727 putFileChunkBE(file, "EMPX", chunk_size);
7728 SaveLevel_EMPX(file, level, element);
7735 SetFilePermissions(filename, PERMS_PRIVATE);
7738 void SaveLevel(int nr)
7740 char *filename = getDefaultLevelFilename(nr);
7742 SaveLevelFromFilename(&level, filename, FALSE);
7745 void SaveLevelTemplate(void)
7747 char *filename = getLocalLevelTemplateFilename();
7749 SaveLevelFromFilename(&level, filename, TRUE);
7752 boolean SaveLevelChecked(int nr)
7754 char *filename = getDefaultLevelFilename(nr);
7755 boolean new_level = !fileExists(filename);
7756 boolean level_saved = FALSE;
7758 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7763 Request("Level saved!", REQ_CONFIRM);
7771 void DumpLevel(struct LevelInfo *level)
7773 if (level->no_level_file || level->no_valid_file)
7775 Warn("cannot dump -- no valid level file found");
7781 Print("Level xxx (file version %08d, game version %08d)\n",
7782 level->file_version, level->game_version);
7785 Print("Level author: '%s'\n", level->author);
7786 Print("Level title: '%s'\n", level->name);
7788 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7790 Print("Level time: %d seconds\n", level->time);
7791 Print("Gems needed: %d\n", level->gems_needed);
7793 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7794 Print("Time for wheel: %d seconds\n", level->time_wheel);
7795 Print("Time for light: %d seconds\n", level->time_light);
7796 Print("Time for timegate: %d seconds\n", level->time_timegate);
7798 Print("Amoeba speed: %d\n", level->amoeba_speed);
7801 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7802 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7803 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7804 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7805 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7806 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7812 for (i = 0; i < NUM_ENVELOPES; i++)
7814 char *text = level->envelope[i].text;
7815 int text_len = strlen(text);
7816 boolean has_text = FALSE;
7818 for (j = 0; j < text_len; j++)
7819 if (text[j] != ' ' && text[j] != '\n')
7825 Print("Envelope %d:\n'%s'\n", i + 1, text);
7833 void DumpLevels(void)
7835 static LevelDirTree *dumplevel_leveldir = NULL;
7837 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7838 global.dumplevel_leveldir);
7840 if (dumplevel_leveldir == NULL)
7841 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7843 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7844 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7845 Fail("no such level number: %d", global.dumplevel_level_nr);
7847 leveldir_current = dumplevel_leveldir;
7849 LoadLevel(global.dumplevel_level_nr);
7856 // ============================================================================
7857 // tape file functions
7858 // ============================================================================
7860 static void setTapeInfoToDefaults(void)
7864 // always start with reliable default values (empty tape)
7867 // default values (also for pre-1.2 tapes) with only the first player
7868 tape.player_participates[0] = TRUE;
7869 for (i = 1; i < MAX_PLAYERS; i++)
7870 tape.player_participates[i] = FALSE;
7872 // at least one (default: the first) player participates in every tape
7873 tape.num_participating_players = 1;
7875 tape.property_bits = TAPE_PROPERTY_NONE;
7877 tape.level_nr = level_nr;
7879 tape.changed = FALSE;
7881 tape.recording = FALSE;
7882 tape.playing = FALSE;
7883 tape.pausing = FALSE;
7885 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7886 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7888 tape.no_info_chunk = TRUE;
7889 tape.no_valid_file = FALSE;
7892 static int getTapePosSize(struct TapeInfo *tape)
7894 int tape_pos_size = 0;
7896 if (tape->use_key_actions)
7897 tape_pos_size += tape->num_participating_players;
7899 if (tape->use_mouse_actions)
7900 tape_pos_size += 3; // x and y position and mouse button mask
7902 tape_pos_size += 1; // tape action delay value
7904 return tape_pos_size;
7907 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7909 tape->use_key_actions = FALSE;
7910 tape->use_mouse_actions = FALSE;
7912 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7913 tape->use_key_actions = TRUE;
7915 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7916 tape->use_mouse_actions = TRUE;
7919 static int getTapeActionValue(struct TapeInfo *tape)
7921 return (tape->use_key_actions &&
7922 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7923 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7924 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7925 TAPE_ACTIONS_DEFAULT);
7928 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7930 tape->file_version = getFileVersion(file);
7931 tape->game_version = getFileVersion(file);
7936 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7940 tape->random_seed = getFile32BitBE(file);
7941 tape->date = getFile32BitBE(file);
7942 tape->length = getFile32BitBE(file);
7944 // read header fields that are new since version 1.2
7945 if (tape->file_version >= FILE_VERSION_1_2)
7947 byte store_participating_players = getFile8Bit(file);
7950 // since version 1.2, tapes store which players participate in the tape
7951 tape->num_participating_players = 0;
7952 for (i = 0; i < MAX_PLAYERS; i++)
7954 tape->player_participates[i] = FALSE;
7956 if (store_participating_players & (1 << i))
7958 tape->player_participates[i] = TRUE;
7959 tape->num_participating_players++;
7963 setTapeActionFlags(tape, getFile8Bit(file));
7965 tape->property_bits = getFile8Bit(file);
7967 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7969 engine_version = getFileVersion(file);
7970 if (engine_version > 0)
7971 tape->engine_version = engine_version;
7973 tape->engine_version = tape->game_version;
7979 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7981 tape->scr_fieldx = getFile8Bit(file);
7982 tape->scr_fieldy = getFile8Bit(file);
7987 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7989 char *level_identifier = NULL;
7990 int level_identifier_size;
7993 tape->no_info_chunk = FALSE;
7995 level_identifier_size = getFile16BitBE(file);
7997 level_identifier = checked_malloc(level_identifier_size);
7999 for (i = 0; i < level_identifier_size; i++)
8000 level_identifier[i] = getFile8Bit(file);
8002 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8003 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8005 checked_free(level_identifier);
8007 tape->level_nr = getFile16BitBE(file);
8009 chunk_size = 2 + level_identifier_size + 2;
8014 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8017 int tape_pos_size = getTapePosSize(tape);
8018 int chunk_size_expected = tape_pos_size * tape->length;
8020 if (chunk_size_expected != chunk_size)
8022 ReadUnusedBytesFromFile(file, chunk_size);
8023 return chunk_size_expected;
8026 for (i = 0; i < tape->length; i++)
8028 if (i >= MAX_TAPE_LEN)
8030 Warn("tape truncated -- size exceeds maximum tape size %d",
8033 // tape too large; read and ignore remaining tape data from this chunk
8034 for (;i < tape->length; i++)
8035 ReadUnusedBytesFromFile(file, tape_pos_size);
8040 if (tape->use_key_actions)
8042 for (j = 0; j < MAX_PLAYERS; j++)
8044 tape->pos[i].action[j] = MV_NONE;
8046 if (tape->player_participates[j])
8047 tape->pos[i].action[j] = getFile8Bit(file);
8051 if (tape->use_mouse_actions)
8053 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8054 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8055 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8058 tape->pos[i].delay = getFile8Bit(file);
8060 if (tape->file_version == FILE_VERSION_1_0)
8062 // eliminate possible diagonal moves in old tapes
8063 // this is only for backward compatibility
8065 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8066 byte action = tape->pos[i].action[0];
8067 int k, num_moves = 0;
8069 for (k = 0; k<4; k++)
8071 if (action & joy_dir[k])
8073 tape->pos[i + num_moves].action[0] = joy_dir[k];
8075 tape->pos[i + num_moves].delay = 0;
8084 tape->length += num_moves;
8087 else if (tape->file_version < FILE_VERSION_2_0)
8089 // convert pre-2.0 tapes to new tape format
8091 if (tape->pos[i].delay > 1)
8094 tape->pos[i + 1] = tape->pos[i];
8095 tape->pos[i + 1].delay = 1;
8098 for (j = 0; j < MAX_PLAYERS; j++)
8099 tape->pos[i].action[j] = MV_NONE;
8100 tape->pos[i].delay--;
8107 if (checkEndOfFile(file))
8111 if (i != tape->length)
8112 chunk_size = tape_pos_size * i;
8117 static void LoadTape_SokobanSolution(char *filename)
8120 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8122 if (!(file = openFile(filename, MODE_READ)))
8124 tape.no_valid_file = TRUE;
8129 while (!checkEndOfFile(file))
8131 unsigned char c = getByteFromFile(file);
8133 if (checkEndOfFile(file))
8140 tape.pos[tape.length].action[0] = MV_UP;
8141 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8147 tape.pos[tape.length].action[0] = MV_DOWN;
8148 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8154 tape.pos[tape.length].action[0] = MV_LEFT;
8155 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8161 tape.pos[tape.length].action[0] = MV_RIGHT;
8162 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8170 // ignore white-space characters
8174 tape.no_valid_file = TRUE;
8176 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8184 if (tape.no_valid_file)
8187 tape.length_frames = GetTapeLengthFrames();
8188 tape.length_seconds = GetTapeLengthSeconds();
8191 void LoadTapeFromFilename(char *filename)
8193 char cookie[MAX_LINE_LEN];
8194 char chunk_name[CHUNK_ID_LEN + 1];
8198 // always start with reliable default values
8199 setTapeInfoToDefaults();
8201 if (strSuffix(filename, ".sln"))
8203 LoadTape_SokobanSolution(filename);
8208 if (!(file = openFile(filename, MODE_READ)))
8210 tape.no_valid_file = TRUE;
8215 getFileChunkBE(file, chunk_name, NULL);
8216 if (strEqual(chunk_name, "RND1"))
8218 getFile32BitBE(file); // not used
8220 getFileChunkBE(file, chunk_name, NULL);
8221 if (!strEqual(chunk_name, "TAPE"))
8223 tape.no_valid_file = TRUE;
8225 Warn("unknown format of tape file '%s'", filename);
8232 else // check for pre-2.0 file format with cookie string
8234 strcpy(cookie, chunk_name);
8235 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8237 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8238 cookie[strlen(cookie) - 1] = '\0';
8240 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8242 tape.no_valid_file = TRUE;
8244 Warn("unknown format of tape file '%s'", filename);
8251 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8253 tape.no_valid_file = TRUE;
8255 Warn("unsupported version of tape file '%s'", filename);
8262 // pre-2.0 tape files have no game version, so use file version here
8263 tape.game_version = tape.file_version;
8266 if (tape.file_version < FILE_VERSION_1_2)
8268 // tape files from versions before 1.2.0 without chunk structure
8269 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8270 LoadTape_BODY(file, 2 * tape.length, &tape);
8278 int (*loader)(File *, int, struct TapeInfo *);
8282 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8283 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8284 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8285 { "INFO", -1, LoadTape_INFO },
8286 { "BODY", -1, LoadTape_BODY },
8290 while (getFileChunkBE(file, chunk_name, &chunk_size))
8294 while (chunk_info[i].name != NULL &&
8295 !strEqual(chunk_name, chunk_info[i].name))
8298 if (chunk_info[i].name == NULL)
8300 Warn("unknown chunk '%s' in tape file '%s'",
8301 chunk_name, filename);
8303 ReadUnusedBytesFromFile(file, chunk_size);
8305 else if (chunk_info[i].size != -1 &&
8306 chunk_info[i].size != chunk_size)
8308 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8309 chunk_size, chunk_name, filename);
8311 ReadUnusedBytesFromFile(file, chunk_size);
8315 // call function to load this tape chunk
8316 int chunk_size_expected =
8317 (chunk_info[i].loader)(file, chunk_size, &tape);
8319 // the size of some chunks cannot be checked before reading other
8320 // chunks first (like "HEAD" and "BODY") that contain some header
8321 // information, so check them here
8322 if (chunk_size_expected != chunk_size)
8324 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8325 chunk_size, chunk_name, filename);
8333 tape.length_frames = GetTapeLengthFrames();
8334 tape.length_seconds = GetTapeLengthSeconds();
8337 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8339 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8341 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8342 tape.engine_version);
8346 void LoadTape(int nr)
8348 char *filename = getTapeFilename(nr);
8350 LoadTapeFromFilename(filename);
8353 void LoadSolutionTape(int nr)
8355 char *filename = getSolutionTapeFilename(nr);
8357 LoadTapeFromFilename(filename);
8359 if (TAPE_IS_EMPTY(tape) &&
8360 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8361 level.native_sp_level->demo.is_available)
8362 CopyNativeTape_SP_to_RND(&level);
8365 void LoadScoreTape(char *score_tape_basename, int nr)
8367 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8369 LoadTapeFromFilename(filename);
8372 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8374 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8376 LoadTapeFromFilename(filename);
8379 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8381 // chunk required for team mode tapes with non-default screen size
8382 return (tape->num_participating_players > 1 &&
8383 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8384 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8387 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8389 putFileVersion(file, tape->file_version);
8390 putFileVersion(file, tape->game_version);
8393 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8396 byte store_participating_players = 0;
8398 // set bits for participating players for compact storage
8399 for (i = 0; i < MAX_PLAYERS; i++)
8400 if (tape->player_participates[i])
8401 store_participating_players |= (1 << i);
8403 putFile32BitBE(file, tape->random_seed);
8404 putFile32BitBE(file, tape->date);
8405 putFile32BitBE(file, tape->length);
8407 putFile8Bit(file, store_participating_players);
8409 putFile8Bit(file, getTapeActionValue(tape));
8411 putFile8Bit(file, tape->property_bits);
8413 // unused bytes not at the end here for 4-byte alignment of engine_version
8414 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8416 putFileVersion(file, tape->engine_version);
8419 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8421 putFile8Bit(file, tape->scr_fieldx);
8422 putFile8Bit(file, tape->scr_fieldy);
8425 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8427 int level_identifier_size = strlen(tape->level_identifier) + 1;
8430 putFile16BitBE(file, level_identifier_size);
8432 for (i = 0; i < level_identifier_size; i++)
8433 putFile8Bit(file, tape->level_identifier[i]);
8435 putFile16BitBE(file, tape->level_nr);
8438 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8442 for (i = 0; i < tape->length; i++)
8444 if (tape->use_key_actions)
8446 for (j = 0; j < MAX_PLAYERS; j++)
8447 if (tape->player_participates[j])
8448 putFile8Bit(file, tape->pos[i].action[j]);
8451 if (tape->use_mouse_actions)
8453 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8454 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8455 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8458 putFile8Bit(file, tape->pos[i].delay);
8462 void SaveTapeToFilename(char *filename)
8466 int info_chunk_size;
8467 int body_chunk_size;
8469 if (!(file = fopen(filename, MODE_WRITE)))
8471 Warn("cannot save level recording file '%s'", filename);
8476 tape_pos_size = getTapePosSize(&tape);
8478 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8479 body_chunk_size = tape_pos_size * tape.length;
8481 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8482 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8484 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8485 SaveTape_VERS(file, &tape);
8487 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8488 SaveTape_HEAD(file, &tape);
8490 if (checkSaveTape_SCRN(&tape))
8492 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8493 SaveTape_SCRN(file, &tape);
8496 putFileChunkBE(file, "INFO", info_chunk_size);
8497 SaveTape_INFO(file, &tape);
8499 putFileChunkBE(file, "BODY", body_chunk_size);
8500 SaveTape_BODY(file, &tape);
8504 SetFilePermissions(filename, PERMS_PRIVATE);
8507 static void SaveTapeExt(char *filename)
8511 tape.file_version = FILE_VERSION_ACTUAL;
8512 tape.game_version = GAME_VERSION_ACTUAL;
8514 tape.num_participating_players = 0;
8516 // count number of participating players
8517 for (i = 0; i < MAX_PLAYERS; i++)
8518 if (tape.player_participates[i])
8519 tape.num_participating_players++;
8521 SaveTapeToFilename(filename);
8523 tape.changed = FALSE;
8526 void SaveTape(int nr)
8528 char *filename = getTapeFilename(nr);
8530 InitTapeDirectory(leveldir_current->subdir);
8532 SaveTapeExt(filename);
8535 void SaveScoreTape(int nr)
8537 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8539 // used instead of "leveldir_current->subdir" (for network games)
8540 InitScoreTapeDirectory(levelset.identifier, nr);
8542 SaveTapeExt(filename);
8545 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8546 unsigned int req_state_added)
8548 char *filename = getTapeFilename(nr);
8549 boolean new_tape = !fileExists(filename);
8550 boolean tape_saved = FALSE;
8552 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8557 Request(msg_saved, REQ_CONFIRM | req_state_added);
8565 boolean SaveTapeChecked(int nr)
8567 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8570 boolean SaveTapeChecked_LevelSolved(int nr)
8572 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8573 "Level solved! Tape saved!", REQ_STAY_OPEN);
8576 void DumpTape(struct TapeInfo *tape)
8578 int tape_frame_counter;
8581 if (tape->no_valid_file)
8583 Warn("cannot dump -- no valid tape file found");
8590 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8591 tape->level_nr, tape->file_version, tape->game_version);
8592 Print(" (effective engine version %08d)\n",
8593 tape->engine_version);
8594 Print("Level series identifier: '%s'\n", tape->level_identifier);
8596 Print("Special tape properties: ");
8597 if (tape->property_bits == TAPE_PROPERTY_NONE)
8599 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8600 Print("[em_random_bug]");
8601 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8602 Print("[game_speed]");
8603 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8605 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8606 Print("[single_step]");
8607 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8608 Print("[snapshot]");
8609 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8610 Print("[replayed]");
8611 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8612 Print("[tas_keys]");
8613 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8614 Print("[small_graphics]");
8617 int year2 = tape->date / 10000;
8618 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8619 int month_index_raw = (tape->date / 100) % 100;
8620 int month_index = month_index_raw % 12; // prevent invalid index
8621 int month = month_index + 1;
8622 int day = tape->date % 100;
8624 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8628 tape_frame_counter = 0;
8630 for (i = 0; i < tape->length; i++)
8632 if (i >= MAX_TAPE_LEN)
8637 for (j = 0; j < MAX_PLAYERS; j++)
8639 if (tape->player_participates[j])
8641 int action = tape->pos[i].action[j];
8643 Print("%d:%02x ", j, action);
8644 Print("[%c%c%c%c|%c%c] - ",
8645 (action & JOY_LEFT ? '<' : ' '),
8646 (action & JOY_RIGHT ? '>' : ' '),
8647 (action & JOY_UP ? '^' : ' '),
8648 (action & JOY_DOWN ? 'v' : ' '),
8649 (action & JOY_BUTTON_1 ? '1' : ' '),
8650 (action & JOY_BUTTON_2 ? '2' : ' '));
8654 Print("(%03d) ", tape->pos[i].delay);
8655 Print("[%05d]\n", tape_frame_counter);
8657 tape_frame_counter += tape->pos[i].delay;
8663 void DumpTapes(void)
8665 static LevelDirTree *dumptape_leveldir = NULL;
8667 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8668 global.dumptape_leveldir);
8670 if (dumptape_leveldir == NULL)
8671 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8673 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8674 global.dumptape_level_nr > dumptape_leveldir->last_level)
8675 Fail("no such level number: %d", global.dumptape_level_nr);
8677 leveldir_current = dumptape_leveldir;
8679 if (options.mytapes)
8680 LoadTape(global.dumptape_level_nr);
8682 LoadSolutionTape(global.dumptape_level_nr);
8690 // ============================================================================
8691 // score file functions
8692 // ============================================================================
8694 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8698 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8700 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8701 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8702 scores->entry[i].score = 0;
8703 scores->entry[i].time = 0;
8705 scores->entry[i].id = -1;
8706 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8707 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8708 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8709 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8710 strcpy(scores->entry[i].country_code, "??");
8713 scores->num_entries = 0;
8714 scores->last_added = -1;
8715 scores->last_added_local = -1;
8717 scores->updated = FALSE;
8718 scores->uploaded = FALSE;
8719 scores->tape_downloaded = FALSE;
8720 scores->force_last_added = FALSE;
8722 // The following values are intentionally not reset here:
8726 // - continue_playing
8727 // - continue_on_return
8730 static void setScoreInfoToDefaults(void)
8732 setScoreInfoToDefaultsExt(&scores);
8735 static void setServerScoreInfoToDefaults(void)
8737 setScoreInfoToDefaultsExt(&server_scores);
8740 static void LoadScore_OLD(int nr)
8743 char *filename = getScoreFilename(nr);
8744 char cookie[MAX_LINE_LEN];
8745 char line[MAX_LINE_LEN];
8749 if (!(file = fopen(filename, MODE_READ)))
8752 // check file identifier
8753 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8755 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8756 cookie[strlen(cookie) - 1] = '\0';
8758 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8760 Warn("unknown format of score file '%s'", filename);
8767 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8769 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8770 Warn("fscanf() failed; %s", strerror(errno));
8772 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8775 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8776 line[strlen(line) - 1] = '\0';
8778 for (line_ptr = line; *line_ptr; line_ptr++)
8780 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8782 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8783 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8792 static void ConvertScore_OLD(void)
8794 // only convert score to time for levels that rate playing time over score
8795 if (!level.rate_time_over_score)
8798 // convert old score to playing time for score-less levels (like Supaplex)
8799 int time_final_max = 999;
8802 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8804 int score = scores.entry[i].score;
8806 if (score > 0 && score < time_final_max)
8807 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8811 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8813 scores->file_version = getFileVersion(file);
8814 scores->game_version = getFileVersion(file);
8819 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8821 char *level_identifier = NULL;
8822 int level_identifier_size;
8825 level_identifier_size = getFile16BitBE(file);
8827 level_identifier = checked_malloc(level_identifier_size);
8829 for (i = 0; i < level_identifier_size; i++)
8830 level_identifier[i] = getFile8Bit(file);
8832 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8833 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8835 checked_free(level_identifier);
8837 scores->level_nr = getFile16BitBE(file);
8838 scores->num_entries = getFile16BitBE(file);
8840 chunk_size = 2 + level_identifier_size + 2 + 2;
8845 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8849 for (i = 0; i < scores->num_entries; i++)
8851 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8852 scores->entry[i].name[j] = getFile8Bit(file);
8854 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8857 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8862 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8866 for (i = 0; i < scores->num_entries; i++)
8867 scores->entry[i].score = getFile16BitBE(file);
8869 chunk_size = scores->num_entries * 2;
8874 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8878 for (i = 0; i < scores->num_entries; i++)
8879 scores->entry[i].score = getFile32BitBE(file);
8881 chunk_size = scores->num_entries * 4;
8886 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8890 for (i = 0; i < scores->num_entries; i++)
8891 scores->entry[i].time = getFile32BitBE(file);
8893 chunk_size = scores->num_entries * 4;
8898 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8902 for (i = 0; i < scores->num_entries; i++)
8904 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8905 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8907 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8910 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8915 void LoadScore(int nr)
8917 char *filename = getScoreFilename(nr);
8918 char cookie[MAX_LINE_LEN];
8919 char chunk_name[CHUNK_ID_LEN + 1];
8921 boolean old_score_file_format = FALSE;
8924 // always start with reliable default values
8925 setScoreInfoToDefaults();
8927 if (!(file = openFile(filename, MODE_READ)))
8930 getFileChunkBE(file, chunk_name, NULL);
8931 if (strEqual(chunk_name, "RND1"))
8933 getFile32BitBE(file); // not used
8935 getFileChunkBE(file, chunk_name, NULL);
8936 if (!strEqual(chunk_name, "SCOR"))
8938 Warn("unknown format of score file '%s'", filename);
8945 else // check for old file format with cookie string
8947 strcpy(cookie, chunk_name);
8948 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8950 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8951 cookie[strlen(cookie) - 1] = '\0';
8953 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8955 Warn("unknown format of score file '%s'", filename);
8962 old_score_file_format = TRUE;
8965 if (old_score_file_format)
8967 // score files from versions before 4.2.4.0 without chunk structure
8970 // convert score to time, if possible (mainly for Supaplex levels)
8979 int (*loader)(File *, int, struct ScoreInfo *);
8983 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8984 { "INFO", -1, LoadScore_INFO },
8985 { "NAME", -1, LoadScore_NAME },
8986 { "SCOR", -1, LoadScore_SCOR },
8987 { "SC4R", -1, LoadScore_SC4R },
8988 { "TIME", -1, LoadScore_TIME },
8989 { "TAPE", -1, LoadScore_TAPE },
8994 while (getFileChunkBE(file, chunk_name, &chunk_size))
8998 while (chunk_info[i].name != NULL &&
8999 !strEqual(chunk_name, chunk_info[i].name))
9002 if (chunk_info[i].name == NULL)
9004 Warn("unknown chunk '%s' in score file '%s'",
9005 chunk_name, filename);
9007 ReadUnusedBytesFromFile(file, chunk_size);
9009 else if (chunk_info[i].size != -1 &&
9010 chunk_info[i].size != chunk_size)
9012 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9013 chunk_size, chunk_name, filename);
9015 ReadUnusedBytesFromFile(file, chunk_size);
9019 // call function to load this score chunk
9020 int chunk_size_expected =
9021 (chunk_info[i].loader)(file, chunk_size, &scores);
9023 // the size of some chunks cannot be checked before reading other
9024 // chunks first (like "HEAD" and "BODY") that contain some header
9025 // information, so check them here
9026 if (chunk_size_expected != chunk_size)
9028 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9029 chunk_size, chunk_name, filename);
9038 #if ENABLE_HISTORIC_CHUNKS
9039 void SaveScore_OLD(int nr)
9042 char *filename = getScoreFilename(nr);
9045 // used instead of "leveldir_current->subdir" (for network games)
9046 InitScoreDirectory(levelset.identifier);
9048 if (!(file = fopen(filename, MODE_WRITE)))
9050 Warn("cannot save score for level %d", nr);
9055 fprintf(file, "%s\n\n", SCORE_COOKIE);
9057 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9058 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9062 SetFilePermissions(filename, PERMS_PRIVATE);
9066 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9068 putFileVersion(file, scores->file_version);
9069 putFileVersion(file, scores->game_version);
9072 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9074 int level_identifier_size = strlen(scores->level_identifier) + 1;
9077 putFile16BitBE(file, level_identifier_size);
9079 for (i = 0; i < level_identifier_size; i++)
9080 putFile8Bit(file, scores->level_identifier[i]);
9082 putFile16BitBE(file, scores->level_nr);
9083 putFile16BitBE(file, scores->num_entries);
9086 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9090 for (i = 0; i < scores->num_entries; i++)
9092 int name_size = strlen(scores->entry[i].name);
9094 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9095 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9099 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9103 for (i = 0; i < scores->num_entries; i++)
9104 putFile16BitBE(file, scores->entry[i].score);
9107 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9111 for (i = 0; i < scores->num_entries; i++)
9112 putFile32BitBE(file, scores->entry[i].score);
9115 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9119 for (i = 0; i < scores->num_entries; i++)
9120 putFile32BitBE(file, scores->entry[i].time);
9123 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9127 for (i = 0; i < scores->num_entries; i++)
9129 int size = strlen(scores->entry[i].tape_basename);
9131 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9132 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9136 static void SaveScoreToFilename(char *filename)
9139 int info_chunk_size;
9140 int name_chunk_size;
9141 int scor_chunk_size;
9142 int sc4r_chunk_size;
9143 int time_chunk_size;
9144 int tape_chunk_size;
9145 boolean has_large_score_values;
9148 if (!(file = fopen(filename, MODE_WRITE)))
9150 Warn("cannot save score file '%s'", filename);
9155 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9156 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9157 scor_chunk_size = scores.num_entries * 2;
9158 sc4r_chunk_size = scores.num_entries * 4;
9159 time_chunk_size = scores.num_entries * 4;
9160 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9162 has_large_score_values = FALSE;
9163 for (i = 0; i < scores.num_entries; i++)
9164 if (scores.entry[i].score > 0xffff)
9165 has_large_score_values = TRUE;
9167 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9168 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9170 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9171 SaveScore_VERS(file, &scores);
9173 putFileChunkBE(file, "INFO", info_chunk_size);
9174 SaveScore_INFO(file, &scores);
9176 putFileChunkBE(file, "NAME", name_chunk_size);
9177 SaveScore_NAME(file, &scores);
9179 if (has_large_score_values)
9181 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9182 SaveScore_SC4R(file, &scores);
9186 putFileChunkBE(file, "SCOR", scor_chunk_size);
9187 SaveScore_SCOR(file, &scores);
9190 putFileChunkBE(file, "TIME", time_chunk_size);
9191 SaveScore_TIME(file, &scores);
9193 putFileChunkBE(file, "TAPE", tape_chunk_size);
9194 SaveScore_TAPE(file, &scores);
9198 SetFilePermissions(filename, PERMS_PRIVATE);
9201 void SaveScore(int nr)
9203 char *filename = getScoreFilename(nr);
9206 // used instead of "leveldir_current->subdir" (for network games)
9207 InitScoreDirectory(levelset.identifier);
9209 scores.file_version = FILE_VERSION_ACTUAL;
9210 scores.game_version = GAME_VERSION_ACTUAL;
9212 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9213 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9214 scores.level_nr = level_nr;
9216 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9217 if (scores.entry[i].score == 0 &&
9218 scores.entry[i].time == 0 &&
9219 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9222 scores.num_entries = i;
9224 if (scores.num_entries == 0)
9227 SaveScoreToFilename(filename);
9230 static void LoadServerScoreFromCache(int nr)
9232 struct ScoreEntry score_entry;
9241 { &score_entry.score, FALSE, 0 },
9242 { &score_entry.time, FALSE, 0 },
9243 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9244 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9245 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9246 { &score_entry.id, FALSE, 0 },
9247 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9248 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9249 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9250 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9254 char *filename = getScoreCacheFilename(nr);
9255 SetupFileHash *score_hash = loadSetupFileHash(filename);
9258 server_scores.num_entries = 0;
9260 if (score_hash == NULL)
9263 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9265 score_entry = server_scores.entry[i];
9267 for (j = 0; score_mapping[j].value != NULL; j++)
9271 sprintf(token, "%02d.%d", i, j);
9273 char *value = getHashEntry(score_hash, token);
9278 if (score_mapping[j].is_string)
9280 char *score_value = (char *)score_mapping[j].value;
9281 int value_size = score_mapping[j].string_size;
9283 strncpy(score_value, value, value_size);
9284 score_value[value_size] = '\0';
9288 int *score_value = (int *)score_mapping[j].value;
9290 *score_value = atoi(value);
9293 server_scores.num_entries = i + 1;
9296 server_scores.entry[i] = score_entry;
9299 freeSetupFileHash(score_hash);
9302 void LoadServerScore(int nr, boolean download_score)
9304 if (!setup.use_api_server)
9307 // always start with reliable default values
9308 setServerScoreInfoToDefaults();
9310 // 1st step: load server scores from cache file (which may not exist)
9311 // (this should prevent reading it while the thread is writing to it)
9312 LoadServerScoreFromCache(nr);
9314 if (download_score && runtime.use_api_server)
9316 // 2nd step: download server scores from score server to cache file
9317 // (as thread, as it might time out if the server is not reachable)
9318 ApiGetScoreAsThread(nr);
9322 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9324 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9326 // if score tape not uploaded, ask for uploading missing tapes later
9327 if (!setup.has_remaining_tapes)
9328 setup.ask_for_remaining_tapes = TRUE;
9330 setup.provide_uploading_tapes = TRUE;
9331 setup.has_remaining_tapes = TRUE;
9333 SaveSetup_ServerSetup();
9336 void SaveServerScore(int nr, boolean tape_saved)
9338 if (!runtime.use_api_server)
9340 PrepareScoreTapesForUpload(leveldir_current->subdir);
9345 ApiAddScoreAsThread(nr, tape_saved, NULL);
9348 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9349 char *score_tape_filename)
9351 if (!runtime.use_api_server)
9354 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9357 void LoadLocalAndServerScore(int nr, boolean download_score)
9359 int last_added_local = scores.last_added_local;
9360 boolean force_last_added = scores.force_last_added;
9362 // needed if only showing server scores
9363 setScoreInfoToDefaults();
9365 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9368 // restore last added local score entry (before merging server scores)
9369 scores.last_added = scores.last_added_local = last_added_local;
9371 if (setup.use_api_server &&
9372 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9374 // load server scores from cache file and trigger update from server
9375 LoadServerScore(nr, download_score);
9377 // merge local scores with scores from server
9381 if (force_last_added)
9382 scores.force_last_added = force_last_added;
9386 // ============================================================================
9387 // setup file functions
9388 // ============================================================================
9390 #define TOKEN_STR_PLAYER_PREFIX "player_"
9393 static struct TokenInfo global_setup_tokens[] =
9397 &setup.player_name, "player_name"
9401 &setup.multiple_users, "multiple_users"
9405 &setup.sound, "sound"
9409 &setup.sound_loops, "repeating_sound_loops"
9413 &setup.sound_music, "background_music"
9417 &setup.sound_simple, "simple_sound_effects"
9421 &setup.toons, "toons"
9425 &setup.scroll_delay, "scroll_delay"
9429 &setup.forced_scroll_delay, "forced_scroll_delay"
9433 &setup.scroll_delay_value, "scroll_delay_value"
9437 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9441 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9445 &setup.fade_screens, "fade_screens"
9449 &setup.autorecord, "automatic_tape_recording"
9453 &setup.auto_pause_on_start, "auto_pause_on_start"
9457 &setup.show_titlescreen, "show_titlescreen"
9461 &setup.quick_doors, "quick_doors"
9465 &setup.team_mode, "team_mode"
9469 &setup.handicap, "handicap"
9473 &setup.skip_levels, "skip_levels"
9477 &setup.increment_levels, "increment_levels"
9481 &setup.auto_play_next_level, "auto_play_next_level"
9485 &setup.count_score_after_game, "count_score_after_game"
9489 &setup.show_scores_after_game, "show_scores_after_game"
9493 &setup.time_limit, "time_limit"
9497 &setup.fullscreen, "fullscreen"
9501 &setup.window_scaling_percent, "window_scaling_percent"
9505 &setup.window_scaling_quality, "window_scaling_quality"
9509 &setup.screen_rendering_mode, "screen_rendering_mode"
9513 &setup.vsync_mode, "vsync_mode"
9517 &setup.ask_on_escape, "ask_on_escape"
9521 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9525 &setup.ask_on_game_over, "ask_on_game_over"
9529 &setup.ask_on_quit_game, "ask_on_quit_game"
9533 &setup.ask_on_quit_program, "ask_on_quit_program"
9537 &setup.quick_switch, "quick_player_switch"
9541 &setup.input_on_focus, "input_on_focus"
9545 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9549 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9553 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9557 &setup.game_speed_extended, "game_speed_extended"
9561 &setup.game_frame_delay, "game_frame_delay"
9565 &setup.sp_show_border_elements, "sp_show_border_elements"
9569 &setup.small_game_graphics, "small_game_graphics"
9573 &setup.show_load_save_buttons, "show_load_save_buttons"
9577 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9581 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9585 &setup.graphics_set, "graphics_set"
9589 &setup.sounds_set, "sounds_set"
9593 &setup.music_set, "music_set"
9597 &setup.override_level_graphics, "override_level_graphics"
9601 &setup.override_level_sounds, "override_level_sounds"
9605 &setup.override_level_music, "override_level_music"
9609 &setup.volume_simple, "volume_simple"
9613 &setup.volume_loops, "volume_loops"
9617 &setup.volume_music, "volume_music"
9621 &setup.network_mode, "network_mode"
9625 &setup.network_player_nr, "network_player"
9629 &setup.network_server_hostname, "network_server_hostname"
9633 &setup.touch.control_type, "touch.control_type"
9637 &setup.touch.move_distance, "touch.move_distance"
9641 &setup.touch.drop_distance, "touch.drop_distance"
9645 &setup.touch.transparency, "touch.transparency"
9649 &setup.touch.draw_outlined, "touch.draw_outlined"
9653 &setup.touch.draw_pressed, "touch.draw_pressed"
9657 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9661 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9665 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9669 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9673 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9677 static struct TokenInfo auto_setup_tokens[] =
9681 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9685 static struct TokenInfo server_setup_tokens[] =
9689 &setup.player_uuid, "player_uuid"
9693 &setup.player_version, "player_version"
9697 &setup.use_api_server, TEST_PREFIX "use_api_server"
9701 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9705 &setup.api_server_password, TEST_PREFIX "api_server_password"
9709 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9713 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9717 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9721 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9725 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9729 static struct TokenInfo editor_setup_tokens[] =
9733 &setup.editor.el_classic, "editor.el_classic"
9737 &setup.editor.el_custom, "editor.el_custom"
9741 &setup.editor.el_user_defined, "editor.el_user_defined"
9745 &setup.editor.el_dynamic, "editor.el_dynamic"
9749 &setup.editor.el_headlines, "editor.el_headlines"
9753 &setup.editor.show_element_token, "editor.show_element_token"
9757 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9761 static struct TokenInfo editor_cascade_setup_tokens[] =
9765 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9769 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9773 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9777 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9781 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9785 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9789 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9793 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9797 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9801 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9805 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9809 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9813 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9817 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9821 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9825 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9829 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9833 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9837 static struct TokenInfo shortcut_setup_tokens[] =
9841 &setup.shortcut.save_game, "shortcut.save_game"
9845 &setup.shortcut.load_game, "shortcut.load_game"
9849 &setup.shortcut.restart_game, "shortcut.restart_game"
9853 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9857 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9861 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9865 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9869 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9873 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9877 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9881 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9885 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9889 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9893 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9897 &setup.shortcut.tape_record, "shortcut.tape_record"
9901 &setup.shortcut.tape_play, "shortcut.tape_play"
9905 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9909 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9913 &setup.shortcut.sound_music, "shortcut.sound_music"
9917 &setup.shortcut.snap_left, "shortcut.snap_left"
9921 &setup.shortcut.snap_right, "shortcut.snap_right"
9925 &setup.shortcut.snap_up, "shortcut.snap_up"
9929 &setup.shortcut.snap_down, "shortcut.snap_down"
9933 static struct SetupInputInfo setup_input;
9934 static struct TokenInfo player_setup_tokens[] =
9938 &setup_input.use_joystick, ".use_joystick"
9942 &setup_input.joy.device_name, ".joy.device_name"
9946 &setup_input.joy.xleft, ".joy.xleft"
9950 &setup_input.joy.xmiddle, ".joy.xmiddle"
9954 &setup_input.joy.xright, ".joy.xright"
9958 &setup_input.joy.yupper, ".joy.yupper"
9962 &setup_input.joy.ymiddle, ".joy.ymiddle"
9966 &setup_input.joy.ylower, ".joy.ylower"
9970 &setup_input.joy.snap, ".joy.snap_field"
9974 &setup_input.joy.drop, ".joy.place_bomb"
9978 &setup_input.key.left, ".key.move_left"
9982 &setup_input.key.right, ".key.move_right"
9986 &setup_input.key.up, ".key.move_up"
9990 &setup_input.key.down, ".key.move_down"
9994 &setup_input.key.snap, ".key.snap_field"
9998 &setup_input.key.drop, ".key.place_bomb"
10002 static struct TokenInfo system_setup_tokens[] =
10006 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10010 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10014 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10018 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10022 static struct TokenInfo internal_setup_tokens[] =
10026 &setup.internal.program_title, "program_title"
10030 &setup.internal.program_version, "program_version"
10034 &setup.internal.program_author, "program_author"
10038 &setup.internal.program_email, "program_email"
10042 &setup.internal.program_website, "program_website"
10046 &setup.internal.program_copyright, "program_copyright"
10050 &setup.internal.program_company, "program_company"
10054 &setup.internal.program_icon_file, "program_icon_file"
10058 &setup.internal.default_graphics_set, "default_graphics_set"
10062 &setup.internal.default_sounds_set, "default_sounds_set"
10066 &setup.internal.default_music_set, "default_music_set"
10070 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10074 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10078 &setup.internal.fallback_music_file, "fallback_music_file"
10082 &setup.internal.default_level_series, "default_level_series"
10086 &setup.internal.default_window_width, "default_window_width"
10090 &setup.internal.default_window_height, "default_window_height"
10094 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10098 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10102 &setup.internal.create_user_levelset, "create_user_levelset"
10106 &setup.internal.menu_game, "menu_game"
10110 &setup.internal.menu_editor, "menu_editor"
10114 &setup.internal.menu_graphics, "menu_graphics"
10118 &setup.internal.menu_sound, "menu_sound"
10122 &setup.internal.menu_artwork, "menu_artwork"
10126 &setup.internal.menu_input, "menu_input"
10130 &setup.internal.menu_touch, "menu_touch"
10134 &setup.internal.menu_shortcuts, "menu_shortcuts"
10138 &setup.internal.menu_exit, "menu_exit"
10142 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10146 &setup.internal.info_title, "info_title"
10150 &setup.internal.info_elements, "info_elements"
10154 &setup.internal.info_music, "info_music"
10158 &setup.internal.info_credits, "info_credits"
10162 &setup.internal.info_program, "info_program"
10166 &setup.internal.info_version, "info_version"
10170 &setup.internal.info_levelset, "info_levelset"
10174 &setup.internal.info_exit, "info_exit"
10178 static struct TokenInfo debug_setup_tokens[] =
10182 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10186 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10190 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10194 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10198 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10202 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10206 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10210 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10214 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10218 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10222 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10226 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10230 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10234 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10238 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10242 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10246 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10250 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10254 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10258 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10262 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10265 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10269 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10273 &setup.debug.xsn_mode, "debug.xsn_mode"
10277 &setup.debug.xsn_percent, "debug.xsn_percent"
10281 static struct TokenInfo options_setup_tokens[] =
10285 &setup.options.verbose, "options.verbose"
10289 static void setSetupInfoToDefaults(struct SetupInfo *si)
10293 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10295 si->multiple_users = TRUE;
10298 si->sound_loops = TRUE;
10299 si->sound_music = TRUE;
10300 si->sound_simple = TRUE;
10302 si->scroll_delay = TRUE;
10303 si->forced_scroll_delay = FALSE;
10304 si->scroll_delay_value = STD_SCROLL_DELAY;
10305 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10306 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10307 si->fade_screens = TRUE;
10308 si->autorecord = TRUE;
10309 si->auto_pause_on_start = FALSE;
10310 si->show_titlescreen = TRUE;
10311 si->quick_doors = FALSE;
10312 si->team_mode = FALSE;
10313 si->handicap = TRUE;
10314 si->skip_levels = TRUE;
10315 si->increment_levels = TRUE;
10316 si->auto_play_next_level = TRUE;
10317 si->count_score_after_game = TRUE;
10318 si->show_scores_after_game = TRUE;
10319 si->time_limit = TRUE;
10320 si->fullscreen = FALSE;
10321 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10322 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10323 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10324 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10325 si->ask_on_escape = TRUE;
10326 si->ask_on_escape_editor = TRUE;
10327 si->ask_on_game_over = TRUE;
10328 si->ask_on_quit_game = TRUE;
10329 si->ask_on_quit_program = TRUE;
10330 si->quick_switch = FALSE;
10331 si->input_on_focus = FALSE;
10332 si->prefer_aga_graphics = TRUE;
10333 si->prefer_lowpass_sounds = FALSE;
10334 si->prefer_extra_panel_items = TRUE;
10335 si->game_speed_extended = FALSE;
10336 si->game_frame_delay = GAME_FRAME_DELAY;
10337 si->sp_show_border_elements = FALSE;
10338 si->small_game_graphics = FALSE;
10339 si->show_load_save_buttons = FALSE;
10340 si->show_undo_redo_buttons = FALSE;
10341 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10343 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10344 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10345 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10347 si->override_level_graphics = FALSE;
10348 si->override_level_sounds = FALSE;
10349 si->override_level_music = FALSE;
10351 si->volume_simple = 100; // percent
10352 si->volume_loops = 100; // percent
10353 si->volume_music = 100; // percent
10355 si->network_mode = FALSE;
10356 si->network_player_nr = 0; // first player
10357 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10359 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10360 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10361 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10362 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10363 si->touch.draw_outlined = TRUE;
10364 si->touch.draw_pressed = TRUE;
10366 for (i = 0; i < 2; i++)
10368 char *default_grid_button[6][2] =
10374 { "111222", " vv " },
10375 { "111222", " vv " }
10377 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10378 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10379 int min_xsize = MIN(6, grid_xsize);
10380 int min_ysize = MIN(6, grid_ysize);
10381 int startx = grid_xsize - min_xsize;
10382 int starty = grid_ysize - min_ysize;
10385 // virtual buttons grid can only be set to defaults if video is initialized
10386 // (this will be repeated if virtual buttons are not loaded from setup file)
10387 if (video.initialized)
10389 si->touch.grid_xsize[i] = grid_xsize;
10390 si->touch.grid_ysize[i] = grid_ysize;
10394 si->touch.grid_xsize[i] = -1;
10395 si->touch.grid_ysize[i] = -1;
10398 for (x = 0; x < MAX_GRID_XSIZE; x++)
10399 for (y = 0; y < MAX_GRID_YSIZE; y++)
10400 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10402 for (x = 0; x < min_xsize; x++)
10403 for (y = 0; y < min_ysize; y++)
10404 si->touch.grid_button[i][x][starty + y] =
10405 default_grid_button[y][0][x];
10407 for (x = 0; x < min_xsize; x++)
10408 for (y = 0; y < min_ysize; y++)
10409 si->touch.grid_button[i][startx + x][starty + y] =
10410 default_grid_button[y][1][x];
10413 si->touch.grid_initialized = video.initialized;
10415 si->touch.overlay_buttons = FALSE;
10417 si->editor.el_boulderdash = TRUE;
10418 si->editor.el_emerald_mine = TRUE;
10419 si->editor.el_emerald_mine_club = TRUE;
10420 si->editor.el_more = TRUE;
10421 si->editor.el_sokoban = TRUE;
10422 si->editor.el_supaplex = TRUE;
10423 si->editor.el_diamond_caves = TRUE;
10424 si->editor.el_dx_boulderdash = TRUE;
10426 si->editor.el_mirror_magic = TRUE;
10427 si->editor.el_deflektor = TRUE;
10429 si->editor.el_chars = TRUE;
10430 si->editor.el_steel_chars = TRUE;
10432 si->editor.el_classic = TRUE;
10433 si->editor.el_custom = TRUE;
10435 si->editor.el_user_defined = FALSE;
10436 si->editor.el_dynamic = TRUE;
10438 si->editor.el_headlines = TRUE;
10440 si->editor.show_element_token = FALSE;
10442 si->editor.show_read_only_warning = TRUE;
10444 si->editor.use_template_for_new_levels = TRUE;
10446 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10447 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10448 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10449 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10450 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10452 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10453 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10454 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10455 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10456 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10458 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10459 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10460 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10461 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10462 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10463 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10465 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10466 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10467 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10469 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10470 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10471 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10472 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10474 for (i = 0; i < MAX_PLAYERS; i++)
10476 si->input[i].use_joystick = FALSE;
10477 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10478 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10479 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10480 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10481 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10482 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10483 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10484 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10485 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10486 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10487 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10488 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10489 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10490 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10491 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10494 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10495 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10496 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10497 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10499 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10500 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10501 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10502 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10503 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10504 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10505 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10507 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10509 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10510 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10511 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10513 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10514 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10515 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10517 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10518 si->internal.choose_from_top_leveldir = FALSE;
10519 si->internal.show_scaling_in_title = TRUE;
10520 si->internal.create_user_levelset = TRUE;
10522 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10523 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10525 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10526 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10527 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10528 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10529 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10530 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10531 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10532 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10533 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10534 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10536 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10537 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10538 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10539 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10540 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10541 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10542 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10543 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10544 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10545 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10547 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10548 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10550 si->debug.show_frames_per_second = FALSE;
10552 si->debug.xsn_mode = AUTO;
10553 si->debug.xsn_percent = 0;
10555 si->options.verbose = FALSE;
10557 #if defined(PLATFORM_ANDROID)
10558 si->fullscreen = TRUE;
10559 si->touch.overlay_buttons = TRUE;
10562 setHideSetupEntry(&setup.debug.xsn_mode);
10565 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10567 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10570 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10572 si->player_uuid = NULL; // (will be set later)
10573 si->player_version = 1; // (will be set later)
10575 si->use_api_server = TRUE;
10576 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10577 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10578 si->ask_for_uploading_tapes = TRUE;
10579 si->ask_for_remaining_tapes = FALSE;
10580 si->provide_uploading_tapes = TRUE;
10581 si->ask_for_using_api_server = TRUE;
10582 si->has_remaining_tapes = FALSE;
10585 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10587 si->editor_cascade.el_bd = TRUE;
10588 si->editor_cascade.el_em = TRUE;
10589 si->editor_cascade.el_emc = TRUE;
10590 si->editor_cascade.el_rnd = TRUE;
10591 si->editor_cascade.el_sb = TRUE;
10592 si->editor_cascade.el_sp = TRUE;
10593 si->editor_cascade.el_dc = TRUE;
10594 si->editor_cascade.el_dx = TRUE;
10596 si->editor_cascade.el_mm = TRUE;
10597 si->editor_cascade.el_df = TRUE;
10599 si->editor_cascade.el_chars = FALSE;
10600 si->editor_cascade.el_steel_chars = FALSE;
10601 si->editor_cascade.el_ce = FALSE;
10602 si->editor_cascade.el_ge = FALSE;
10603 si->editor_cascade.el_es = FALSE;
10604 si->editor_cascade.el_ref = FALSE;
10605 si->editor_cascade.el_user = FALSE;
10606 si->editor_cascade.el_dynamic = FALSE;
10609 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10611 static char *getHideSetupToken(void *setup_value)
10613 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10615 if (setup_value != NULL)
10616 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10618 return hide_setup_token;
10621 void setHideSetupEntry(void *setup_value)
10623 char *hide_setup_token = getHideSetupToken(setup_value);
10625 if (hide_setup_hash == NULL)
10626 hide_setup_hash = newSetupFileHash();
10628 if (setup_value != NULL)
10629 setHashEntry(hide_setup_hash, hide_setup_token, "");
10632 void removeHideSetupEntry(void *setup_value)
10634 char *hide_setup_token = getHideSetupToken(setup_value);
10636 if (setup_value != NULL)
10637 removeHashEntry(hide_setup_hash, hide_setup_token);
10640 boolean hideSetupEntry(void *setup_value)
10642 char *hide_setup_token = getHideSetupToken(setup_value);
10644 return (setup_value != NULL &&
10645 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10648 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10649 struct TokenInfo *token_info,
10650 int token_nr, char *token_text)
10652 char *token_hide_text = getStringCat2(token_text, ".hide");
10653 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10655 // set the value of this setup option in the setup option structure
10656 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10658 // check if this setup option should be hidden in the setup menu
10659 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10660 setHideSetupEntry(token_info[token_nr].value);
10662 free(token_hide_text);
10665 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10666 struct TokenInfo *token_info,
10669 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10670 token_info[token_nr].text);
10673 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10677 if (!setup_file_hash)
10680 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10681 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10683 setup.touch.grid_initialized = TRUE;
10684 for (i = 0; i < 2; i++)
10686 int grid_xsize = setup.touch.grid_xsize[i];
10687 int grid_ysize = setup.touch.grid_ysize[i];
10690 // if virtual buttons are not loaded from setup file, repeat initializing
10691 // virtual buttons grid with default values later when video is initialized
10692 if (grid_xsize == -1 ||
10695 setup.touch.grid_initialized = FALSE;
10700 for (y = 0; y < grid_ysize; y++)
10702 char token_string[MAX_LINE_LEN];
10704 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10706 char *value_string = getHashEntry(setup_file_hash, token_string);
10708 if (value_string == NULL)
10711 for (x = 0; x < grid_xsize; x++)
10713 char c = value_string[x];
10715 setup.touch.grid_button[i][x][y] =
10716 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10721 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10722 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10724 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10725 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10727 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10731 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10733 setup_input = setup.input[pnr];
10734 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10736 char full_token[100];
10738 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10739 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10742 setup.input[pnr] = setup_input;
10745 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10746 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10748 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10749 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10751 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10752 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10754 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10755 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10757 setHideRelatedSetupEntries();
10760 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10764 if (!setup_file_hash)
10767 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10768 setSetupInfo(auto_setup_tokens, i,
10769 getHashEntry(setup_file_hash,
10770 auto_setup_tokens[i].text));
10773 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10777 if (!setup_file_hash)
10780 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10781 setSetupInfo(server_setup_tokens, i,
10782 getHashEntry(setup_file_hash,
10783 server_setup_tokens[i].text));
10786 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10790 if (!setup_file_hash)
10793 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10794 setSetupInfo(editor_cascade_setup_tokens, i,
10795 getHashEntry(setup_file_hash,
10796 editor_cascade_setup_tokens[i].text));
10799 void LoadUserNames(void)
10801 int last_user_nr = user.nr;
10804 if (global.user_names != NULL)
10806 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10807 checked_free(global.user_names[i]);
10809 checked_free(global.user_names);
10812 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10814 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10818 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10820 if (setup_file_hash)
10822 char *player_name = getHashEntry(setup_file_hash, "player_name");
10824 global.user_names[i] = getFixedUserName(player_name);
10826 freeSetupFileHash(setup_file_hash);
10829 if (global.user_names[i] == NULL)
10830 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10833 user.nr = last_user_nr;
10836 void LoadSetupFromFilename(char *filename)
10838 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10840 if (setup_file_hash)
10842 decodeSetupFileHash_Default(setup_file_hash);
10844 freeSetupFileHash(setup_file_hash);
10848 Debug("setup", "using default setup values");
10852 static void LoadSetup_SpecialPostProcessing(void)
10854 char *player_name_new;
10856 // needed to work around problems with fixed length strings
10857 player_name_new = getFixedUserName(setup.player_name);
10858 free(setup.player_name);
10859 setup.player_name = player_name_new;
10861 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10862 if (setup.scroll_delay == FALSE)
10864 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10865 setup.scroll_delay = TRUE; // now always "on"
10868 // make sure that scroll delay value stays inside valid range
10869 setup.scroll_delay_value =
10870 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10873 void LoadSetup_Default(void)
10877 // always start with reliable default values
10878 setSetupInfoToDefaults(&setup);
10880 // try to load setup values from default setup file
10881 filename = getDefaultSetupFilename();
10883 if (fileExists(filename))
10884 LoadSetupFromFilename(filename);
10886 // try to load setup values from platform setup file
10887 filename = getPlatformSetupFilename();
10889 if (fileExists(filename))
10890 LoadSetupFromFilename(filename);
10892 // try to load setup values from user setup file
10893 filename = getSetupFilename();
10895 LoadSetupFromFilename(filename);
10897 LoadSetup_SpecialPostProcessing();
10900 void LoadSetup_AutoSetup(void)
10902 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10903 SetupFileHash *setup_file_hash = NULL;
10905 // always start with reliable default values
10906 setSetupInfoToDefaults_AutoSetup(&setup);
10908 setup_file_hash = loadSetupFileHash(filename);
10910 if (setup_file_hash)
10912 decodeSetupFileHash_AutoSetup(setup_file_hash);
10914 freeSetupFileHash(setup_file_hash);
10920 void LoadSetup_ServerSetup(void)
10922 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
10923 SetupFileHash *setup_file_hash = NULL;
10925 // always start with reliable default values
10926 setSetupInfoToDefaults_ServerSetup(&setup);
10928 setup_file_hash = loadSetupFileHash(filename);
10930 if (setup_file_hash)
10932 decodeSetupFileHash_ServerSetup(setup_file_hash);
10934 freeSetupFileHash(setup_file_hash);
10939 if (setup.player_uuid == NULL)
10941 // player UUID does not yet exist in setup file
10942 setup.player_uuid = getStringCopy(getUUID());
10943 setup.player_version = 2;
10945 SaveSetup_ServerSetup();
10949 void LoadSetup_EditorCascade(void)
10951 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10952 SetupFileHash *setup_file_hash = NULL;
10954 // always start with reliable default values
10955 setSetupInfoToDefaults_EditorCascade(&setup);
10957 setup_file_hash = loadSetupFileHash(filename);
10959 if (setup_file_hash)
10961 decodeSetupFileHash_EditorCascade(setup_file_hash);
10963 freeSetupFileHash(setup_file_hash);
10969 void LoadSetup(void)
10971 LoadSetup_Default();
10972 LoadSetup_AutoSetup();
10973 LoadSetup_ServerSetup();
10974 LoadSetup_EditorCascade();
10977 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
10978 char *mapping_line)
10980 char mapping_guid[MAX_LINE_LEN];
10981 char *mapping_start, *mapping_end;
10983 // get GUID from game controller mapping line: copy complete line
10984 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
10985 mapping_guid[MAX_LINE_LEN - 1] = '\0';
10987 // get GUID from game controller mapping line: cut after GUID part
10988 mapping_start = strchr(mapping_guid, ',');
10989 if (mapping_start != NULL)
10990 *mapping_start = '\0';
10992 // cut newline from game controller mapping line
10993 mapping_end = strchr(mapping_line, '\n');
10994 if (mapping_end != NULL)
10995 *mapping_end = '\0';
10997 // add mapping entry to game controller mappings hash
10998 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11001 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11006 if (!(file = fopen(filename, MODE_READ)))
11008 Warn("cannot read game controller mappings file '%s'", filename);
11013 while (!feof(file))
11015 char line[MAX_LINE_LEN];
11017 if (!fgets(line, MAX_LINE_LEN, file))
11020 addGameControllerMappingToHash(mappings_hash, line);
11026 void SaveSetup_Default(void)
11028 char *filename = getSetupFilename();
11032 InitUserDataDirectory();
11034 if (!(file = fopen(filename, MODE_WRITE)))
11036 Warn("cannot write setup file '%s'", filename);
11041 fprintFileHeader(file, SETUP_FILENAME);
11043 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11045 // just to make things nicer :)
11046 if (global_setup_tokens[i].value == &setup.multiple_users ||
11047 global_setup_tokens[i].value == &setup.sound ||
11048 global_setup_tokens[i].value == &setup.graphics_set ||
11049 global_setup_tokens[i].value == &setup.volume_simple ||
11050 global_setup_tokens[i].value == &setup.network_mode ||
11051 global_setup_tokens[i].value == &setup.touch.control_type ||
11052 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11053 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11054 fprintf(file, "\n");
11056 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11059 for (i = 0; i < 2; i++)
11061 int grid_xsize = setup.touch.grid_xsize[i];
11062 int grid_ysize = setup.touch.grid_ysize[i];
11065 fprintf(file, "\n");
11067 for (y = 0; y < grid_ysize; y++)
11069 char token_string[MAX_LINE_LEN];
11070 char value_string[MAX_LINE_LEN];
11072 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11074 for (x = 0; x < grid_xsize; x++)
11076 char c = setup.touch.grid_button[i][x][y];
11078 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11081 value_string[grid_xsize] = '\0';
11083 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11087 fprintf(file, "\n");
11088 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11089 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11091 fprintf(file, "\n");
11092 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11093 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11095 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11099 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11100 fprintf(file, "\n");
11102 setup_input = setup.input[pnr];
11103 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11104 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11107 fprintf(file, "\n");
11108 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11109 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11111 // (internal setup values not saved to user setup file)
11113 fprintf(file, "\n");
11114 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11115 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11116 setup.debug.xsn_mode != AUTO)
11117 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11119 fprintf(file, "\n");
11120 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11121 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11125 SetFilePermissions(filename, PERMS_PRIVATE);
11128 void SaveSetup_AutoSetup(void)
11130 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11134 InitUserDataDirectory();
11136 if (!(file = fopen(filename, MODE_WRITE)))
11138 Warn("cannot write auto setup file '%s'", filename);
11145 fprintFileHeader(file, AUTOSETUP_FILENAME);
11147 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11148 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11152 SetFilePermissions(filename, PERMS_PRIVATE);
11157 void SaveSetup_ServerSetup(void)
11159 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11163 InitUserDataDirectory();
11165 if (!(file = fopen(filename, MODE_WRITE)))
11167 Warn("cannot write server setup file '%s'", filename);
11174 fprintFileHeader(file, SERVERSETUP_FILENAME);
11176 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11178 // just to make things nicer :)
11179 if (server_setup_tokens[i].value == &setup.use_api_server)
11180 fprintf(file, "\n");
11182 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11187 SetFilePermissions(filename, PERMS_PRIVATE);
11192 void SaveSetup_EditorCascade(void)
11194 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11198 InitUserDataDirectory();
11200 if (!(file = fopen(filename, MODE_WRITE)))
11202 Warn("cannot write editor cascade state file '%s'", filename);
11209 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11211 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11212 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11216 SetFilePermissions(filename, PERMS_PRIVATE);
11221 void SaveSetup(void)
11223 SaveSetup_Default();
11224 SaveSetup_AutoSetup();
11225 SaveSetup_ServerSetup();
11226 SaveSetup_EditorCascade();
11229 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11234 if (!(file = fopen(filename, MODE_WRITE)))
11236 Warn("cannot write game controller mappings file '%s'", filename);
11241 BEGIN_HASH_ITERATION(mappings_hash, itr)
11243 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11245 END_HASH_ITERATION(mappings_hash, itr)
11250 void SaveSetup_AddGameControllerMapping(char *mapping)
11252 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11253 SetupFileHash *mappings_hash = newSetupFileHash();
11255 InitUserDataDirectory();
11257 // load existing personal game controller mappings
11258 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11260 // add new mapping to personal game controller mappings
11261 addGameControllerMappingToHash(mappings_hash, mapping);
11263 // save updated personal game controller mappings
11264 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11266 freeSetupFileHash(mappings_hash);
11270 void LoadCustomElementDescriptions(void)
11272 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11273 SetupFileHash *setup_file_hash;
11276 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11278 if (element_info[i].custom_description != NULL)
11280 free(element_info[i].custom_description);
11281 element_info[i].custom_description = NULL;
11285 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11288 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11290 char *token = getStringCat2(element_info[i].token_name, ".name");
11291 char *value = getHashEntry(setup_file_hash, token);
11294 element_info[i].custom_description = getStringCopy(value);
11299 freeSetupFileHash(setup_file_hash);
11302 static int getElementFromToken(char *token)
11304 char *value = getHashEntry(element_token_hash, token);
11307 return atoi(value);
11309 Warn("unknown element token '%s'", token);
11311 return EL_UNDEFINED;
11314 void FreeGlobalAnimEventInfo(void)
11316 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11318 if (gaei->event_list == NULL)
11323 for (i = 0; i < gaei->num_event_lists; i++)
11325 checked_free(gaei->event_list[i]->event_value);
11326 checked_free(gaei->event_list[i]);
11329 checked_free(gaei->event_list);
11331 gaei->event_list = NULL;
11332 gaei->num_event_lists = 0;
11335 static int AddGlobalAnimEventList(void)
11337 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11338 int list_pos = gaei->num_event_lists++;
11340 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11341 sizeof(struct GlobalAnimEventListInfo *));
11343 gaei->event_list[list_pos] =
11344 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11346 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11348 gaeli->event_value = NULL;
11349 gaeli->num_event_values = 0;
11354 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11356 // do not add empty global animation events
11357 if (event_value == ANIM_EVENT_NONE)
11360 // if list position is undefined, create new list
11361 if (list_pos == ANIM_EVENT_UNDEFINED)
11362 list_pos = AddGlobalAnimEventList();
11364 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11365 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11366 int value_pos = gaeli->num_event_values++;
11368 gaeli->event_value = checked_realloc(gaeli->event_value,
11369 gaeli->num_event_values * sizeof(int *));
11371 gaeli->event_value[value_pos] = event_value;
11376 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11378 if (list_pos == ANIM_EVENT_UNDEFINED)
11379 return ANIM_EVENT_NONE;
11381 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11382 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11384 return gaeli->event_value[value_pos];
11387 int GetGlobalAnimEventValueCount(int list_pos)
11389 if (list_pos == ANIM_EVENT_UNDEFINED)
11392 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11393 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11395 return gaeli->num_event_values;
11398 // This function checks if a string <s> of the format "string1, string2, ..."
11399 // exactly contains a string <s_contained>.
11401 static boolean string_has_parameter(char *s, char *s_contained)
11405 if (s == NULL || s_contained == NULL)
11408 if (strlen(s_contained) > strlen(s))
11411 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11413 char next_char = s[strlen(s_contained)];
11415 // check if next character is delimiter or whitespace
11416 return (next_char == ',' || next_char == '\0' ||
11417 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11420 // check if string contains another parameter string after a comma
11421 substring = strchr(s, ',');
11422 if (substring == NULL) // string does not contain a comma
11425 // advance string pointer to next character after the comma
11428 // skip potential whitespaces after the comma
11429 while (*substring == ' ' || *substring == '\t')
11432 return string_has_parameter(substring, s_contained);
11435 static int get_anim_parameter_value(char *s)
11437 int event_value[] =
11445 char *pattern_1[] =
11453 char *pattern_2 = ".part_";
11454 char *matching_char = NULL;
11456 int pattern_1_len = 0;
11457 int result = ANIM_EVENT_NONE;
11460 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11462 matching_char = strstr(s_ptr, pattern_1[i]);
11463 pattern_1_len = strlen(pattern_1[i]);
11464 result = event_value[i];
11466 if (matching_char != NULL)
11470 if (matching_char == NULL)
11471 return ANIM_EVENT_NONE;
11473 s_ptr = matching_char + pattern_1_len;
11475 // check for main animation number ("anim_X" or "anim_XX")
11476 if (*s_ptr >= '0' && *s_ptr <= '9')
11478 int gic_anim_nr = (*s_ptr++ - '0');
11480 if (*s_ptr >= '0' && *s_ptr <= '9')
11481 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11483 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11484 return ANIM_EVENT_NONE;
11486 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11490 // invalid main animation number specified
11492 return ANIM_EVENT_NONE;
11495 // check for animation part number ("part_X" or "part_XX") (optional)
11496 if (strPrefix(s_ptr, pattern_2))
11498 s_ptr += strlen(pattern_2);
11500 if (*s_ptr >= '0' && *s_ptr <= '9')
11502 int gic_part_nr = (*s_ptr++ - '0');
11504 if (*s_ptr >= '0' && *s_ptr <= '9')
11505 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11507 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11508 return ANIM_EVENT_NONE;
11510 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11514 // invalid animation part number specified
11516 return ANIM_EVENT_NONE;
11520 // discard result if next character is neither delimiter nor whitespace
11521 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11522 *s_ptr == ' ' || *s_ptr == '\t'))
11523 return ANIM_EVENT_NONE;
11528 static int get_anim_parameter_values(char *s)
11530 int list_pos = ANIM_EVENT_UNDEFINED;
11531 int event_value = ANIM_EVENT_DEFAULT;
11533 if (string_has_parameter(s, "any"))
11534 event_value |= ANIM_EVENT_ANY;
11536 if (string_has_parameter(s, "click:self") ||
11537 string_has_parameter(s, "click") ||
11538 string_has_parameter(s, "self"))
11539 event_value |= ANIM_EVENT_SELF;
11541 if (string_has_parameter(s, "unclick:any"))
11542 event_value |= ANIM_EVENT_UNCLICK_ANY;
11544 // if animation event found, add it to global animation event list
11545 if (event_value != ANIM_EVENT_NONE)
11546 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11550 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11551 event_value = get_anim_parameter_value(s);
11553 // if animation event found, add it to global animation event list
11554 if (event_value != ANIM_EVENT_NONE)
11555 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11557 // continue with next part of the string, starting with next comma
11558 s = strchr(s + 1, ',');
11564 static int get_anim_action_parameter_value(char *token)
11566 // check most common default case first to massively speed things up
11567 if (strEqual(token, ARG_UNDEFINED))
11568 return ANIM_EVENT_ACTION_NONE;
11570 int result = getImageIDFromToken(token);
11574 char *gfx_token = getStringCat2("gfx.", token);
11576 result = getImageIDFromToken(gfx_token);
11578 checked_free(gfx_token);
11583 Key key = getKeyFromX11KeyName(token);
11585 if (key != KSYM_UNDEFINED)
11586 result = -(int)key;
11593 result = get_hash_from_key(token); // unsigned int => int
11594 result = ABS(result); // may be negative now
11595 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11597 setHashEntry(anim_url_hash, int2str(result, 0), token);
11602 result = ANIM_EVENT_ACTION_NONE;
11607 int get_parameter_value(char *value_raw, char *suffix, int type)
11609 char *value = getStringToLower(value_raw);
11610 int result = 0; // probably a save default value
11612 if (strEqual(suffix, ".direction"))
11614 result = (strEqual(value, "left") ? MV_LEFT :
11615 strEqual(value, "right") ? MV_RIGHT :
11616 strEqual(value, "up") ? MV_UP :
11617 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11619 else if (strEqual(suffix, ".position"))
11621 result = (strEqual(value, "left") ? POS_LEFT :
11622 strEqual(value, "right") ? POS_RIGHT :
11623 strEqual(value, "top") ? POS_TOP :
11624 strEqual(value, "upper") ? POS_UPPER :
11625 strEqual(value, "middle") ? POS_MIDDLE :
11626 strEqual(value, "lower") ? POS_LOWER :
11627 strEqual(value, "bottom") ? POS_BOTTOM :
11628 strEqual(value, "any") ? POS_ANY :
11629 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11631 else if (strEqual(suffix, ".align"))
11633 result = (strEqual(value, "left") ? ALIGN_LEFT :
11634 strEqual(value, "right") ? ALIGN_RIGHT :
11635 strEqual(value, "center") ? ALIGN_CENTER :
11636 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11638 else if (strEqual(suffix, ".valign"))
11640 result = (strEqual(value, "top") ? VALIGN_TOP :
11641 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11642 strEqual(value, "middle") ? VALIGN_MIDDLE :
11643 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11645 else if (strEqual(suffix, ".anim_mode"))
11647 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11648 string_has_parameter(value, "loop") ? ANIM_LOOP :
11649 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11650 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11651 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11652 string_has_parameter(value, "random") ? ANIM_RANDOM :
11653 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11654 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11655 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11656 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11657 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11658 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11659 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11660 string_has_parameter(value, "all") ? ANIM_ALL :
11661 string_has_parameter(value, "tiled") ? ANIM_TILED :
11664 if (string_has_parameter(value, "once"))
11665 result |= ANIM_ONCE;
11667 if (string_has_parameter(value, "reverse"))
11668 result |= ANIM_REVERSE;
11670 if (string_has_parameter(value, "opaque_player"))
11671 result |= ANIM_OPAQUE_PLAYER;
11673 if (string_has_parameter(value, "static_panel"))
11674 result |= ANIM_STATIC_PANEL;
11676 else if (strEqual(suffix, ".init_event") ||
11677 strEqual(suffix, ".anim_event"))
11679 result = get_anim_parameter_values(value);
11681 else if (strEqual(suffix, ".init_delay_action") ||
11682 strEqual(suffix, ".anim_delay_action") ||
11683 strEqual(suffix, ".post_delay_action") ||
11684 strEqual(suffix, ".init_event_action") ||
11685 strEqual(suffix, ".anim_event_action"))
11687 result = get_anim_action_parameter_value(value_raw);
11689 else if (strEqual(suffix, ".class"))
11691 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11692 get_hash_from_key(value));
11694 else if (strEqual(suffix, ".style"))
11696 result = STYLE_DEFAULT;
11698 if (string_has_parameter(value, "accurate_borders"))
11699 result |= STYLE_ACCURATE_BORDERS;
11701 if (string_has_parameter(value, "inner_corners"))
11702 result |= STYLE_INNER_CORNERS;
11704 if (string_has_parameter(value, "reverse"))
11705 result |= STYLE_REVERSE;
11707 if (string_has_parameter(value, "leftmost_position"))
11708 result |= STYLE_LEFTMOST_POSITION;
11710 if (string_has_parameter(value, "block_clicks"))
11711 result |= STYLE_BLOCK;
11713 if (string_has_parameter(value, "passthrough_clicks"))
11714 result |= STYLE_PASSTHROUGH;
11716 if (string_has_parameter(value, "multiple_actions"))
11717 result |= STYLE_MULTIPLE_ACTIONS;
11719 else if (strEqual(suffix, ".fade_mode"))
11721 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11722 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11723 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11724 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11725 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11726 FADE_MODE_DEFAULT);
11728 else if (strEqual(suffix, ".auto_delay_unit"))
11730 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11731 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11732 AUTO_DELAY_UNIT_DEFAULT);
11734 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11736 result = gfx.get_font_from_token_function(value);
11738 else // generic parameter of type integer or boolean
11740 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11741 type == TYPE_INTEGER ? get_integer_from_string(value) :
11742 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11743 ARG_UNDEFINED_VALUE);
11751 static int get_token_parameter_value(char *token, char *value_raw)
11755 if (token == NULL || value_raw == NULL)
11756 return ARG_UNDEFINED_VALUE;
11758 suffix = strrchr(token, '.');
11759 if (suffix == NULL)
11762 if (strEqual(suffix, ".element"))
11763 return getElementFromToken(value_raw);
11765 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11766 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11769 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11770 boolean ignore_defaults)
11774 for (i = 0; image_config_vars[i].token != NULL; i++)
11776 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11778 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11779 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11783 *image_config_vars[i].value =
11784 get_token_parameter_value(image_config_vars[i].token, value);
11788 void InitMenuDesignSettings_Static(void)
11790 // always start with reliable default values from static default config
11791 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11794 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11798 // the following initializes hierarchical values from static configuration
11800 // special case: initialize "ARG_DEFAULT" values in static default config
11801 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11802 titlescreen_initial_first_default.fade_mode =
11803 title_initial_first_default.fade_mode;
11804 titlescreen_initial_first_default.fade_delay =
11805 title_initial_first_default.fade_delay;
11806 titlescreen_initial_first_default.post_delay =
11807 title_initial_first_default.post_delay;
11808 titlescreen_initial_first_default.auto_delay =
11809 title_initial_first_default.auto_delay;
11810 titlescreen_initial_first_default.auto_delay_unit =
11811 title_initial_first_default.auto_delay_unit;
11812 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
11813 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11814 titlescreen_first_default.post_delay = title_first_default.post_delay;
11815 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11816 titlescreen_first_default.auto_delay_unit =
11817 title_first_default.auto_delay_unit;
11818 titlemessage_initial_first_default.fade_mode =
11819 title_initial_first_default.fade_mode;
11820 titlemessage_initial_first_default.fade_delay =
11821 title_initial_first_default.fade_delay;
11822 titlemessage_initial_first_default.post_delay =
11823 title_initial_first_default.post_delay;
11824 titlemessage_initial_first_default.auto_delay =
11825 title_initial_first_default.auto_delay;
11826 titlemessage_initial_first_default.auto_delay_unit =
11827 title_initial_first_default.auto_delay_unit;
11828 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
11829 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11830 titlemessage_first_default.post_delay = title_first_default.post_delay;
11831 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11832 titlemessage_first_default.auto_delay_unit =
11833 title_first_default.auto_delay_unit;
11835 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
11836 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11837 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11838 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11839 titlescreen_initial_default.auto_delay_unit =
11840 title_initial_default.auto_delay_unit;
11841 titlescreen_default.fade_mode = title_default.fade_mode;
11842 titlescreen_default.fade_delay = title_default.fade_delay;
11843 titlescreen_default.post_delay = title_default.post_delay;
11844 titlescreen_default.auto_delay = title_default.auto_delay;
11845 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11846 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
11847 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11848 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11849 titlemessage_initial_default.auto_delay_unit =
11850 title_initial_default.auto_delay_unit;
11851 titlemessage_default.fade_mode = title_default.fade_mode;
11852 titlemessage_default.fade_delay = title_default.fade_delay;
11853 titlemessage_default.post_delay = title_default.post_delay;
11854 titlemessage_default.auto_delay = title_default.auto_delay;
11855 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11857 // special case: initialize "ARG_DEFAULT" values in static default config
11858 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11859 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11861 titlescreen_initial_first[i] = titlescreen_initial_first_default;
11862 titlescreen_first[i] = titlescreen_first_default;
11863 titlemessage_initial_first[i] = titlemessage_initial_first_default;
11864 titlemessage_first[i] = titlemessage_first_default;
11866 titlescreen_initial[i] = titlescreen_initial_default;
11867 titlescreen[i] = titlescreen_default;
11868 titlemessage_initial[i] = titlemessage_initial_default;
11869 titlemessage[i] = titlemessage_default;
11872 // special case: initialize "ARG_DEFAULT" values in static default config
11873 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11874 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11876 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11879 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11880 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11881 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11884 // special case: initialize "ARG_DEFAULT" values in static default config
11885 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11886 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11888 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11889 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11890 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11892 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
11895 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11899 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11903 struct XY *dst, *src;
11905 game_buttons_xy[] =
11907 { &game.button.save, &game.button.stop },
11908 { &game.button.pause2, &game.button.pause },
11909 { &game.button.load, &game.button.play },
11910 { &game.button.undo, &game.button.stop },
11911 { &game.button.redo, &game.button.play },
11917 // special case: initialize later added SETUP list size from LEVELS value
11918 if (menu.list_size[GAME_MODE_SETUP] == -1)
11919 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11921 // set default position for snapshot buttons to stop/pause/play buttons
11922 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11923 if ((*game_buttons_xy[i].dst).x == -1 &&
11924 (*game_buttons_xy[i].dst).y == -1)
11925 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11927 // --------------------------------------------------------------------------
11928 // dynamic viewports (including playfield margins, borders and alignments)
11929 // --------------------------------------------------------------------------
11931 // dynamic viewports currently only supported for landscape mode
11932 int display_width = MAX(video.display_width, video.display_height);
11933 int display_height = MIN(video.display_width, video.display_height);
11935 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11937 struct RectWithBorder *vp_window = &viewport.window[i];
11938 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11939 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
11940 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
11941 boolean dynamic_window_width = (vp_window->min_width != -1);
11942 boolean dynamic_window_height = (vp_window->min_height != -1);
11943 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
11944 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11946 // adjust window size if min/max width/height is specified
11948 if (vp_window->min_width != -1)
11950 int window_width = display_width;
11952 // when using static window height, use aspect ratio of display
11953 if (vp_window->min_height == -1)
11954 window_width = vp_window->height * display_width / display_height;
11956 vp_window->width = MAX(vp_window->min_width, window_width);
11959 if (vp_window->min_height != -1)
11961 int window_height = display_height;
11963 // when using static window width, use aspect ratio of display
11964 if (vp_window->min_width == -1)
11965 window_height = vp_window->width * display_height / display_width;
11967 vp_window->height = MAX(vp_window->min_height, window_height);
11970 if (vp_window->max_width != -1)
11971 vp_window->width = MIN(vp_window->width, vp_window->max_width);
11973 if (vp_window->max_height != -1)
11974 vp_window->height = MIN(vp_window->height, vp_window->max_height);
11976 int playfield_width = vp_window->width;
11977 int playfield_height = vp_window->height;
11979 // adjust playfield size and position according to specified margins
11981 playfield_width -= vp_playfield->margin_left;
11982 playfield_width -= vp_playfield->margin_right;
11984 playfield_height -= vp_playfield->margin_top;
11985 playfield_height -= vp_playfield->margin_bottom;
11987 // adjust playfield size if min/max width/height is specified
11989 if (vp_playfield->min_width != -1)
11990 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
11992 if (vp_playfield->min_height != -1)
11993 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
11995 if (vp_playfield->max_width != -1)
11996 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
11998 if (vp_playfield->max_height != -1)
11999 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
12001 // adjust playfield position according to specified alignment
12003 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12004 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12005 else if (vp_playfield->align == ALIGN_CENTER)
12006 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12007 else if (vp_playfield->align == ALIGN_RIGHT)
12008 vp_playfield->x += playfield_width - vp_playfield->width;
12010 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12011 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12012 else if (vp_playfield->valign == VALIGN_MIDDLE)
12013 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12014 else if (vp_playfield->valign == VALIGN_BOTTOM)
12015 vp_playfield->y += playfield_height - vp_playfield->height;
12017 vp_playfield->x += vp_playfield->margin_left;
12018 vp_playfield->y += vp_playfield->margin_top;
12020 // adjust individual playfield borders if only default border is specified
12022 if (vp_playfield->border_left == -1)
12023 vp_playfield->border_left = vp_playfield->border_size;
12024 if (vp_playfield->border_right == -1)
12025 vp_playfield->border_right = vp_playfield->border_size;
12026 if (vp_playfield->border_top == -1)
12027 vp_playfield->border_top = vp_playfield->border_size;
12028 if (vp_playfield->border_bottom == -1)
12029 vp_playfield->border_bottom = vp_playfield->border_size;
12031 // set dynamic playfield borders if borders are specified as undefined
12032 // (but only if window size was dynamic and playfield size was static)
12034 if (dynamic_window_width && !dynamic_playfield_width)
12036 if (vp_playfield->border_left == -1)
12038 vp_playfield->border_left = (vp_playfield->x -
12039 vp_playfield->margin_left);
12040 vp_playfield->x -= vp_playfield->border_left;
12041 vp_playfield->width += vp_playfield->border_left;
12044 if (vp_playfield->border_right == -1)
12046 vp_playfield->border_right = (vp_window->width -
12048 vp_playfield->width -
12049 vp_playfield->margin_right);
12050 vp_playfield->width += vp_playfield->border_right;
12054 if (dynamic_window_height && !dynamic_playfield_height)
12056 if (vp_playfield->border_top == -1)
12058 vp_playfield->border_top = (vp_playfield->y -
12059 vp_playfield->margin_top);
12060 vp_playfield->y -= vp_playfield->border_top;
12061 vp_playfield->height += vp_playfield->border_top;
12064 if (vp_playfield->border_bottom == -1)
12066 vp_playfield->border_bottom = (vp_window->height -
12068 vp_playfield->height -
12069 vp_playfield->margin_bottom);
12070 vp_playfield->height += vp_playfield->border_bottom;
12074 // adjust playfield size to be a multiple of a defined alignment tile size
12076 int align_size = vp_playfield->align_size;
12077 int playfield_xtiles = vp_playfield->width / align_size;
12078 int playfield_ytiles = vp_playfield->height / align_size;
12079 int playfield_width_corrected = playfield_xtiles * align_size;
12080 int playfield_height_corrected = playfield_ytiles * align_size;
12081 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12082 i == GFX_SPECIAL_ARG_EDITOR);
12084 if (is_playfield_mode &&
12085 dynamic_playfield_width &&
12086 vp_playfield->width != playfield_width_corrected)
12088 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12090 vp_playfield->width = playfield_width_corrected;
12092 if (vp_playfield->align == ALIGN_LEFT)
12094 vp_playfield->border_left += playfield_xdiff;
12096 else if (vp_playfield->align == ALIGN_RIGHT)
12098 vp_playfield->border_right += playfield_xdiff;
12100 else if (vp_playfield->align == ALIGN_CENTER)
12102 int border_left_diff = playfield_xdiff / 2;
12103 int border_right_diff = playfield_xdiff - border_left_diff;
12105 vp_playfield->border_left += border_left_diff;
12106 vp_playfield->border_right += border_right_diff;
12110 if (is_playfield_mode &&
12111 dynamic_playfield_height &&
12112 vp_playfield->height != playfield_height_corrected)
12114 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12116 vp_playfield->height = playfield_height_corrected;
12118 if (vp_playfield->valign == VALIGN_TOP)
12120 vp_playfield->border_top += playfield_ydiff;
12122 else if (vp_playfield->align == VALIGN_BOTTOM)
12124 vp_playfield->border_right += playfield_ydiff;
12126 else if (vp_playfield->align == VALIGN_MIDDLE)
12128 int border_top_diff = playfield_ydiff / 2;
12129 int border_bottom_diff = playfield_ydiff - border_top_diff;
12131 vp_playfield->border_top += border_top_diff;
12132 vp_playfield->border_bottom += border_bottom_diff;
12136 // adjust door positions according to specified alignment
12138 for (j = 0; j < 2; j++)
12140 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12142 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12143 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12144 else if (vp_door->align == ALIGN_CENTER)
12145 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12146 else if (vp_door->align == ALIGN_RIGHT)
12147 vp_door->x += vp_window->width - vp_door->width;
12149 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12150 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12151 else if (vp_door->valign == VALIGN_MIDDLE)
12152 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12153 else if (vp_door->valign == VALIGN_BOTTOM)
12154 vp_door->y += vp_window->height - vp_door->height;
12159 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12163 struct XYTileSize *dst, *src;
12166 editor_buttons_xy[] =
12169 &editor.button.element_left, &editor.palette.element_left,
12170 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12173 &editor.button.element_middle, &editor.palette.element_middle,
12174 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12177 &editor.button.element_right, &editor.palette.element_right,
12178 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12185 // set default position for element buttons to element graphics
12186 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12188 if ((*editor_buttons_xy[i].dst).x == -1 &&
12189 (*editor_buttons_xy[i].dst).y == -1)
12191 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12193 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12195 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12199 // adjust editor palette rows and columns if specified to be dynamic
12201 if (editor.palette.cols == -1)
12203 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12204 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12205 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12207 editor.palette.cols = (vp_width - sc_width) / bt_width;
12209 if (editor.palette.x == -1)
12211 int palette_width = editor.palette.cols * bt_width + sc_width;
12213 editor.palette.x = (vp_width - palette_width) / 2;
12217 if (editor.palette.rows == -1)
12219 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12220 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12221 int tx_height = getFontHeight(FONT_TEXT_2);
12223 editor.palette.rows = (vp_height - tx_height) / bt_height;
12225 if (editor.palette.y == -1)
12227 int palette_height = editor.palette.rows * bt_height + tx_height;
12229 editor.palette.y = (vp_height - palette_height) / 2;
12234 static void LoadMenuDesignSettingsFromFilename(char *filename)
12236 static struct TitleFadingInfo tfi;
12237 static struct TitleMessageInfo tmi;
12238 static struct TokenInfo title_tokens[] =
12240 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12241 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12242 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12243 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12244 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12248 static struct TokenInfo titlemessage_tokens[] =
12250 { TYPE_INTEGER, &tmi.x, ".x" },
12251 { TYPE_INTEGER, &tmi.y, ".y" },
12252 { TYPE_INTEGER, &tmi.width, ".width" },
12253 { TYPE_INTEGER, &tmi.height, ".height" },
12254 { TYPE_INTEGER, &tmi.chars, ".chars" },
12255 { TYPE_INTEGER, &tmi.lines, ".lines" },
12256 { TYPE_INTEGER, &tmi.align, ".align" },
12257 { TYPE_INTEGER, &tmi.valign, ".valign" },
12258 { TYPE_INTEGER, &tmi.font, ".font" },
12259 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12260 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12261 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12262 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12263 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12264 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12265 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12266 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12267 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12273 struct TitleFadingInfo *info;
12278 // initialize first titles from "enter screen" definitions, if defined
12279 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12280 { &title_first_default, "menu.enter_screen.TITLE" },
12282 // initialize title screens from "next screen" definitions, if defined
12283 { &title_initial_default, "menu.next_screen.TITLE" },
12284 { &title_default, "menu.next_screen.TITLE" },
12290 struct TitleMessageInfo *array;
12293 titlemessage_arrays[] =
12295 // initialize first titles from "enter screen" definitions, if defined
12296 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12297 { titlescreen_first, "menu.enter_screen.TITLE" },
12298 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12299 { titlemessage_first, "menu.enter_screen.TITLE" },
12301 // initialize titles from "next screen" definitions, if defined
12302 { titlescreen_initial, "menu.next_screen.TITLE" },
12303 { titlescreen, "menu.next_screen.TITLE" },
12304 { titlemessage_initial, "menu.next_screen.TITLE" },
12305 { titlemessage, "menu.next_screen.TITLE" },
12307 // overwrite titles with title definitions, if defined
12308 { titlescreen_initial_first, "[title_initial]" },
12309 { titlescreen_first, "[title]" },
12310 { titlemessage_initial_first, "[title_initial]" },
12311 { titlemessage_first, "[title]" },
12313 { titlescreen_initial, "[title_initial]" },
12314 { titlescreen, "[title]" },
12315 { titlemessage_initial, "[title_initial]" },
12316 { titlemessage, "[title]" },
12318 // overwrite titles with title screen/message definitions, if defined
12319 { titlescreen_initial_first, "[titlescreen_initial]" },
12320 { titlescreen_first, "[titlescreen]" },
12321 { titlemessage_initial_first, "[titlemessage_initial]" },
12322 { titlemessage_first, "[titlemessage]" },
12324 { titlescreen_initial, "[titlescreen_initial]" },
12325 { titlescreen, "[titlescreen]" },
12326 { titlemessage_initial, "[titlemessage_initial]" },
12327 { titlemessage, "[titlemessage]" },
12331 SetupFileHash *setup_file_hash;
12334 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12337 // the following initializes hierarchical values from dynamic configuration
12339 // special case: initialize with default values that may be overwritten
12340 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12341 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12343 struct TokenIntPtrInfo menu_config[] =
12345 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12346 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12347 { "menu.list_size", &menu.list_size[i] }
12350 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12352 char *token = menu_config[j].token;
12353 char *value = getHashEntry(setup_file_hash, token);
12356 *menu_config[j].value = get_integer_from_string(value);
12360 // special case: initialize with default values that may be overwritten
12361 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12362 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12364 struct TokenIntPtrInfo menu_config[] =
12366 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12367 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12368 { "menu.list_size.INFO", &menu.list_size_info[i] }
12371 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12373 char *token = menu_config[j].token;
12374 char *value = getHashEntry(setup_file_hash, token);
12377 *menu_config[j].value = get_integer_from_string(value);
12381 // special case: initialize with default values that may be overwritten
12382 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12383 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12385 struct TokenIntPtrInfo menu_config[] =
12387 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12388 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12391 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12393 char *token = menu_config[j].token;
12394 char *value = getHashEntry(setup_file_hash, token);
12397 *menu_config[j].value = get_integer_from_string(value);
12401 // special case: initialize with default values that may be overwritten
12402 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12403 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12405 struct TokenIntPtrInfo menu_config[] =
12407 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12408 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12409 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12410 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12411 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12412 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12413 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12414 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12415 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12418 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12420 char *token = menu_config[j].token;
12421 char *value = getHashEntry(setup_file_hash, token);
12424 *menu_config[j].value = get_integer_from_string(value);
12428 // special case: initialize with default values that may be overwritten
12429 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12430 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12432 struct TokenIntPtrInfo menu_config[] =
12434 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12435 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12436 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12437 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12438 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12439 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12440 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12441 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12442 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12445 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12447 char *token = menu_config[j].token;
12448 char *value = getHashEntry(setup_file_hash, token);
12451 *menu_config[j].value = get_token_parameter_value(token, value);
12455 // special case: initialize with default values that may be overwritten
12456 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12457 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12461 char *token_prefix;
12462 struct RectWithBorder *struct_ptr;
12466 { "viewport.window", &viewport.window[i] },
12467 { "viewport.playfield", &viewport.playfield[i] },
12468 { "viewport.door_1", &viewport.door_1[i] },
12469 { "viewport.door_2", &viewport.door_2[i] }
12472 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12474 struct TokenIntPtrInfo vp_config[] =
12476 { ".x", &vp_struct[j].struct_ptr->x },
12477 { ".y", &vp_struct[j].struct_ptr->y },
12478 { ".width", &vp_struct[j].struct_ptr->width },
12479 { ".height", &vp_struct[j].struct_ptr->height },
12480 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12481 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12482 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12483 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12484 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12485 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12486 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12487 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12488 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12489 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12490 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12491 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12492 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12493 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12494 { ".align", &vp_struct[j].struct_ptr->align },
12495 { ".valign", &vp_struct[j].struct_ptr->valign }
12498 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12500 char *token = getStringCat2(vp_struct[j].token_prefix,
12501 vp_config[k].token);
12502 char *value = getHashEntry(setup_file_hash, token);
12505 *vp_config[k].value = get_token_parameter_value(token, value);
12512 // special case: initialize with default values that may be overwritten
12513 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12514 for (i = 0; title_info[i].info != NULL; i++)
12516 struct TitleFadingInfo *info = title_info[i].info;
12517 char *base_token = title_info[i].text;
12519 for (j = 0; title_tokens[j].type != -1; j++)
12521 char *token = getStringCat2(base_token, title_tokens[j].text);
12522 char *value = getHashEntry(setup_file_hash, token);
12526 int parameter_value = get_token_parameter_value(token, value);
12530 *(int *)title_tokens[j].value = (int)parameter_value;
12539 // special case: initialize with default values that may be overwritten
12540 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12541 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12543 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12544 char *base_token = titlemessage_arrays[i].text;
12546 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12548 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12549 char *value = getHashEntry(setup_file_hash, token);
12553 int parameter_value = get_token_parameter_value(token, value);
12555 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12559 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12560 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12562 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12572 // special case: check if network and preview player positions are redefined,
12573 // to compare this later against the main menu level preview being redefined
12574 struct TokenIntPtrInfo menu_config_players[] =
12576 { "main.network_players.x", &menu.main.network_players.redefined },
12577 { "main.network_players.y", &menu.main.network_players.redefined },
12578 { "main.preview_players.x", &menu.main.preview_players.redefined },
12579 { "main.preview_players.y", &menu.main.preview_players.redefined },
12580 { "preview.x", &preview.redefined },
12581 { "preview.y", &preview.redefined }
12584 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12585 *menu_config_players[i].value = FALSE;
12587 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12588 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12589 *menu_config_players[i].value = TRUE;
12591 // read (and overwrite with) values that may be specified in config file
12592 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12594 freeSetupFileHash(setup_file_hash);
12597 void LoadMenuDesignSettings(void)
12599 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12601 InitMenuDesignSettings_Static();
12602 InitMenuDesignSettings_SpecialPreProcessing();
12604 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12606 // first look for special settings configured in level series config
12607 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12609 if (fileExists(filename_base))
12610 LoadMenuDesignSettingsFromFilename(filename_base);
12613 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12615 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12616 LoadMenuDesignSettingsFromFilename(filename_local);
12618 InitMenuDesignSettings_SpecialPostProcessing();
12621 void LoadMenuDesignSettings_AfterGraphics(void)
12623 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12626 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12628 char *filename = getEditorSetupFilename();
12629 SetupFileList *setup_file_list, *list;
12630 SetupFileHash *element_hash;
12631 int num_unknown_tokens = 0;
12634 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12637 element_hash = newSetupFileHash();
12639 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12640 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12642 // determined size may be larger than needed (due to unknown elements)
12644 for (list = setup_file_list; list != NULL; list = list->next)
12647 // add space for up to 3 more elements for padding that may be needed
12648 *num_elements += 3;
12650 // free memory for old list of elements, if needed
12651 checked_free(*elements);
12653 // allocate memory for new list of elements
12654 *elements = checked_malloc(*num_elements * sizeof(int));
12657 for (list = setup_file_list; list != NULL; list = list->next)
12659 char *value = getHashEntry(element_hash, list->token);
12661 if (value == NULL) // try to find obsolete token mapping
12663 char *mapped_token = get_mapped_token(list->token);
12665 if (mapped_token != NULL)
12667 value = getHashEntry(element_hash, mapped_token);
12669 free(mapped_token);
12675 (*elements)[(*num_elements)++] = atoi(value);
12679 if (num_unknown_tokens == 0)
12682 Warn("unknown token(s) found in config file:");
12683 Warn("- config file: '%s'", filename);
12685 num_unknown_tokens++;
12688 Warn("- token: '%s'", list->token);
12692 if (num_unknown_tokens > 0)
12695 while (*num_elements % 4) // pad with empty elements, if needed
12696 (*elements)[(*num_elements)++] = EL_EMPTY;
12698 freeSetupFileList(setup_file_list);
12699 freeSetupFileHash(element_hash);
12702 for (i = 0; i < *num_elements; i++)
12703 Debug("editor", "element '%s' [%d]\n",
12704 element_info[(*elements)[i]].token_name, (*elements)[i]);
12708 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12711 SetupFileHash *setup_file_hash = NULL;
12712 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12713 char *filename_music, *filename_prefix, *filename_info;
12719 token_to_value_ptr[] =
12721 { "title_header", &tmp_music_file_info.title_header },
12722 { "artist_header", &tmp_music_file_info.artist_header },
12723 { "album_header", &tmp_music_file_info.album_header },
12724 { "year_header", &tmp_music_file_info.year_header },
12726 { "title", &tmp_music_file_info.title },
12727 { "artist", &tmp_music_file_info.artist },
12728 { "album", &tmp_music_file_info.album },
12729 { "year", &tmp_music_file_info.year },
12735 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12736 getCustomMusicFilename(basename));
12738 if (filename_music == NULL)
12741 // ---------- try to replace file extension ----------
12743 filename_prefix = getStringCopy(filename_music);
12744 if (strrchr(filename_prefix, '.') != NULL)
12745 *strrchr(filename_prefix, '.') = '\0';
12746 filename_info = getStringCat2(filename_prefix, ".txt");
12748 if (fileExists(filename_info))
12749 setup_file_hash = loadSetupFileHash(filename_info);
12751 free(filename_prefix);
12752 free(filename_info);
12754 if (setup_file_hash == NULL)
12756 // ---------- try to add file extension ----------
12758 filename_prefix = getStringCopy(filename_music);
12759 filename_info = getStringCat2(filename_prefix, ".txt");
12761 if (fileExists(filename_info))
12762 setup_file_hash = loadSetupFileHash(filename_info);
12764 free(filename_prefix);
12765 free(filename_info);
12768 if (setup_file_hash == NULL)
12771 // ---------- music file info found ----------
12773 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12775 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12777 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12779 *token_to_value_ptr[i].value_ptr =
12780 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12783 tmp_music_file_info.basename = getStringCopy(basename);
12784 tmp_music_file_info.music = music;
12785 tmp_music_file_info.is_sound = is_sound;
12787 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12788 *new_music_file_info = tmp_music_file_info;
12790 return new_music_file_info;
12793 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12795 return get_music_file_info_ext(basename, music, FALSE);
12798 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12800 return get_music_file_info_ext(basename, sound, TRUE);
12803 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12804 char *basename, boolean is_sound)
12806 for (; list != NULL; list = list->next)
12807 if (list->is_sound == is_sound && strEqual(list->basename, basename))
12813 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12815 return music_info_listed_ext(list, basename, FALSE);
12818 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12820 return music_info_listed_ext(list, basename, TRUE);
12823 void LoadMusicInfo(void)
12825 char *music_directory = getCustomMusicDirectory();
12826 int num_music = getMusicListSize();
12827 int num_music_noconf = 0;
12828 int num_sounds = getSoundListSize();
12830 DirectoryEntry *dir_entry;
12831 struct FileInfo *music, *sound;
12832 struct MusicFileInfo *next, **new;
12835 while (music_file_info != NULL)
12837 next = music_file_info->next;
12839 checked_free(music_file_info->basename);
12841 checked_free(music_file_info->title_header);
12842 checked_free(music_file_info->artist_header);
12843 checked_free(music_file_info->album_header);
12844 checked_free(music_file_info->year_header);
12846 checked_free(music_file_info->title);
12847 checked_free(music_file_info->artist);
12848 checked_free(music_file_info->album);
12849 checked_free(music_file_info->year);
12851 free(music_file_info);
12853 music_file_info = next;
12856 new = &music_file_info;
12858 for (i = 0; i < num_music; i++)
12860 music = getMusicListEntry(i);
12862 if (music->filename == NULL)
12865 if (strEqual(music->filename, UNDEFINED_FILENAME))
12868 // a configured file may be not recognized as music
12869 if (!FileIsMusic(music->filename))
12872 if (!music_info_listed(music_file_info, music->filename))
12874 *new = get_music_file_info(music->filename, i);
12877 new = &(*new)->next;
12881 if ((dir = openDirectory(music_directory)) == NULL)
12883 Warn("cannot read music directory '%s'", music_directory);
12888 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12890 char *basename = dir_entry->basename;
12891 boolean music_already_used = FALSE;
12894 // skip all music files that are configured in music config file
12895 for (i = 0; i < num_music; i++)
12897 music = getMusicListEntry(i);
12899 if (music->filename == NULL)
12902 if (strEqual(basename, music->filename))
12904 music_already_used = TRUE;
12909 if (music_already_used)
12912 if (!FileIsMusic(dir_entry->filename))
12915 if (!music_info_listed(music_file_info, basename))
12917 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12920 new = &(*new)->next;
12923 num_music_noconf++;
12926 closeDirectory(dir);
12928 for (i = 0; i < num_sounds; i++)
12930 sound = getSoundListEntry(i);
12932 if (sound->filename == NULL)
12935 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12938 // a configured file may be not recognized as sound
12939 if (!FileIsSound(sound->filename))
12942 if (!sound_info_listed(music_file_info, sound->filename))
12944 *new = get_sound_file_info(sound->filename, i);
12946 new = &(*new)->next;
12950 // add pointers to previous list nodes
12952 struct MusicFileInfo *node = music_file_info;
12954 while (node != NULL)
12957 node->next->prev = node;
12963 static void add_helpanim_entry(int element, int action, int direction,
12964 int delay, int *num_list_entries)
12966 struct HelpAnimInfo *new_list_entry;
12967 (*num_list_entries)++;
12970 checked_realloc(helpanim_info,
12971 *num_list_entries * sizeof(struct HelpAnimInfo));
12972 new_list_entry = &helpanim_info[*num_list_entries - 1];
12974 new_list_entry->element = element;
12975 new_list_entry->action = action;
12976 new_list_entry->direction = direction;
12977 new_list_entry->delay = delay;
12980 static void print_unknown_token(char *filename, char *token, int token_nr)
12985 Warn("unknown token(s) found in config file:");
12986 Warn("- config file: '%s'", filename);
12989 Warn("- token: '%s'", token);
12992 static void print_unknown_token_end(int token_nr)
12998 void LoadHelpAnimInfo(void)
13000 char *filename = getHelpAnimFilename();
13001 SetupFileList *setup_file_list = NULL, *list;
13002 SetupFileHash *element_hash, *action_hash, *direction_hash;
13003 int num_list_entries = 0;
13004 int num_unknown_tokens = 0;
13007 if (fileExists(filename))
13008 setup_file_list = loadSetupFileList(filename);
13010 if (setup_file_list == NULL)
13012 // use reliable default values from static configuration
13013 SetupFileList *insert_ptr;
13015 insert_ptr = setup_file_list =
13016 newSetupFileList(helpanim_config[0].token,
13017 helpanim_config[0].value);
13019 for (i = 1; helpanim_config[i].token; i++)
13020 insert_ptr = addListEntry(insert_ptr,
13021 helpanim_config[i].token,
13022 helpanim_config[i].value);
13025 element_hash = newSetupFileHash();
13026 action_hash = newSetupFileHash();
13027 direction_hash = newSetupFileHash();
13029 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13030 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13032 for (i = 0; i < NUM_ACTIONS; i++)
13033 setHashEntry(action_hash, element_action_info[i].suffix,
13034 i_to_a(element_action_info[i].value));
13036 // do not store direction index (bit) here, but direction value!
13037 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13038 setHashEntry(direction_hash, element_direction_info[i].suffix,
13039 i_to_a(1 << element_direction_info[i].value));
13041 for (list = setup_file_list; list != NULL; list = list->next)
13043 char *element_token, *action_token, *direction_token;
13044 char *element_value, *action_value, *direction_value;
13045 int delay = atoi(list->value);
13047 if (strEqual(list->token, "end"))
13049 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13054 /* first try to break element into element/action/direction parts;
13055 if this does not work, also accept combined "element[.act][.dir]"
13056 elements (like "dynamite.active"), which are unique elements */
13058 if (strchr(list->token, '.') == NULL) // token contains no '.'
13060 element_value = getHashEntry(element_hash, list->token);
13061 if (element_value != NULL) // element found
13062 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13063 &num_list_entries);
13066 // no further suffixes found -- this is not an element
13067 print_unknown_token(filename, list->token, num_unknown_tokens++);
13073 // token has format "<prefix>.<something>"
13075 action_token = strchr(list->token, '.'); // suffix may be action ...
13076 direction_token = action_token; // ... or direction
13078 element_token = getStringCopy(list->token);
13079 *strchr(element_token, '.') = '\0';
13081 element_value = getHashEntry(element_hash, element_token);
13083 if (element_value == NULL) // this is no element
13085 element_value = getHashEntry(element_hash, list->token);
13086 if (element_value != NULL) // combined element found
13087 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13088 &num_list_entries);
13090 print_unknown_token(filename, list->token, num_unknown_tokens++);
13092 free(element_token);
13097 action_value = getHashEntry(action_hash, action_token);
13099 if (action_value != NULL) // action found
13101 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13102 &num_list_entries);
13104 free(element_token);
13109 direction_value = getHashEntry(direction_hash, direction_token);
13111 if (direction_value != NULL) // direction found
13113 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13114 &num_list_entries);
13116 free(element_token);
13121 if (strchr(action_token + 1, '.') == NULL)
13123 // no further suffixes found -- this is not an action nor direction
13125 element_value = getHashEntry(element_hash, list->token);
13126 if (element_value != NULL) // combined element found
13127 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13128 &num_list_entries);
13130 print_unknown_token(filename, list->token, num_unknown_tokens++);
13132 free(element_token);
13137 // token has format "<prefix>.<suffix>.<something>"
13139 direction_token = strchr(action_token + 1, '.');
13141 action_token = getStringCopy(action_token);
13142 *strchr(action_token + 1, '.') = '\0';
13144 action_value = getHashEntry(action_hash, action_token);
13146 if (action_value == NULL) // this is no action
13148 element_value = getHashEntry(element_hash, list->token);
13149 if (element_value != NULL) // combined element found
13150 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13151 &num_list_entries);
13153 print_unknown_token(filename, list->token, num_unknown_tokens++);
13155 free(element_token);
13156 free(action_token);
13161 direction_value = getHashEntry(direction_hash, direction_token);
13163 if (direction_value != NULL) // direction found
13165 add_helpanim_entry(atoi(element_value), atoi(action_value),
13166 atoi(direction_value), delay, &num_list_entries);
13168 free(element_token);
13169 free(action_token);
13174 // this is no direction
13176 element_value = getHashEntry(element_hash, list->token);
13177 if (element_value != NULL) // combined element found
13178 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13179 &num_list_entries);
13181 print_unknown_token(filename, list->token, num_unknown_tokens++);
13183 free(element_token);
13184 free(action_token);
13187 print_unknown_token_end(num_unknown_tokens);
13189 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13190 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13192 freeSetupFileList(setup_file_list);
13193 freeSetupFileHash(element_hash);
13194 freeSetupFileHash(action_hash);
13195 freeSetupFileHash(direction_hash);
13198 for (i = 0; i < num_list_entries; i++)
13199 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13200 EL_NAME(helpanim_info[i].element),
13201 helpanim_info[i].element,
13202 helpanim_info[i].action,
13203 helpanim_info[i].direction,
13204 helpanim_info[i].delay);
13208 void LoadHelpTextInfo(void)
13210 char *filename = getHelpTextFilename();
13213 if (helptext_info != NULL)
13215 freeSetupFileHash(helptext_info);
13216 helptext_info = NULL;
13219 if (fileExists(filename))
13220 helptext_info = loadSetupFileHash(filename);
13222 if (helptext_info == NULL)
13224 // use reliable default values from static configuration
13225 helptext_info = newSetupFileHash();
13227 for (i = 0; helptext_config[i].token; i++)
13228 setHashEntry(helptext_info,
13229 helptext_config[i].token,
13230 helptext_config[i].value);
13234 BEGIN_HASH_ITERATION(helptext_info, itr)
13236 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13237 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13239 END_HASH_ITERATION(hash, itr)
13244 // ----------------------------------------------------------------------------
13246 // ----------------------------------------------------------------------------
13248 #define MAX_NUM_CONVERT_LEVELS 1000
13250 void ConvertLevels(void)
13252 static LevelDirTree *convert_leveldir = NULL;
13253 static int convert_level_nr = -1;
13254 static int num_levels_handled = 0;
13255 static int num_levels_converted = 0;
13256 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13259 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13260 global.convert_leveldir);
13262 if (convert_leveldir == NULL)
13263 Fail("no such level identifier: '%s'", global.convert_leveldir);
13265 leveldir_current = convert_leveldir;
13267 if (global.convert_level_nr != -1)
13269 convert_leveldir->first_level = global.convert_level_nr;
13270 convert_leveldir->last_level = global.convert_level_nr;
13273 convert_level_nr = convert_leveldir->first_level;
13275 PrintLine("=", 79);
13276 Print("Converting levels\n");
13277 PrintLine("-", 79);
13278 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13279 Print("Level series name: '%s'\n", convert_leveldir->name);
13280 Print("Level series author: '%s'\n", convert_leveldir->author);
13281 Print("Number of levels: %d\n", convert_leveldir->levels);
13282 PrintLine("=", 79);
13285 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13286 levels_failed[i] = FALSE;
13288 while (convert_level_nr <= convert_leveldir->last_level)
13290 char *level_filename;
13293 level_nr = convert_level_nr++;
13295 Print("Level %03d: ", level_nr);
13297 LoadLevel(level_nr);
13298 if (level.no_level_file || level.no_valid_file)
13300 Print("(no level)\n");
13304 Print("converting level ... ");
13307 // special case: conversion of some EMC levels as requested by ACME
13308 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13311 level_filename = getDefaultLevelFilename(level_nr);
13312 new_level = !fileExists(level_filename);
13316 SaveLevel(level_nr);
13318 num_levels_converted++;
13320 Print("converted.\n");
13324 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13325 levels_failed[level_nr] = TRUE;
13327 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13330 num_levels_handled++;
13334 PrintLine("=", 79);
13335 Print("Number of levels handled: %d\n", num_levels_handled);
13336 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13337 (num_levels_handled ?
13338 num_levels_converted * 100 / num_levels_handled : 0));
13339 PrintLine("-", 79);
13340 Print("Summary (for automatic parsing by scripts):\n");
13341 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13342 convert_leveldir->identifier, num_levels_converted,
13343 num_levels_handled,
13344 (num_levels_handled ?
13345 num_levels_converted * 100 / num_levels_handled : 0));
13347 if (num_levels_handled != num_levels_converted)
13349 Print(", FAILED:");
13350 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13351 if (levels_failed[i])
13356 PrintLine("=", 79);
13358 CloseAllAndExit(0);
13362 // ----------------------------------------------------------------------------
13363 // create and save images for use in level sketches (raw BMP format)
13364 // ----------------------------------------------------------------------------
13366 void CreateLevelSketchImages(void)
13372 InitElementPropertiesGfxElement();
13374 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13375 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13377 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13379 int element = getMappedElement(i);
13380 char basename1[16];
13381 char basename2[16];
13385 sprintf(basename1, "%04d.bmp", i);
13386 sprintf(basename2, "%04ds.bmp", i);
13388 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13389 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13391 DrawSizedElement(0, 0, element, TILESIZE);
13392 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13394 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13395 Fail("cannot save level sketch image file '%s'", filename1);
13397 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13398 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13400 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13401 Fail("cannot save level sketch image file '%s'", filename2);
13406 // create corresponding SQL statements (for normal and small images)
13409 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13410 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13413 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13414 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13416 // optional: create content for forum level sketch demonstration post
13418 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13421 FreeBitmap(bitmap1);
13422 FreeBitmap(bitmap2);
13425 fprintf(stderr, "\n");
13427 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13429 CloseAllAndExit(0);
13433 // ----------------------------------------------------------------------------
13434 // create and save images for element collecting animations (raw BMP format)
13435 // ----------------------------------------------------------------------------
13437 static boolean createCollectImage(int element)
13439 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13442 void CreateCollectElementImages(void)
13446 int anim_frames = num_steps - 1;
13447 int tile_size = TILESIZE;
13448 int anim_width = tile_size * anim_frames;
13449 int anim_height = tile_size;
13450 int num_collect_images = 0;
13451 int pos_collect_images = 0;
13453 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13454 if (createCollectImage(i))
13455 num_collect_images++;
13457 Info("Creating %d element collecting animation images ...",
13458 num_collect_images);
13460 int dst_width = anim_width * 2;
13461 int dst_height = anim_height * num_collect_images / 2;
13462 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13463 char *basename_bmp = "RocksCollect.bmp";
13464 char *basename_png = "RocksCollect.png";
13465 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13466 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13467 int len_filename_bmp = strlen(filename_bmp);
13468 int len_filename_png = strlen(filename_png);
13469 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13470 char cmd_convert[max_command_len];
13472 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13476 // force using RGBA surface for destination bitmap
13477 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13478 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13480 dst_bitmap->surface =
13481 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_RGBA32, 0);
13483 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13485 if (!createCollectImage(i))
13488 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13489 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13490 int graphic = el2img(i);
13491 char *token_name = element_info[i].token_name;
13492 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13493 Bitmap *src_bitmap;
13496 Info("- creating collecting image for '%s' ...", token_name);
13498 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13500 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13501 tile_size, tile_size, 0, 0);
13503 // force using RGBA surface for temporary bitmap (using transparent black)
13504 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13505 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13507 tmp_bitmap->surface =
13508 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_RGBA32, 0);
13510 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13512 for (j = 0; j < anim_frames; j++)
13514 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13515 int frame_size = frame_size_final * num_steps;
13516 int offset = (tile_size - frame_size_final) / 2;
13517 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13519 while (frame_size > frame_size_final)
13523 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13525 FreeBitmap(frame_bitmap);
13527 frame_bitmap = half_bitmap;
13530 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13531 frame_size_final, frame_size_final,
13532 dst_x + j * tile_size + offset, dst_y + offset);
13534 FreeBitmap(frame_bitmap);
13537 tmp_bitmap->surface_masked = NULL;
13539 FreeBitmap(tmp_bitmap);
13541 pos_collect_images++;
13544 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13545 Fail("cannot save element collecting image file '%s'", filename_bmp);
13547 FreeBitmap(dst_bitmap);
13549 Info("Converting image file from BMP to PNG ...");
13551 system(cmd_convert);
13552 unlink(filename_bmp);
13556 CloseAllAndExit(0);
13560 // ----------------------------------------------------------------------------
13561 // create and save images for custom and group elements (raw BMP format)
13562 // ----------------------------------------------------------------------------
13564 void CreateCustomElementImages(char *directory)
13566 char *src_basename = "RocksCE-template.ilbm";
13567 char *dst_basename = "RocksCE.bmp";
13568 char *src_filename = getPath2(directory, src_basename);
13569 char *dst_filename = getPath2(directory, dst_basename);
13570 Bitmap *src_bitmap;
13572 int yoffset_ce = 0;
13573 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13576 InitVideoDefaults();
13578 ReCreateBitmap(&backbuffer, video.width, video.height);
13580 src_bitmap = LoadImage(src_filename);
13582 bitmap = CreateBitmap(TILEX * 16 * 2,
13583 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13586 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13593 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13594 TILEX * x, TILEY * y + yoffset_ce);
13596 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13598 TILEX * x + TILEX * 16,
13599 TILEY * y + yoffset_ce);
13601 for (j = 2; j >= 0; j--)
13605 BlitBitmap(src_bitmap, bitmap,
13606 TILEX + c * 7, 0, 6, 10,
13607 TILEX * x + 6 + j * 7,
13608 TILEY * y + 11 + yoffset_ce);
13610 BlitBitmap(src_bitmap, bitmap,
13611 TILEX + c * 8, TILEY, 6, 10,
13612 TILEX * 16 + TILEX * x + 6 + j * 8,
13613 TILEY * y + 10 + yoffset_ce);
13619 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13626 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13627 TILEX * x, TILEY * y + yoffset_ge);
13629 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13631 TILEX * x + TILEX * 16,
13632 TILEY * y + yoffset_ge);
13634 for (j = 1; j >= 0; j--)
13638 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13639 TILEX * x + 6 + j * 10,
13640 TILEY * y + 11 + yoffset_ge);
13642 BlitBitmap(src_bitmap, bitmap,
13643 TILEX + c * 8, TILEY + 12, 6, 10,
13644 TILEX * 16 + TILEX * x + 10 + j * 8,
13645 TILEY * y + 10 + yoffset_ge);
13651 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13652 Fail("cannot save CE graphics file '%s'", dst_filename);
13654 FreeBitmap(bitmap);
13656 CloseAllAndExit(0);