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;
3391 xx_ei = *ei; // copy element data into temporary buffer
3392 xx_group = *group; // copy group data into temporary buffer
3394 while (!checkEndOfFile(file))
3396 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3399 if (real_chunk_size >= chunk_size)
3406 level->file_has_custom_elements = TRUE;
3408 return real_chunk_size;
3411 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3413 int element = getMappedElement(getFile16BitBE(file));
3414 int real_chunk_size = 2;
3415 struct ElementInfo *ei = &element_info[element];
3417 xx_ei = *ei; // copy element data into temporary buffer
3419 while (!checkEndOfFile(file))
3421 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3424 if (real_chunk_size >= chunk_size)
3430 level->file_has_custom_elements = TRUE;
3432 return real_chunk_size;
3435 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3436 struct LevelFileInfo *level_file_info,
3437 boolean level_info_only)
3439 char *filename = level_file_info->filename;
3440 char cookie[MAX_LINE_LEN];
3441 char chunk_name[CHUNK_ID_LEN + 1];
3445 if (!(file = openFile(filename, MODE_READ)))
3447 level->no_valid_file = TRUE;
3448 level->no_level_file = TRUE;
3450 if (level_info_only)
3453 Warn("cannot read level '%s' -- using empty level", filename);
3455 if (!setup.editor.use_template_for_new_levels)
3458 // if level file not found, try to initialize level data from template
3459 filename = getGlobalLevelTemplateFilename();
3461 if (!(file = openFile(filename, MODE_READ)))
3464 // default: for empty levels, use level template for custom elements
3465 level->use_custom_template = TRUE;
3467 level->no_valid_file = FALSE;
3470 getFileChunkBE(file, chunk_name, NULL);
3471 if (strEqual(chunk_name, "RND1"))
3473 getFile32BitBE(file); // not used
3475 getFileChunkBE(file, chunk_name, NULL);
3476 if (!strEqual(chunk_name, "CAVE"))
3478 level->no_valid_file = TRUE;
3480 Warn("unknown format of level file '%s'", filename);
3487 else // check for pre-2.0 file format with cookie string
3489 strcpy(cookie, chunk_name);
3490 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3492 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3493 cookie[strlen(cookie) - 1] = '\0';
3495 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3497 level->no_valid_file = TRUE;
3499 Warn("unknown format of level file '%s'", filename);
3506 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3508 level->no_valid_file = TRUE;
3510 Warn("unsupported version of level file '%s'", filename);
3517 // pre-2.0 level files have no game version, so use file version here
3518 level->game_version = level->file_version;
3521 if (level->file_version < FILE_VERSION_1_2)
3523 // level files from versions before 1.2.0 without chunk structure
3524 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3525 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3533 int (*loader)(File *, int, struct LevelInfo *);
3537 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3538 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3539 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3540 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3541 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3542 { "INFO", -1, LoadLevel_INFO },
3543 { "BODY", -1, LoadLevel_BODY },
3544 { "CONT", -1, LoadLevel_CONT },
3545 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3546 { "CNT3", -1, LoadLevel_CNT3 },
3547 { "CUS1", -1, LoadLevel_CUS1 },
3548 { "CUS2", -1, LoadLevel_CUS2 },
3549 { "CUS3", -1, LoadLevel_CUS3 },
3550 { "CUS4", -1, LoadLevel_CUS4 },
3551 { "GRP1", -1, LoadLevel_GRP1 },
3552 { "CONF", -1, LoadLevel_CONF },
3553 { "ELEM", -1, LoadLevel_ELEM },
3554 { "NOTE", -1, LoadLevel_NOTE },
3555 { "CUSX", -1, LoadLevel_CUSX },
3556 { "GRPX", -1, LoadLevel_GRPX },
3557 { "EMPX", -1, LoadLevel_EMPX },
3562 while (getFileChunkBE(file, chunk_name, &chunk_size))
3566 while (chunk_info[i].name != NULL &&
3567 !strEqual(chunk_name, chunk_info[i].name))
3570 if (chunk_info[i].name == NULL)
3572 Warn("unknown chunk '%s' in level file '%s'",
3573 chunk_name, filename);
3575 ReadUnusedBytesFromFile(file, chunk_size);
3577 else if (chunk_info[i].size != -1 &&
3578 chunk_info[i].size != chunk_size)
3580 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3581 chunk_size, chunk_name, filename);
3583 ReadUnusedBytesFromFile(file, chunk_size);
3587 // call function to load this level chunk
3588 int chunk_size_expected =
3589 (chunk_info[i].loader)(file, chunk_size, level);
3591 // the size of some chunks cannot be checked before reading other
3592 // chunks first (like "HEAD" and "BODY") that contain some header
3593 // information, so check them here
3594 if (chunk_size_expected != chunk_size)
3596 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3597 chunk_size, chunk_name, filename);
3607 // ----------------------------------------------------------------------------
3608 // functions for loading EM level
3609 // ----------------------------------------------------------------------------
3611 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3613 static int ball_xy[8][2] =
3624 struct LevelInfo_EM *level_em = level->native_em_level;
3625 struct CAVE *cav = level_em->cav;
3628 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3629 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3631 cav->time_seconds = level->time;
3632 cav->gems_needed = level->gems_needed;
3634 cav->emerald_score = level->score[SC_EMERALD];
3635 cav->diamond_score = level->score[SC_DIAMOND];
3636 cav->alien_score = level->score[SC_ROBOT];
3637 cav->tank_score = level->score[SC_SPACESHIP];
3638 cav->bug_score = level->score[SC_BUG];
3639 cav->eater_score = level->score[SC_YAMYAM];
3640 cav->nut_score = level->score[SC_NUT];
3641 cav->dynamite_score = level->score[SC_DYNAMITE];
3642 cav->key_score = level->score[SC_KEY];
3643 cav->exit_score = level->score[SC_TIME_BONUS];
3645 cav->num_eater_arrays = level->num_yamyam_contents;
3647 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3648 for (y = 0; y < 3; y++)
3649 for (x = 0; x < 3; x++)
3650 cav->eater_array[i][y * 3 + x] =
3651 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3653 cav->amoeba_time = level->amoeba_speed;
3654 cav->wonderwall_time = level->time_magic_wall;
3655 cav->wheel_time = level->time_wheel;
3657 cav->android_move_time = level->android_move_time;
3658 cav->android_clone_time = level->android_clone_time;
3659 cav->ball_random = level->ball_random;
3660 cav->ball_active = level->ball_active_initial;
3661 cav->ball_time = level->ball_time;
3662 cav->num_ball_arrays = level->num_ball_contents;
3664 cav->lenses_score = level->lenses_score;
3665 cav->magnify_score = level->magnify_score;
3666 cav->slurp_score = level->slurp_score;
3668 cav->lenses_time = level->lenses_time;
3669 cav->magnify_time = level->magnify_time;
3671 cav->wind_direction =
3672 map_direction_RND_to_EM(level->wind_direction_initial);
3674 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3675 for (j = 0; j < 8; j++)
3676 cav->ball_array[i][j] =
3677 map_element_RND_to_EM_cave(level->ball_content[i].
3678 e[ball_xy[j][0]][ball_xy[j][1]]);
3680 map_android_clone_elements_RND_to_EM(level);
3682 // first fill the complete playfield with the empty space element
3683 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3684 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3685 cav->cave[x][y] = Cblank;
3687 // then copy the real level contents from level file into the playfield
3688 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3690 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3692 if (level->field[x][y] == EL_AMOEBA_DEAD)
3693 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3695 cav->cave[x][y] = new_element;
3698 for (i = 0; i < MAX_PLAYERS; i++)
3700 cav->player_x[i] = -1;
3701 cav->player_y[i] = -1;
3704 // initialize player positions and delete players from the playfield
3705 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3707 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3709 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3711 cav->player_x[player_nr] = x;
3712 cav->player_y[player_nr] = y;
3714 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3719 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3721 static int ball_xy[8][2] =
3732 struct LevelInfo_EM *level_em = level->native_em_level;
3733 struct CAVE *cav = level_em->cav;
3736 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3737 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3739 level->time = cav->time_seconds;
3740 level->gems_needed = cav->gems_needed;
3742 sprintf(level->name, "Level %d", level->file_info.nr);
3744 level->score[SC_EMERALD] = cav->emerald_score;
3745 level->score[SC_DIAMOND] = cav->diamond_score;
3746 level->score[SC_ROBOT] = cav->alien_score;
3747 level->score[SC_SPACESHIP] = cav->tank_score;
3748 level->score[SC_BUG] = cav->bug_score;
3749 level->score[SC_YAMYAM] = cav->eater_score;
3750 level->score[SC_NUT] = cav->nut_score;
3751 level->score[SC_DYNAMITE] = cav->dynamite_score;
3752 level->score[SC_KEY] = cav->key_score;
3753 level->score[SC_TIME_BONUS] = cav->exit_score;
3755 level->num_yamyam_contents = cav->num_eater_arrays;
3757 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3758 for (y = 0; y < 3; y++)
3759 for (x = 0; x < 3; x++)
3760 level->yamyam_content[i].e[x][y] =
3761 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3763 level->amoeba_speed = cav->amoeba_time;
3764 level->time_magic_wall = cav->wonderwall_time;
3765 level->time_wheel = cav->wheel_time;
3767 level->android_move_time = cav->android_move_time;
3768 level->android_clone_time = cav->android_clone_time;
3769 level->ball_random = cav->ball_random;
3770 level->ball_active_initial = cav->ball_active;
3771 level->ball_time = cav->ball_time;
3772 level->num_ball_contents = cav->num_ball_arrays;
3774 level->lenses_score = cav->lenses_score;
3775 level->magnify_score = cav->magnify_score;
3776 level->slurp_score = cav->slurp_score;
3778 level->lenses_time = cav->lenses_time;
3779 level->magnify_time = cav->magnify_time;
3781 level->wind_direction_initial =
3782 map_direction_EM_to_RND(cav->wind_direction);
3784 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3785 for (j = 0; j < 8; j++)
3786 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3787 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3789 map_android_clone_elements_EM_to_RND(level);
3791 // convert the playfield (some elements need special treatment)
3792 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3794 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3796 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3797 new_element = EL_AMOEBA_DEAD;
3799 level->field[x][y] = new_element;
3802 for (i = 0; i < MAX_PLAYERS; i++)
3804 // in case of all players set to the same field, use the first player
3805 int nr = MAX_PLAYERS - i - 1;
3806 int jx = cav->player_x[nr];
3807 int jy = cav->player_y[nr];
3809 if (jx != -1 && jy != -1)
3810 level->field[jx][jy] = EL_PLAYER_1 + nr;
3813 // time score is counted for each 10 seconds left in Emerald Mine levels
3814 level->time_score_base = 10;
3818 // ----------------------------------------------------------------------------
3819 // functions for loading SP level
3820 // ----------------------------------------------------------------------------
3822 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3824 struct LevelInfo_SP *level_sp = level->native_sp_level;
3825 LevelInfoType *header = &level_sp->header;
3828 level_sp->width = level->fieldx;
3829 level_sp->height = level->fieldy;
3831 for (x = 0; x < level->fieldx; x++)
3832 for (y = 0; y < level->fieldy; y++)
3833 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3835 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3837 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3838 header->LevelTitle[i] = level->name[i];
3839 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3841 header->InfotronsNeeded = level->gems_needed;
3843 header->SpecialPortCount = 0;
3845 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3847 boolean gravity_port_found = FALSE;
3848 boolean gravity_port_valid = FALSE;
3849 int gravity_port_flag;
3850 int gravity_port_base_element;
3851 int element = level->field[x][y];
3853 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3854 element <= EL_SP_GRAVITY_ON_PORT_UP)
3856 gravity_port_found = TRUE;
3857 gravity_port_valid = TRUE;
3858 gravity_port_flag = 1;
3859 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3861 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3862 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3864 gravity_port_found = TRUE;
3865 gravity_port_valid = TRUE;
3866 gravity_port_flag = 0;
3867 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3869 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3870 element <= EL_SP_GRAVITY_PORT_UP)
3872 // change R'n'D style gravity inverting special port to normal port
3873 // (there are no gravity inverting ports in native Supaplex engine)
3875 gravity_port_found = TRUE;
3876 gravity_port_valid = FALSE;
3877 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3880 if (gravity_port_found)
3882 if (gravity_port_valid &&
3883 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3885 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3887 port->PortLocation = (y * level->fieldx + x) * 2;
3888 port->Gravity = gravity_port_flag;
3890 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3892 header->SpecialPortCount++;
3896 // change special gravity port to normal port
3898 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3901 level_sp->playfield[x][y] = element - EL_SP_START;
3906 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3908 struct LevelInfo_SP *level_sp = level->native_sp_level;
3909 LevelInfoType *header = &level_sp->header;
3910 boolean num_invalid_elements = 0;
3913 level->fieldx = level_sp->width;
3914 level->fieldy = level_sp->height;
3916 for (x = 0; x < level->fieldx; x++)
3918 for (y = 0; y < level->fieldy; y++)
3920 int element_old = level_sp->playfield[x][y];
3921 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3923 if (element_new == EL_UNKNOWN)
3925 num_invalid_elements++;
3927 Debug("level:native:SP", "invalid element %d at position %d, %d",
3931 level->field[x][y] = element_new;
3935 if (num_invalid_elements > 0)
3936 Warn("found %d invalid elements%s", num_invalid_elements,
3937 (!options.debug ? " (use '--debug' for more details)" : ""));
3939 for (i = 0; i < MAX_PLAYERS; i++)
3940 level->initial_player_gravity[i] =
3941 (header->InitialGravity == 1 ? TRUE : FALSE);
3943 // skip leading spaces
3944 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3945 if (header->LevelTitle[i] != ' ')
3949 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3950 level->name[j] = header->LevelTitle[i];
3951 level->name[j] = '\0';
3953 // cut trailing spaces
3955 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3956 level->name[j - 1] = '\0';
3958 level->gems_needed = header->InfotronsNeeded;
3960 for (i = 0; i < header->SpecialPortCount; i++)
3962 SpecialPortType *port = &header->SpecialPort[i];
3963 int port_location = port->PortLocation;
3964 int gravity = port->Gravity;
3965 int port_x, port_y, port_element;
3967 port_x = (port_location / 2) % level->fieldx;
3968 port_y = (port_location / 2) / level->fieldx;
3970 if (port_x < 0 || port_x >= level->fieldx ||
3971 port_y < 0 || port_y >= level->fieldy)
3973 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3978 port_element = level->field[port_x][port_y];
3980 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3981 port_element > EL_SP_GRAVITY_PORT_UP)
3983 Warn("no special port at position (%d, %d)", port_x, port_y);
3988 // change previous (wrong) gravity inverting special port to either
3989 // gravity enabling special port or gravity disabling special port
3990 level->field[port_x][port_y] +=
3991 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3992 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3995 // change special gravity ports without database entries to normal ports
3996 for (x = 0; x < level->fieldx; x++)
3997 for (y = 0; y < level->fieldy; y++)
3998 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3999 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4000 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4002 level->time = 0; // no time limit
4003 level->amoeba_speed = 0;
4004 level->time_magic_wall = 0;
4005 level->time_wheel = 0;
4006 level->amoeba_content = EL_EMPTY;
4008 // original Supaplex does not use score values -- rate by playing time
4009 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4010 level->score[i] = 0;
4012 level->rate_time_over_score = TRUE;
4014 // there are no yamyams in supaplex levels
4015 for (i = 0; i < level->num_yamyam_contents; i++)
4016 for (x = 0; x < 3; x++)
4017 for (y = 0; y < 3; y++)
4018 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4021 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4023 struct LevelInfo_SP *level_sp = level->native_sp_level;
4024 struct DemoInfo_SP *demo = &level_sp->demo;
4027 // always start with reliable default values
4028 demo->is_available = FALSE;
4031 if (TAPE_IS_EMPTY(tape))
4034 demo->level_nr = tape.level_nr; // (currently not used)
4036 level_sp->header.DemoRandomSeed = tape.random_seed;
4040 for (i = 0; i < tape.length; i++)
4042 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4043 int demo_repeat = tape.pos[i].delay;
4044 int demo_entries = (demo_repeat + 15) / 16;
4046 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4048 Warn("tape truncated: size exceeds maximum SP demo size %d",
4054 for (j = 0; j < demo_repeat / 16; j++)
4055 demo->data[demo->length++] = 0xf0 | demo_action;
4057 if (demo_repeat % 16)
4058 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4061 demo->is_available = TRUE;
4064 static void setTapeInfoToDefaults(void);
4066 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4068 struct LevelInfo_SP *level_sp = level->native_sp_level;
4069 struct DemoInfo_SP *demo = &level_sp->demo;
4070 char *filename = level->file_info.filename;
4073 // always start with reliable default values
4074 setTapeInfoToDefaults();
4076 if (!demo->is_available)
4079 tape.level_nr = demo->level_nr; // (currently not used)
4080 tape.random_seed = level_sp->header.DemoRandomSeed;
4082 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4085 tape.pos[tape.counter].delay = 0;
4087 for (i = 0; i < demo->length; i++)
4089 int demo_action = demo->data[i] & 0x0f;
4090 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4091 int tape_action = map_key_SP_to_RND(demo_action);
4092 int tape_repeat = demo_repeat + 1;
4093 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4094 boolean success = 0;
4097 for (j = 0; j < tape_repeat; j++)
4098 success = TapeAddAction(action);
4102 Warn("SP demo truncated: size exceeds maximum tape size %d",
4109 TapeHaltRecording();
4113 // ----------------------------------------------------------------------------
4114 // functions for loading MM level
4115 // ----------------------------------------------------------------------------
4117 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4119 struct LevelInfo_MM *level_mm = level->native_mm_level;
4122 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4123 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4125 level_mm->time = level->time;
4126 level_mm->kettles_needed = level->gems_needed;
4127 level_mm->auto_count_kettles = level->auto_count_gems;
4129 level_mm->laser_red = level->mm_laser_red;
4130 level_mm->laser_green = level->mm_laser_green;
4131 level_mm->laser_blue = level->mm_laser_blue;
4133 strcpy(level_mm->name, level->name);
4134 strcpy(level_mm->author, level->author);
4136 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4137 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4138 level_mm->score[SC_KEY] = level->score[SC_KEY];
4139 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4140 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4142 level_mm->amoeba_speed = level->amoeba_speed;
4143 level_mm->time_fuse = level->mm_time_fuse;
4144 level_mm->time_bomb = level->mm_time_bomb;
4145 level_mm->time_ball = level->mm_time_ball;
4146 level_mm->time_block = level->mm_time_block;
4148 for (x = 0; x < level->fieldx; x++)
4149 for (y = 0; y < level->fieldy; y++)
4151 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4154 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4156 struct LevelInfo_MM *level_mm = level->native_mm_level;
4159 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4160 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4162 level->time = level_mm->time;
4163 level->gems_needed = level_mm->kettles_needed;
4164 level->auto_count_gems = level_mm->auto_count_kettles;
4166 level->mm_laser_red = level_mm->laser_red;
4167 level->mm_laser_green = level_mm->laser_green;
4168 level->mm_laser_blue = level_mm->laser_blue;
4170 strcpy(level->name, level_mm->name);
4172 // only overwrite author from 'levelinfo.conf' if author defined in level
4173 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4174 strcpy(level->author, level_mm->author);
4176 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4177 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4178 level->score[SC_KEY] = level_mm->score[SC_KEY];
4179 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4180 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4182 level->amoeba_speed = level_mm->amoeba_speed;
4183 level->mm_time_fuse = level_mm->time_fuse;
4184 level->mm_time_bomb = level_mm->time_bomb;
4185 level->mm_time_ball = level_mm->time_ball;
4186 level->mm_time_block = level_mm->time_block;
4188 for (x = 0; x < level->fieldx; x++)
4189 for (y = 0; y < level->fieldy; y++)
4190 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4194 // ----------------------------------------------------------------------------
4195 // functions for loading DC level
4196 // ----------------------------------------------------------------------------
4198 #define DC_LEVEL_HEADER_SIZE 344
4200 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4203 static int last_data_encoded;
4207 int diff_hi, diff_lo;
4208 int data_hi, data_lo;
4209 unsigned short data_decoded;
4213 last_data_encoded = 0;
4220 diff = data_encoded - last_data_encoded;
4221 diff_hi = diff & ~0xff;
4222 diff_lo = diff & 0xff;
4226 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4227 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4228 data_hi = data_hi & 0xff00;
4230 data_decoded = data_hi | data_lo;
4232 last_data_encoded = data_encoded;
4234 offset1 = (offset1 + 1) % 31;
4235 offset2 = offset2 & 0xff;
4237 return data_decoded;
4240 static int getMappedElement_DC(int element)
4248 // 0x0117 - 0x036e: (?)
4251 // 0x042d - 0x0684: (?)
4267 element = EL_CRYSTAL;
4270 case 0x0e77: // quicksand (boulder)
4271 element = EL_QUICKSAND_FAST_FULL;
4274 case 0x0e99: // slow quicksand (boulder)
4275 element = EL_QUICKSAND_FULL;
4279 element = EL_EM_EXIT_OPEN;
4283 element = EL_EM_EXIT_CLOSED;
4287 element = EL_EM_STEEL_EXIT_OPEN;
4291 element = EL_EM_STEEL_EXIT_CLOSED;
4294 case 0x0f4f: // dynamite (lit 1)
4295 element = EL_EM_DYNAMITE_ACTIVE;
4298 case 0x0f57: // dynamite (lit 2)
4299 element = EL_EM_DYNAMITE_ACTIVE;
4302 case 0x0f5f: // dynamite (lit 3)
4303 element = EL_EM_DYNAMITE_ACTIVE;
4306 case 0x0f67: // dynamite (lit 4)
4307 element = EL_EM_DYNAMITE_ACTIVE;
4314 element = EL_AMOEBA_WET;
4318 element = EL_AMOEBA_DROP;
4322 element = EL_DC_MAGIC_WALL;
4326 element = EL_SPACESHIP_UP;
4330 element = EL_SPACESHIP_DOWN;
4334 element = EL_SPACESHIP_LEFT;
4338 element = EL_SPACESHIP_RIGHT;
4342 element = EL_BUG_UP;
4346 element = EL_BUG_DOWN;
4350 element = EL_BUG_LEFT;
4354 element = EL_BUG_RIGHT;
4358 element = EL_MOLE_UP;
4362 element = EL_MOLE_DOWN;
4366 element = EL_MOLE_LEFT;
4370 element = EL_MOLE_RIGHT;
4378 element = EL_YAMYAM_UP;
4382 element = EL_SWITCHGATE_OPEN;
4386 element = EL_SWITCHGATE_CLOSED;
4390 element = EL_DC_SWITCHGATE_SWITCH_UP;
4394 element = EL_TIMEGATE_CLOSED;
4397 case 0x144c: // conveyor belt switch (green)
4398 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4401 case 0x144f: // conveyor belt switch (red)
4402 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4405 case 0x1452: // conveyor belt switch (blue)
4406 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4410 element = EL_CONVEYOR_BELT_3_MIDDLE;
4414 element = EL_CONVEYOR_BELT_3_LEFT;
4418 element = EL_CONVEYOR_BELT_3_RIGHT;
4422 element = EL_CONVEYOR_BELT_1_MIDDLE;
4426 element = EL_CONVEYOR_BELT_1_LEFT;
4430 element = EL_CONVEYOR_BELT_1_RIGHT;
4434 element = EL_CONVEYOR_BELT_4_MIDDLE;
4438 element = EL_CONVEYOR_BELT_4_LEFT;
4442 element = EL_CONVEYOR_BELT_4_RIGHT;
4446 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4450 element = EL_EXPANDABLE_WALL_VERTICAL;
4454 element = EL_EXPANDABLE_WALL_ANY;
4457 case 0x14ce: // growing steel wall (left/right)
4458 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4461 case 0x14df: // growing steel wall (up/down)
4462 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4465 case 0x14e8: // growing steel wall (up/down/left/right)
4466 element = EL_EXPANDABLE_STEELWALL_ANY;
4470 element = EL_SHIELD_DEADLY;
4474 element = EL_EXTRA_TIME;
4482 element = EL_EMPTY_SPACE;
4485 case 0x1578: // quicksand (empty)
4486 element = EL_QUICKSAND_FAST_EMPTY;
4489 case 0x1579: // slow quicksand (empty)
4490 element = EL_QUICKSAND_EMPTY;
4500 element = EL_EM_DYNAMITE;
4503 case 0x15a1: // key (red)
4504 element = EL_EM_KEY_1;
4507 case 0x15a2: // key (yellow)
4508 element = EL_EM_KEY_2;
4511 case 0x15a3: // key (blue)
4512 element = EL_EM_KEY_4;
4515 case 0x15a4: // key (green)
4516 element = EL_EM_KEY_3;
4519 case 0x15a5: // key (white)
4520 element = EL_DC_KEY_WHITE;
4524 element = EL_WALL_SLIPPERY;
4531 case 0x15a8: // wall (not round)
4535 case 0x15a9: // (blue)
4536 element = EL_CHAR_A;
4539 case 0x15aa: // (blue)
4540 element = EL_CHAR_B;
4543 case 0x15ab: // (blue)
4544 element = EL_CHAR_C;
4547 case 0x15ac: // (blue)
4548 element = EL_CHAR_D;
4551 case 0x15ad: // (blue)
4552 element = EL_CHAR_E;
4555 case 0x15ae: // (blue)
4556 element = EL_CHAR_F;
4559 case 0x15af: // (blue)
4560 element = EL_CHAR_G;
4563 case 0x15b0: // (blue)
4564 element = EL_CHAR_H;
4567 case 0x15b1: // (blue)
4568 element = EL_CHAR_I;
4571 case 0x15b2: // (blue)
4572 element = EL_CHAR_J;
4575 case 0x15b3: // (blue)
4576 element = EL_CHAR_K;
4579 case 0x15b4: // (blue)
4580 element = EL_CHAR_L;
4583 case 0x15b5: // (blue)
4584 element = EL_CHAR_M;
4587 case 0x15b6: // (blue)
4588 element = EL_CHAR_N;
4591 case 0x15b7: // (blue)
4592 element = EL_CHAR_O;
4595 case 0x15b8: // (blue)
4596 element = EL_CHAR_P;
4599 case 0x15b9: // (blue)
4600 element = EL_CHAR_Q;
4603 case 0x15ba: // (blue)
4604 element = EL_CHAR_R;
4607 case 0x15bb: // (blue)
4608 element = EL_CHAR_S;
4611 case 0x15bc: // (blue)
4612 element = EL_CHAR_T;
4615 case 0x15bd: // (blue)
4616 element = EL_CHAR_U;
4619 case 0x15be: // (blue)
4620 element = EL_CHAR_V;
4623 case 0x15bf: // (blue)
4624 element = EL_CHAR_W;
4627 case 0x15c0: // (blue)
4628 element = EL_CHAR_X;
4631 case 0x15c1: // (blue)
4632 element = EL_CHAR_Y;
4635 case 0x15c2: // (blue)
4636 element = EL_CHAR_Z;
4639 case 0x15c3: // (blue)
4640 element = EL_CHAR_AUMLAUT;
4643 case 0x15c4: // (blue)
4644 element = EL_CHAR_OUMLAUT;
4647 case 0x15c5: // (blue)
4648 element = EL_CHAR_UUMLAUT;
4651 case 0x15c6: // (blue)
4652 element = EL_CHAR_0;
4655 case 0x15c7: // (blue)
4656 element = EL_CHAR_1;
4659 case 0x15c8: // (blue)
4660 element = EL_CHAR_2;
4663 case 0x15c9: // (blue)
4664 element = EL_CHAR_3;
4667 case 0x15ca: // (blue)
4668 element = EL_CHAR_4;
4671 case 0x15cb: // (blue)
4672 element = EL_CHAR_5;
4675 case 0x15cc: // (blue)
4676 element = EL_CHAR_6;
4679 case 0x15cd: // (blue)
4680 element = EL_CHAR_7;
4683 case 0x15ce: // (blue)
4684 element = EL_CHAR_8;
4687 case 0x15cf: // (blue)
4688 element = EL_CHAR_9;
4691 case 0x15d0: // (blue)
4692 element = EL_CHAR_PERIOD;
4695 case 0x15d1: // (blue)
4696 element = EL_CHAR_EXCLAM;
4699 case 0x15d2: // (blue)
4700 element = EL_CHAR_COLON;
4703 case 0x15d3: // (blue)
4704 element = EL_CHAR_LESS;
4707 case 0x15d4: // (blue)
4708 element = EL_CHAR_GREATER;
4711 case 0x15d5: // (blue)
4712 element = EL_CHAR_QUESTION;
4715 case 0x15d6: // (blue)
4716 element = EL_CHAR_COPYRIGHT;
4719 case 0x15d7: // (blue)
4720 element = EL_CHAR_UP;
4723 case 0x15d8: // (blue)
4724 element = EL_CHAR_DOWN;
4727 case 0x15d9: // (blue)
4728 element = EL_CHAR_BUTTON;
4731 case 0x15da: // (blue)
4732 element = EL_CHAR_PLUS;
4735 case 0x15db: // (blue)
4736 element = EL_CHAR_MINUS;
4739 case 0x15dc: // (blue)
4740 element = EL_CHAR_APOSTROPHE;
4743 case 0x15dd: // (blue)
4744 element = EL_CHAR_PARENLEFT;
4747 case 0x15de: // (blue)
4748 element = EL_CHAR_PARENRIGHT;
4751 case 0x15df: // (green)
4752 element = EL_CHAR_A;
4755 case 0x15e0: // (green)
4756 element = EL_CHAR_B;
4759 case 0x15e1: // (green)
4760 element = EL_CHAR_C;
4763 case 0x15e2: // (green)
4764 element = EL_CHAR_D;
4767 case 0x15e3: // (green)
4768 element = EL_CHAR_E;
4771 case 0x15e4: // (green)
4772 element = EL_CHAR_F;
4775 case 0x15e5: // (green)
4776 element = EL_CHAR_G;
4779 case 0x15e6: // (green)
4780 element = EL_CHAR_H;
4783 case 0x15e7: // (green)
4784 element = EL_CHAR_I;
4787 case 0x15e8: // (green)
4788 element = EL_CHAR_J;
4791 case 0x15e9: // (green)
4792 element = EL_CHAR_K;
4795 case 0x15ea: // (green)
4796 element = EL_CHAR_L;
4799 case 0x15eb: // (green)
4800 element = EL_CHAR_M;
4803 case 0x15ec: // (green)
4804 element = EL_CHAR_N;
4807 case 0x15ed: // (green)
4808 element = EL_CHAR_O;
4811 case 0x15ee: // (green)
4812 element = EL_CHAR_P;
4815 case 0x15ef: // (green)
4816 element = EL_CHAR_Q;
4819 case 0x15f0: // (green)
4820 element = EL_CHAR_R;
4823 case 0x15f1: // (green)
4824 element = EL_CHAR_S;
4827 case 0x15f2: // (green)
4828 element = EL_CHAR_T;
4831 case 0x15f3: // (green)
4832 element = EL_CHAR_U;
4835 case 0x15f4: // (green)
4836 element = EL_CHAR_V;
4839 case 0x15f5: // (green)
4840 element = EL_CHAR_W;
4843 case 0x15f6: // (green)
4844 element = EL_CHAR_X;
4847 case 0x15f7: // (green)
4848 element = EL_CHAR_Y;
4851 case 0x15f8: // (green)
4852 element = EL_CHAR_Z;
4855 case 0x15f9: // (green)
4856 element = EL_CHAR_AUMLAUT;
4859 case 0x15fa: // (green)
4860 element = EL_CHAR_OUMLAUT;
4863 case 0x15fb: // (green)
4864 element = EL_CHAR_UUMLAUT;
4867 case 0x15fc: // (green)
4868 element = EL_CHAR_0;
4871 case 0x15fd: // (green)
4872 element = EL_CHAR_1;
4875 case 0x15fe: // (green)
4876 element = EL_CHAR_2;
4879 case 0x15ff: // (green)
4880 element = EL_CHAR_3;
4883 case 0x1600: // (green)
4884 element = EL_CHAR_4;
4887 case 0x1601: // (green)
4888 element = EL_CHAR_5;
4891 case 0x1602: // (green)
4892 element = EL_CHAR_6;
4895 case 0x1603: // (green)
4896 element = EL_CHAR_7;
4899 case 0x1604: // (green)
4900 element = EL_CHAR_8;
4903 case 0x1605: // (green)
4904 element = EL_CHAR_9;
4907 case 0x1606: // (green)
4908 element = EL_CHAR_PERIOD;
4911 case 0x1607: // (green)
4912 element = EL_CHAR_EXCLAM;
4915 case 0x1608: // (green)
4916 element = EL_CHAR_COLON;
4919 case 0x1609: // (green)
4920 element = EL_CHAR_LESS;
4923 case 0x160a: // (green)
4924 element = EL_CHAR_GREATER;
4927 case 0x160b: // (green)
4928 element = EL_CHAR_QUESTION;
4931 case 0x160c: // (green)
4932 element = EL_CHAR_COPYRIGHT;
4935 case 0x160d: // (green)
4936 element = EL_CHAR_UP;
4939 case 0x160e: // (green)
4940 element = EL_CHAR_DOWN;
4943 case 0x160f: // (green)
4944 element = EL_CHAR_BUTTON;
4947 case 0x1610: // (green)
4948 element = EL_CHAR_PLUS;
4951 case 0x1611: // (green)
4952 element = EL_CHAR_MINUS;
4955 case 0x1612: // (green)
4956 element = EL_CHAR_APOSTROPHE;
4959 case 0x1613: // (green)
4960 element = EL_CHAR_PARENLEFT;
4963 case 0x1614: // (green)
4964 element = EL_CHAR_PARENRIGHT;
4967 case 0x1615: // (blue steel)
4968 element = EL_STEEL_CHAR_A;
4971 case 0x1616: // (blue steel)
4972 element = EL_STEEL_CHAR_B;
4975 case 0x1617: // (blue steel)
4976 element = EL_STEEL_CHAR_C;
4979 case 0x1618: // (blue steel)
4980 element = EL_STEEL_CHAR_D;
4983 case 0x1619: // (blue steel)
4984 element = EL_STEEL_CHAR_E;
4987 case 0x161a: // (blue steel)
4988 element = EL_STEEL_CHAR_F;
4991 case 0x161b: // (blue steel)
4992 element = EL_STEEL_CHAR_G;
4995 case 0x161c: // (blue steel)
4996 element = EL_STEEL_CHAR_H;
4999 case 0x161d: // (blue steel)
5000 element = EL_STEEL_CHAR_I;
5003 case 0x161e: // (blue steel)
5004 element = EL_STEEL_CHAR_J;
5007 case 0x161f: // (blue steel)
5008 element = EL_STEEL_CHAR_K;
5011 case 0x1620: // (blue steel)
5012 element = EL_STEEL_CHAR_L;
5015 case 0x1621: // (blue steel)
5016 element = EL_STEEL_CHAR_M;
5019 case 0x1622: // (blue steel)
5020 element = EL_STEEL_CHAR_N;
5023 case 0x1623: // (blue steel)
5024 element = EL_STEEL_CHAR_O;
5027 case 0x1624: // (blue steel)
5028 element = EL_STEEL_CHAR_P;
5031 case 0x1625: // (blue steel)
5032 element = EL_STEEL_CHAR_Q;
5035 case 0x1626: // (blue steel)
5036 element = EL_STEEL_CHAR_R;
5039 case 0x1627: // (blue steel)
5040 element = EL_STEEL_CHAR_S;
5043 case 0x1628: // (blue steel)
5044 element = EL_STEEL_CHAR_T;
5047 case 0x1629: // (blue steel)
5048 element = EL_STEEL_CHAR_U;
5051 case 0x162a: // (blue steel)
5052 element = EL_STEEL_CHAR_V;
5055 case 0x162b: // (blue steel)
5056 element = EL_STEEL_CHAR_W;
5059 case 0x162c: // (blue steel)
5060 element = EL_STEEL_CHAR_X;
5063 case 0x162d: // (blue steel)
5064 element = EL_STEEL_CHAR_Y;
5067 case 0x162e: // (blue steel)
5068 element = EL_STEEL_CHAR_Z;
5071 case 0x162f: // (blue steel)
5072 element = EL_STEEL_CHAR_AUMLAUT;
5075 case 0x1630: // (blue steel)
5076 element = EL_STEEL_CHAR_OUMLAUT;
5079 case 0x1631: // (blue steel)
5080 element = EL_STEEL_CHAR_UUMLAUT;
5083 case 0x1632: // (blue steel)
5084 element = EL_STEEL_CHAR_0;
5087 case 0x1633: // (blue steel)
5088 element = EL_STEEL_CHAR_1;
5091 case 0x1634: // (blue steel)
5092 element = EL_STEEL_CHAR_2;
5095 case 0x1635: // (blue steel)
5096 element = EL_STEEL_CHAR_3;
5099 case 0x1636: // (blue steel)
5100 element = EL_STEEL_CHAR_4;
5103 case 0x1637: // (blue steel)
5104 element = EL_STEEL_CHAR_5;
5107 case 0x1638: // (blue steel)
5108 element = EL_STEEL_CHAR_6;
5111 case 0x1639: // (blue steel)
5112 element = EL_STEEL_CHAR_7;
5115 case 0x163a: // (blue steel)
5116 element = EL_STEEL_CHAR_8;
5119 case 0x163b: // (blue steel)
5120 element = EL_STEEL_CHAR_9;
5123 case 0x163c: // (blue steel)
5124 element = EL_STEEL_CHAR_PERIOD;
5127 case 0x163d: // (blue steel)
5128 element = EL_STEEL_CHAR_EXCLAM;
5131 case 0x163e: // (blue steel)
5132 element = EL_STEEL_CHAR_COLON;
5135 case 0x163f: // (blue steel)
5136 element = EL_STEEL_CHAR_LESS;
5139 case 0x1640: // (blue steel)
5140 element = EL_STEEL_CHAR_GREATER;
5143 case 0x1641: // (blue steel)
5144 element = EL_STEEL_CHAR_QUESTION;
5147 case 0x1642: // (blue steel)
5148 element = EL_STEEL_CHAR_COPYRIGHT;
5151 case 0x1643: // (blue steel)
5152 element = EL_STEEL_CHAR_UP;
5155 case 0x1644: // (blue steel)
5156 element = EL_STEEL_CHAR_DOWN;
5159 case 0x1645: // (blue steel)
5160 element = EL_STEEL_CHAR_BUTTON;
5163 case 0x1646: // (blue steel)
5164 element = EL_STEEL_CHAR_PLUS;
5167 case 0x1647: // (blue steel)
5168 element = EL_STEEL_CHAR_MINUS;
5171 case 0x1648: // (blue steel)
5172 element = EL_STEEL_CHAR_APOSTROPHE;
5175 case 0x1649: // (blue steel)
5176 element = EL_STEEL_CHAR_PARENLEFT;
5179 case 0x164a: // (blue steel)
5180 element = EL_STEEL_CHAR_PARENRIGHT;
5183 case 0x164b: // (green steel)
5184 element = EL_STEEL_CHAR_A;
5187 case 0x164c: // (green steel)
5188 element = EL_STEEL_CHAR_B;
5191 case 0x164d: // (green steel)
5192 element = EL_STEEL_CHAR_C;
5195 case 0x164e: // (green steel)
5196 element = EL_STEEL_CHAR_D;
5199 case 0x164f: // (green steel)
5200 element = EL_STEEL_CHAR_E;
5203 case 0x1650: // (green steel)
5204 element = EL_STEEL_CHAR_F;
5207 case 0x1651: // (green steel)
5208 element = EL_STEEL_CHAR_G;
5211 case 0x1652: // (green steel)
5212 element = EL_STEEL_CHAR_H;
5215 case 0x1653: // (green steel)
5216 element = EL_STEEL_CHAR_I;
5219 case 0x1654: // (green steel)
5220 element = EL_STEEL_CHAR_J;
5223 case 0x1655: // (green steel)
5224 element = EL_STEEL_CHAR_K;
5227 case 0x1656: // (green steel)
5228 element = EL_STEEL_CHAR_L;
5231 case 0x1657: // (green steel)
5232 element = EL_STEEL_CHAR_M;
5235 case 0x1658: // (green steel)
5236 element = EL_STEEL_CHAR_N;
5239 case 0x1659: // (green steel)
5240 element = EL_STEEL_CHAR_O;
5243 case 0x165a: // (green steel)
5244 element = EL_STEEL_CHAR_P;
5247 case 0x165b: // (green steel)
5248 element = EL_STEEL_CHAR_Q;
5251 case 0x165c: // (green steel)
5252 element = EL_STEEL_CHAR_R;
5255 case 0x165d: // (green steel)
5256 element = EL_STEEL_CHAR_S;
5259 case 0x165e: // (green steel)
5260 element = EL_STEEL_CHAR_T;
5263 case 0x165f: // (green steel)
5264 element = EL_STEEL_CHAR_U;
5267 case 0x1660: // (green steel)
5268 element = EL_STEEL_CHAR_V;
5271 case 0x1661: // (green steel)
5272 element = EL_STEEL_CHAR_W;
5275 case 0x1662: // (green steel)
5276 element = EL_STEEL_CHAR_X;
5279 case 0x1663: // (green steel)
5280 element = EL_STEEL_CHAR_Y;
5283 case 0x1664: // (green steel)
5284 element = EL_STEEL_CHAR_Z;
5287 case 0x1665: // (green steel)
5288 element = EL_STEEL_CHAR_AUMLAUT;
5291 case 0x1666: // (green steel)
5292 element = EL_STEEL_CHAR_OUMLAUT;
5295 case 0x1667: // (green steel)
5296 element = EL_STEEL_CHAR_UUMLAUT;
5299 case 0x1668: // (green steel)
5300 element = EL_STEEL_CHAR_0;
5303 case 0x1669: // (green steel)
5304 element = EL_STEEL_CHAR_1;
5307 case 0x166a: // (green steel)
5308 element = EL_STEEL_CHAR_2;
5311 case 0x166b: // (green steel)
5312 element = EL_STEEL_CHAR_3;
5315 case 0x166c: // (green steel)
5316 element = EL_STEEL_CHAR_4;
5319 case 0x166d: // (green steel)
5320 element = EL_STEEL_CHAR_5;
5323 case 0x166e: // (green steel)
5324 element = EL_STEEL_CHAR_6;
5327 case 0x166f: // (green steel)
5328 element = EL_STEEL_CHAR_7;
5331 case 0x1670: // (green steel)
5332 element = EL_STEEL_CHAR_8;
5335 case 0x1671: // (green steel)
5336 element = EL_STEEL_CHAR_9;
5339 case 0x1672: // (green steel)
5340 element = EL_STEEL_CHAR_PERIOD;
5343 case 0x1673: // (green steel)
5344 element = EL_STEEL_CHAR_EXCLAM;
5347 case 0x1674: // (green steel)
5348 element = EL_STEEL_CHAR_COLON;
5351 case 0x1675: // (green steel)
5352 element = EL_STEEL_CHAR_LESS;
5355 case 0x1676: // (green steel)
5356 element = EL_STEEL_CHAR_GREATER;
5359 case 0x1677: // (green steel)
5360 element = EL_STEEL_CHAR_QUESTION;
5363 case 0x1678: // (green steel)
5364 element = EL_STEEL_CHAR_COPYRIGHT;
5367 case 0x1679: // (green steel)
5368 element = EL_STEEL_CHAR_UP;
5371 case 0x167a: // (green steel)
5372 element = EL_STEEL_CHAR_DOWN;
5375 case 0x167b: // (green steel)
5376 element = EL_STEEL_CHAR_BUTTON;
5379 case 0x167c: // (green steel)
5380 element = EL_STEEL_CHAR_PLUS;
5383 case 0x167d: // (green steel)
5384 element = EL_STEEL_CHAR_MINUS;
5387 case 0x167e: // (green steel)
5388 element = EL_STEEL_CHAR_APOSTROPHE;
5391 case 0x167f: // (green steel)
5392 element = EL_STEEL_CHAR_PARENLEFT;
5395 case 0x1680: // (green steel)
5396 element = EL_STEEL_CHAR_PARENRIGHT;
5399 case 0x1681: // gate (red)
5400 element = EL_EM_GATE_1;
5403 case 0x1682: // secret gate (red)
5404 element = EL_EM_GATE_1_GRAY;
5407 case 0x1683: // gate (yellow)
5408 element = EL_EM_GATE_2;
5411 case 0x1684: // secret gate (yellow)
5412 element = EL_EM_GATE_2_GRAY;
5415 case 0x1685: // gate (blue)
5416 element = EL_EM_GATE_4;
5419 case 0x1686: // secret gate (blue)
5420 element = EL_EM_GATE_4_GRAY;
5423 case 0x1687: // gate (green)
5424 element = EL_EM_GATE_3;
5427 case 0x1688: // secret gate (green)
5428 element = EL_EM_GATE_3_GRAY;
5431 case 0x1689: // gate (white)
5432 element = EL_DC_GATE_WHITE;
5435 case 0x168a: // secret gate (white)
5436 element = EL_DC_GATE_WHITE_GRAY;
5439 case 0x168b: // secret gate (no key)
5440 element = EL_DC_GATE_FAKE_GRAY;
5444 element = EL_ROBOT_WHEEL;
5448 element = EL_DC_TIMEGATE_SWITCH;
5452 element = EL_ACID_POOL_BOTTOM;
5456 element = EL_ACID_POOL_TOPLEFT;
5460 element = EL_ACID_POOL_TOPRIGHT;
5464 element = EL_ACID_POOL_BOTTOMLEFT;
5468 element = EL_ACID_POOL_BOTTOMRIGHT;
5472 element = EL_STEELWALL;
5476 element = EL_STEELWALL_SLIPPERY;
5479 case 0x1695: // steel wall (not round)
5480 element = EL_STEELWALL;
5483 case 0x1696: // steel wall (left)
5484 element = EL_DC_STEELWALL_1_LEFT;
5487 case 0x1697: // steel wall (bottom)
5488 element = EL_DC_STEELWALL_1_BOTTOM;
5491 case 0x1698: // steel wall (right)
5492 element = EL_DC_STEELWALL_1_RIGHT;
5495 case 0x1699: // steel wall (top)
5496 element = EL_DC_STEELWALL_1_TOP;
5499 case 0x169a: // steel wall (left/bottom)
5500 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5503 case 0x169b: // steel wall (right/bottom)
5504 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5507 case 0x169c: // steel wall (right/top)
5508 element = EL_DC_STEELWALL_1_TOPRIGHT;
5511 case 0x169d: // steel wall (left/top)
5512 element = EL_DC_STEELWALL_1_TOPLEFT;
5515 case 0x169e: // steel wall (right/bottom small)
5516 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5519 case 0x169f: // steel wall (left/bottom small)
5520 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5523 case 0x16a0: // steel wall (right/top small)
5524 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5527 case 0x16a1: // steel wall (left/top small)
5528 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5531 case 0x16a2: // steel wall (left/right)
5532 element = EL_DC_STEELWALL_1_VERTICAL;
5535 case 0x16a3: // steel wall (top/bottom)
5536 element = EL_DC_STEELWALL_1_HORIZONTAL;
5539 case 0x16a4: // steel wall 2 (left end)
5540 element = EL_DC_STEELWALL_2_LEFT;
5543 case 0x16a5: // steel wall 2 (right end)
5544 element = EL_DC_STEELWALL_2_RIGHT;
5547 case 0x16a6: // steel wall 2 (top end)
5548 element = EL_DC_STEELWALL_2_TOP;
5551 case 0x16a7: // steel wall 2 (bottom end)
5552 element = EL_DC_STEELWALL_2_BOTTOM;
5555 case 0x16a8: // steel wall 2 (left/right)
5556 element = EL_DC_STEELWALL_2_HORIZONTAL;
5559 case 0x16a9: // steel wall 2 (up/down)
5560 element = EL_DC_STEELWALL_2_VERTICAL;
5563 case 0x16aa: // steel wall 2 (mid)
5564 element = EL_DC_STEELWALL_2_MIDDLE;
5568 element = EL_SIGN_EXCLAMATION;
5572 element = EL_SIGN_RADIOACTIVITY;
5576 element = EL_SIGN_STOP;
5580 element = EL_SIGN_WHEELCHAIR;
5584 element = EL_SIGN_PARKING;
5588 element = EL_SIGN_NO_ENTRY;
5592 element = EL_SIGN_HEART;
5596 element = EL_SIGN_GIVE_WAY;
5600 element = EL_SIGN_ENTRY_FORBIDDEN;
5604 element = EL_SIGN_EMERGENCY_EXIT;
5608 element = EL_SIGN_YIN_YANG;
5612 element = EL_WALL_EMERALD;
5616 element = EL_WALL_DIAMOND;
5620 element = EL_WALL_PEARL;
5624 element = EL_WALL_CRYSTAL;
5628 element = EL_INVISIBLE_WALL;
5632 element = EL_INVISIBLE_STEELWALL;
5636 // EL_INVISIBLE_SAND
5639 element = EL_LIGHT_SWITCH;
5643 element = EL_ENVELOPE_1;
5647 if (element >= 0x0117 && element <= 0x036e) // (?)
5648 element = EL_DIAMOND;
5649 else if (element >= 0x042d && element <= 0x0684) // (?)
5650 element = EL_EMERALD;
5651 else if (element >= 0x157c && element <= 0x158b)
5653 else if (element >= 0x1590 && element <= 0x159f)
5654 element = EL_DC_LANDMINE;
5655 else if (element >= 0x16bc && element <= 0x16cb)
5656 element = EL_INVISIBLE_SAND;
5659 Warn("unknown Diamond Caves element 0x%04x", element);
5661 element = EL_UNKNOWN;
5666 return getMappedElement(element);
5669 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5672 byte header[DC_LEVEL_HEADER_SIZE];
5674 int envelope_header_pos = 62;
5675 int envelope_content_pos = 94;
5676 int level_name_pos = 251;
5677 int level_author_pos = 292;
5678 int envelope_header_len;
5679 int envelope_content_len;
5681 int level_author_len;
5683 int num_yamyam_contents;
5686 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5688 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5690 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5692 header[i * 2 + 0] = header_word >> 8;
5693 header[i * 2 + 1] = header_word & 0xff;
5696 // read some values from level header to check level decoding integrity
5697 fieldx = header[6] | (header[7] << 8);
5698 fieldy = header[8] | (header[9] << 8);
5699 num_yamyam_contents = header[60] | (header[61] << 8);
5701 // do some simple sanity checks to ensure that level was correctly decoded
5702 if (fieldx < 1 || fieldx > 256 ||
5703 fieldy < 1 || fieldy > 256 ||
5704 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5706 level->no_valid_file = TRUE;
5708 Warn("cannot decode level from stream -- using empty level");
5713 // maximum envelope header size is 31 bytes
5714 envelope_header_len = header[envelope_header_pos];
5715 // maximum envelope content size is 110 (156?) bytes
5716 envelope_content_len = header[envelope_content_pos];
5718 // maximum level title size is 40 bytes
5719 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5720 // maximum level author size is 30 (51?) bytes
5721 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5725 for (i = 0; i < envelope_header_len; i++)
5726 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5727 level->envelope[0].text[envelope_size++] =
5728 header[envelope_header_pos + 1 + i];
5730 if (envelope_header_len > 0 && envelope_content_len > 0)
5732 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5733 level->envelope[0].text[envelope_size++] = '\n';
5734 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5735 level->envelope[0].text[envelope_size++] = '\n';
5738 for (i = 0; i < envelope_content_len; i++)
5739 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5740 level->envelope[0].text[envelope_size++] =
5741 header[envelope_content_pos + 1 + i];
5743 level->envelope[0].text[envelope_size] = '\0';
5745 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5746 level->envelope[0].ysize = 10;
5747 level->envelope[0].autowrap = TRUE;
5748 level->envelope[0].centered = TRUE;
5750 for (i = 0; i < level_name_len; i++)
5751 level->name[i] = header[level_name_pos + 1 + i];
5752 level->name[level_name_len] = '\0';
5754 for (i = 0; i < level_author_len; i++)
5755 level->author[i] = header[level_author_pos + 1 + i];
5756 level->author[level_author_len] = '\0';
5758 num_yamyam_contents = header[60] | (header[61] << 8);
5759 level->num_yamyam_contents =
5760 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5762 for (i = 0; i < num_yamyam_contents; i++)
5764 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5766 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5767 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5769 if (i < MAX_ELEMENT_CONTENTS)
5770 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5774 fieldx = header[6] | (header[7] << 8);
5775 fieldy = header[8] | (header[9] << 8);
5776 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5777 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5779 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5781 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5782 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5784 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5785 level->field[x][y] = getMappedElement_DC(element_dc);
5788 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5789 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5790 level->field[x][y] = EL_PLAYER_1;
5792 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5793 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5794 level->field[x][y] = EL_PLAYER_2;
5796 level->gems_needed = header[18] | (header[19] << 8);
5798 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5799 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5800 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5801 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5802 level->score[SC_NUT] = header[28] | (header[29] << 8);
5803 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5804 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5805 level->score[SC_BUG] = header[34] | (header[35] << 8);
5806 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5807 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5808 level->score[SC_KEY] = header[40] | (header[41] << 8);
5809 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5811 level->time = header[44] | (header[45] << 8);
5813 level->amoeba_speed = header[46] | (header[47] << 8);
5814 level->time_light = header[48] | (header[49] << 8);
5815 level->time_timegate = header[50] | (header[51] << 8);
5816 level->time_wheel = header[52] | (header[53] << 8);
5817 level->time_magic_wall = header[54] | (header[55] << 8);
5818 level->extra_time = header[56] | (header[57] << 8);
5819 level->shield_normal_time = header[58] | (header[59] << 8);
5821 // shield and extra time elements do not have a score
5822 level->score[SC_SHIELD] = 0;
5823 level->extra_time_score = 0;
5825 // set time for normal and deadly shields to the same value
5826 level->shield_deadly_time = level->shield_normal_time;
5828 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5829 // can slip down from flat walls, like normal walls and steel walls
5830 level->em_slippery_gems = TRUE;
5832 // time score is counted for each 10 seconds left in Diamond Caves levels
5833 level->time_score_base = 10;
5836 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5837 struct LevelFileInfo *level_file_info,
5838 boolean level_info_only)
5840 char *filename = level_file_info->filename;
5842 int num_magic_bytes = 8;
5843 char magic_bytes[num_magic_bytes + 1];
5844 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5846 if (!(file = openFile(filename, MODE_READ)))
5848 level->no_valid_file = TRUE;
5850 if (!level_info_only)
5851 Warn("cannot read level '%s' -- using empty level", filename);
5856 // fseek(file, 0x0000, SEEK_SET);
5858 if (level_file_info->packed)
5860 // read "magic bytes" from start of file
5861 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5862 magic_bytes[0] = '\0';
5864 // check "magic bytes" for correct file format
5865 if (!strPrefix(magic_bytes, "DC2"))
5867 level->no_valid_file = TRUE;
5869 Warn("unknown DC level file '%s' -- using empty level", filename);
5874 if (strPrefix(magic_bytes, "DC2Win95") ||
5875 strPrefix(magic_bytes, "DC2Win98"))
5877 int position_first_level = 0x00fa;
5878 int extra_bytes = 4;
5881 // advance file stream to first level inside the level package
5882 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5884 // each block of level data is followed by block of non-level data
5885 num_levels_to_skip *= 2;
5887 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5888 while (num_levels_to_skip >= 0)
5890 // advance file stream to next level inside the level package
5891 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5893 level->no_valid_file = TRUE;
5895 Warn("cannot fseek in file '%s' -- using empty level", filename);
5900 // skip apparently unused extra bytes following each level
5901 ReadUnusedBytesFromFile(file, extra_bytes);
5903 // read size of next level in level package
5904 skip_bytes = getFile32BitLE(file);
5906 num_levels_to_skip--;
5911 level->no_valid_file = TRUE;
5913 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5919 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5925 // ----------------------------------------------------------------------------
5926 // functions for loading SB level
5927 // ----------------------------------------------------------------------------
5929 int getMappedElement_SB(int element_ascii, boolean use_ces)
5937 sb_element_mapping[] =
5939 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5940 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5941 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5942 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5943 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5944 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5945 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5946 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5953 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5954 if (element_ascii == sb_element_mapping[i].ascii)
5955 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5957 return EL_UNDEFINED;
5960 static void SetLevelSettings_SB(struct LevelInfo *level)
5964 level->use_step_counter = TRUE;
5967 level->score[SC_TIME_BONUS] = 0;
5968 level->time_score_base = 1;
5969 level->rate_time_over_score = TRUE;
5972 level->auto_exit_sokoban = TRUE;
5975 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5976 struct LevelFileInfo *level_file_info,
5977 boolean level_info_only)
5979 char *filename = level_file_info->filename;
5980 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5981 char last_comment[MAX_LINE_LEN];
5982 char level_name[MAX_LINE_LEN];
5985 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5986 boolean read_continued_line = FALSE;
5987 boolean reading_playfield = FALSE;
5988 boolean got_valid_playfield_line = FALSE;
5989 boolean invalid_playfield_char = FALSE;
5990 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5991 int file_level_nr = 0;
5993 int x = 0, y = 0; // initialized to make compilers happy
5995 last_comment[0] = '\0';
5996 level_name[0] = '\0';
5998 if (!(file = openFile(filename, MODE_READ)))
6000 level->no_valid_file = TRUE;
6002 if (!level_info_only)
6003 Warn("cannot read level '%s' -- using empty level", filename);
6008 while (!checkEndOfFile(file))
6010 // level successfully read, but next level may follow here
6011 if (!got_valid_playfield_line && reading_playfield)
6013 // read playfield from single level file -- skip remaining file
6014 if (!level_file_info->packed)
6017 if (file_level_nr >= num_levels_to_skip)
6022 last_comment[0] = '\0';
6023 level_name[0] = '\0';
6025 reading_playfield = FALSE;
6028 got_valid_playfield_line = FALSE;
6030 // read next line of input file
6031 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6034 // check if line was completely read and is terminated by line break
6035 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6038 // cut trailing line break (this can be newline and/or carriage return)
6039 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6040 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6043 // copy raw input line for later use (mainly debugging output)
6044 strcpy(line_raw, line);
6046 if (read_continued_line)
6048 // append new line to existing line, if there is enough space
6049 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6050 strcat(previous_line, line_ptr);
6052 strcpy(line, previous_line); // copy storage buffer to line
6054 read_continued_line = FALSE;
6057 // if the last character is '\', continue at next line
6058 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6060 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6061 strcpy(previous_line, line); // copy line to storage buffer
6063 read_continued_line = TRUE;
6069 if (line[0] == '\0')
6072 // extract comment text from comment line
6075 for (line_ptr = line; *line_ptr; line_ptr++)
6076 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6079 strcpy(last_comment, line_ptr);
6084 // extract level title text from line containing level title
6085 if (line[0] == '\'')
6087 strcpy(level_name, &line[1]);
6089 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6090 level_name[strlen(level_name) - 1] = '\0';
6095 // skip lines containing only spaces (or empty lines)
6096 for (line_ptr = line; *line_ptr; line_ptr++)
6097 if (*line_ptr != ' ')
6099 if (*line_ptr == '\0')
6102 // at this point, we have found a line containing part of a playfield
6104 got_valid_playfield_line = TRUE;
6106 if (!reading_playfield)
6108 reading_playfield = TRUE;
6109 invalid_playfield_char = FALSE;
6111 for (x = 0; x < MAX_LEV_FIELDX; x++)
6112 for (y = 0; y < MAX_LEV_FIELDY; y++)
6113 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6118 // start with topmost tile row
6122 // skip playfield line if larger row than allowed
6123 if (y >= MAX_LEV_FIELDY)
6126 // start with leftmost tile column
6129 // read playfield elements from line
6130 for (line_ptr = line; *line_ptr; line_ptr++)
6132 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6134 // stop parsing playfield line if larger column than allowed
6135 if (x >= MAX_LEV_FIELDX)
6138 if (mapped_sb_element == EL_UNDEFINED)
6140 invalid_playfield_char = TRUE;
6145 level->field[x][y] = mapped_sb_element;
6147 // continue with next tile column
6150 level->fieldx = MAX(x, level->fieldx);
6153 if (invalid_playfield_char)
6155 // if first playfield line, treat invalid lines as comment lines
6157 reading_playfield = FALSE;
6162 // continue with next tile row
6170 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6171 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6173 if (!reading_playfield)
6175 level->no_valid_file = TRUE;
6177 Warn("cannot read level '%s' -- using empty level", filename);
6182 if (*level_name != '\0')
6184 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6185 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6187 else if (*last_comment != '\0')
6189 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6190 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6194 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6197 // set all empty fields beyond the border walls to invisible steel wall
6198 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6200 if ((x == 0 || x == level->fieldx - 1 ||
6201 y == 0 || y == level->fieldy - 1) &&
6202 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6203 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6204 level->field, level->fieldx, level->fieldy);
6207 // set special level settings for Sokoban levels
6208 SetLevelSettings_SB(level);
6210 if (load_xsb_to_ces)
6212 // special global settings can now be set in level template
6213 level->use_custom_template = TRUE;
6218 // -------------------------------------------------------------------------
6219 // functions for handling native levels
6220 // -------------------------------------------------------------------------
6222 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6223 struct LevelFileInfo *level_file_info,
6224 boolean level_info_only)
6226 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6227 level->no_valid_file = TRUE;
6230 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6231 struct LevelFileInfo *level_file_info,
6232 boolean level_info_only)
6236 // determine position of requested level inside level package
6237 if (level_file_info->packed)
6238 pos = level_file_info->nr - leveldir_current->first_level;
6240 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6241 level->no_valid_file = TRUE;
6244 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6245 struct LevelFileInfo *level_file_info,
6246 boolean level_info_only)
6248 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6249 level->no_valid_file = TRUE;
6252 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6254 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6255 CopyNativeLevel_RND_to_EM(level);
6256 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6257 CopyNativeLevel_RND_to_SP(level);
6258 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6259 CopyNativeLevel_RND_to_MM(level);
6262 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6264 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6265 CopyNativeLevel_EM_to_RND(level);
6266 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6267 CopyNativeLevel_SP_to_RND(level);
6268 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6269 CopyNativeLevel_MM_to_RND(level);
6272 void SaveNativeLevel(struct LevelInfo *level)
6274 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6276 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6277 char *filename = getLevelFilenameFromBasename(basename);
6279 CopyNativeLevel_RND_to_SP(level);
6280 CopyNativeTape_RND_to_SP(level);
6282 SaveNativeLevel_SP(filename);
6287 // ----------------------------------------------------------------------------
6288 // functions for loading generic level
6289 // ----------------------------------------------------------------------------
6291 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6292 struct LevelFileInfo *level_file_info,
6293 boolean level_info_only)
6295 // always start with reliable default values
6296 setLevelInfoToDefaults(level, level_info_only, TRUE);
6298 switch (level_file_info->type)
6300 case LEVEL_FILE_TYPE_RND:
6301 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6304 case LEVEL_FILE_TYPE_EM:
6305 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6306 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6309 case LEVEL_FILE_TYPE_SP:
6310 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6311 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6314 case LEVEL_FILE_TYPE_MM:
6315 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6316 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6319 case LEVEL_FILE_TYPE_DC:
6320 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6323 case LEVEL_FILE_TYPE_SB:
6324 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6328 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6332 // if level file is invalid, restore level structure to default values
6333 if (level->no_valid_file)
6334 setLevelInfoToDefaults(level, level_info_only, FALSE);
6336 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6337 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6339 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6340 CopyNativeLevel_Native_to_RND(level);
6343 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6345 static struct LevelFileInfo level_file_info;
6347 // always start with reliable default values
6348 setFileInfoToDefaults(&level_file_info);
6350 level_file_info.nr = 0; // unknown level number
6351 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6353 setString(&level_file_info.filename, filename);
6355 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6358 static void LoadLevel_InitVersion(struct LevelInfo *level)
6362 if (leveldir_current == NULL) // only when dumping level
6365 // all engine modifications also valid for levels which use latest engine
6366 if (level->game_version < VERSION_IDENT(3,2,0,5))
6368 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6369 level->time_score_base = 10;
6372 if (leveldir_current->latest_engine)
6374 // ---------- use latest game engine --------------------------------------
6376 /* For all levels which are forced to use the latest game engine version
6377 (normally all but user contributed, private and undefined levels), set
6378 the game engine version to the actual version; this allows for actual
6379 corrections in the game engine to take effect for existing, converted
6380 levels (from "classic" or other existing games) to make the emulation
6381 of the corresponding game more accurate, while (hopefully) not breaking
6382 existing levels created from other players. */
6384 level->game_version = GAME_VERSION_ACTUAL;
6386 /* Set special EM style gems behaviour: EM style gems slip down from
6387 normal, steel and growing wall. As this is a more fundamental change,
6388 it seems better to set the default behaviour to "off" (as it is more
6389 natural) and make it configurable in the level editor (as a property
6390 of gem style elements). Already existing converted levels (neither
6391 private nor contributed levels) are changed to the new behaviour. */
6393 if (level->file_version < FILE_VERSION_2_0)
6394 level->em_slippery_gems = TRUE;
6399 // ---------- use game engine the level was created with --------------------
6401 /* For all levels which are not forced to use the latest game engine
6402 version (normally user contributed, private and undefined levels),
6403 use the version of the game engine the levels were created for.
6405 Since 2.0.1, the game engine version is now directly stored
6406 in the level file (chunk "VERS"), so there is no need anymore
6407 to set the game version from the file version (except for old,
6408 pre-2.0 levels, where the game version is still taken from the
6409 file format version used to store the level -- see above). */
6411 // player was faster than enemies in 1.0.0 and before
6412 if (level->file_version == FILE_VERSION_1_0)
6413 for (i = 0; i < MAX_PLAYERS; i++)
6414 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6416 // default behaviour for EM style gems was "slippery" only in 2.0.1
6417 if (level->game_version == VERSION_IDENT(2,0,1,0))
6418 level->em_slippery_gems = TRUE;
6420 // springs could be pushed over pits before (pre-release version) 2.2.0
6421 if (level->game_version < VERSION_IDENT(2,2,0,0))
6422 level->use_spring_bug = TRUE;
6424 if (level->game_version < VERSION_IDENT(3,2,0,5))
6426 // time orb caused limited time in endless time levels before 3.2.0-5
6427 level->use_time_orb_bug = TRUE;
6429 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6430 level->block_snap_field = FALSE;
6432 // extra time score was same value as time left score before 3.2.0-5
6433 level->extra_time_score = level->score[SC_TIME_BONUS];
6436 if (level->game_version < VERSION_IDENT(3,2,0,7))
6438 // default behaviour for snapping was "not continuous" before 3.2.0-7
6439 level->continuous_snapping = FALSE;
6442 // only few elements were able to actively move into acid before 3.1.0
6443 // trigger settings did not exist before 3.1.0; set to default "any"
6444 if (level->game_version < VERSION_IDENT(3,1,0,0))
6446 // correct "can move into acid" settings (all zero in old levels)
6448 level->can_move_into_acid_bits = 0; // nothing can move into acid
6449 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6451 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6452 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6453 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6454 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6456 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6457 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6459 // correct trigger settings (stored as zero == "none" in old levels)
6461 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6463 int element = EL_CUSTOM_START + i;
6464 struct ElementInfo *ei = &element_info[element];
6466 for (j = 0; j < ei->num_change_pages; j++)
6468 struct ElementChangeInfo *change = &ei->change_page[j];
6470 change->trigger_player = CH_PLAYER_ANY;
6471 change->trigger_page = CH_PAGE_ANY;
6476 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6478 int element = EL_CUSTOM_256;
6479 struct ElementInfo *ei = &element_info[element];
6480 struct ElementChangeInfo *change = &ei->change_page[0];
6482 /* This is needed to fix a problem that was caused by a bugfix in function
6483 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6484 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6485 not replace walkable elements, but instead just placed the player on it,
6486 without placing the Sokoban field under the player). Unfortunately, this
6487 breaks "Snake Bite" style levels when the snake is halfway through a door
6488 that just closes (the snake head is still alive and can be moved in this
6489 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6490 player (without Sokoban element) which then gets killed as designed). */
6492 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6493 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6494 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6495 change->target_element = EL_PLAYER_1;
6498 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6499 if (level->game_version < VERSION_IDENT(3,2,5,0))
6501 /* This is needed to fix a problem that was caused by a bugfix in function
6502 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6503 corrects the behaviour when a custom element changes to another custom
6504 element with a higher element number that has change actions defined.
6505 Normally, only one change per frame is allowed for custom elements.
6506 Therefore, it is checked if a custom element already changed in the
6507 current frame; if it did, subsequent changes are suppressed.
6508 Unfortunately, this is only checked for element changes, but not for
6509 change actions, which are still executed. As the function above loops
6510 through all custom elements from lower to higher, an element change
6511 resulting in a lower CE number won't be checked again, while a target
6512 element with a higher number will also be checked, and potential change
6513 actions will get executed for this CE, too (which is wrong), while
6514 further changes are ignored (which is correct). As this bugfix breaks
6515 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6516 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6517 behaviour for existing levels and tapes that make use of this bug */
6519 level->use_action_after_change_bug = TRUE;
6522 // not centering level after relocating player was default only in 3.2.3
6523 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6524 level->shifted_relocation = TRUE;
6526 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6527 if (level->game_version < VERSION_IDENT(3,2,6,0))
6528 level->em_explodes_by_fire = TRUE;
6530 // levels were solved by the first player entering an exit up to 4.1.0.0
6531 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6532 level->solved_by_one_player = TRUE;
6534 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6535 if (level->game_version < VERSION_IDENT(4,1,1,1))
6536 level->use_life_bugs = TRUE;
6538 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6539 if (level->game_version < VERSION_IDENT(4,1,1,1))
6540 level->sb_objects_needed = FALSE;
6542 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6543 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6544 level->finish_dig_collect = FALSE;
6546 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6547 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6548 level->keep_walkable_ce = TRUE;
6551 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6553 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6556 // check if this level is (not) a Sokoban level
6557 for (y = 0; y < level->fieldy; y++)
6558 for (x = 0; x < level->fieldx; x++)
6559 if (!IS_SB_ELEMENT(Tile[x][y]))
6560 is_sokoban_level = FALSE;
6562 if (is_sokoban_level)
6564 // set special level settings for Sokoban levels
6565 SetLevelSettings_SB(level);
6569 static void LoadLevel_InitSettings(struct LevelInfo *level)
6571 // adjust level settings for (non-native) Sokoban-style levels
6572 LoadLevel_InitSettings_SB(level);
6574 // rename levels with title "nameless level" or if renaming is forced
6575 if (leveldir_current->empty_level_name != NULL &&
6576 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6577 leveldir_current->force_level_name))
6578 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6579 leveldir_current->empty_level_name, level_nr);
6582 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6586 // map elements that have changed in newer versions
6587 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6588 level->game_version);
6589 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6590 for (x = 0; x < 3; x++)
6591 for (y = 0; y < 3; y++)
6592 level->yamyam_content[i].e[x][y] =
6593 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6594 level->game_version);
6598 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6602 // map custom element change events that have changed in newer versions
6603 // (these following values were accidentally changed in version 3.0.1)
6604 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6605 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6607 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6609 int element = EL_CUSTOM_START + i;
6611 // order of checking and copying events to be mapped is important
6612 // (do not change the start and end value -- they are constant)
6613 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6615 if (HAS_CHANGE_EVENT(element, j - 2))
6617 SET_CHANGE_EVENT(element, j - 2, FALSE);
6618 SET_CHANGE_EVENT(element, j, TRUE);
6622 // order of checking and copying events to be mapped is important
6623 // (do not change the start and end value -- they are constant)
6624 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6626 if (HAS_CHANGE_EVENT(element, j - 1))
6628 SET_CHANGE_EVENT(element, j - 1, FALSE);
6629 SET_CHANGE_EVENT(element, j, TRUE);
6635 // initialize "can_change" field for old levels with only one change page
6636 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6638 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6640 int element = EL_CUSTOM_START + i;
6642 if (CAN_CHANGE(element))
6643 element_info[element].change->can_change = TRUE;
6647 // correct custom element values (for old levels without these options)
6648 if (level->game_version < VERSION_IDENT(3,1,1,0))
6650 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6652 int element = EL_CUSTOM_START + i;
6653 struct ElementInfo *ei = &element_info[element];
6655 if (ei->access_direction == MV_NO_DIRECTION)
6656 ei->access_direction = MV_ALL_DIRECTIONS;
6660 // correct custom element values (fix invalid values for all versions)
6663 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6665 int element = EL_CUSTOM_START + i;
6666 struct ElementInfo *ei = &element_info[element];
6668 for (j = 0; j < ei->num_change_pages; j++)
6670 struct ElementChangeInfo *change = &ei->change_page[j];
6672 if (change->trigger_player == CH_PLAYER_NONE)
6673 change->trigger_player = CH_PLAYER_ANY;
6675 if (change->trigger_side == CH_SIDE_NONE)
6676 change->trigger_side = CH_SIDE_ANY;
6681 // initialize "can_explode" field for old levels which did not store this
6682 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6683 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6685 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6687 int element = EL_CUSTOM_START + i;
6689 if (EXPLODES_1X1_OLD(element))
6690 element_info[element].explosion_type = EXPLODES_1X1;
6692 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6693 EXPLODES_SMASHED(element) ||
6694 EXPLODES_IMPACT(element)));
6698 // correct previously hard-coded move delay values for maze runner style
6699 if (level->game_version < VERSION_IDENT(3,1,1,0))
6701 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6703 int element = EL_CUSTOM_START + i;
6705 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6707 // previously hard-coded and therefore ignored
6708 element_info[element].move_delay_fixed = 9;
6709 element_info[element].move_delay_random = 0;
6714 // set some other uninitialized values of custom elements in older levels
6715 if (level->game_version < VERSION_IDENT(3,1,0,0))
6717 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6719 int element = EL_CUSTOM_START + i;
6721 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6723 element_info[element].explosion_delay = 17;
6724 element_info[element].ignition_delay = 8;
6728 // set mouse click change events to work for left/middle/right mouse button
6729 if (level->game_version < VERSION_IDENT(4,2,3,0))
6731 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6733 int element = EL_CUSTOM_START + i;
6734 struct ElementInfo *ei = &element_info[element];
6736 for (j = 0; j < ei->num_change_pages; j++)
6738 struct ElementChangeInfo *change = &ei->change_page[j];
6740 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6741 change->has_event[CE_PRESSED_BY_MOUSE] ||
6742 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6743 change->has_event[CE_MOUSE_PRESSED_ON_X])
6744 change->trigger_side = CH_SIDE_ANY;
6750 static void LoadLevel_InitElements(struct LevelInfo *level)
6752 LoadLevel_InitStandardElements(level);
6754 if (level->file_has_custom_elements)
6755 LoadLevel_InitCustomElements(level);
6757 // initialize element properties for level editor etc.
6758 InitElementPropertiesEngine(level->game_version);
6759 InitElementPropertiesGfxElement();
6762 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6766 // map elements that have changed in newer versions
6767 for (y = 0; y < level->fieldy; y++)
6768 for (x = 0; x < level->fieldx; x++)
6769 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6770 level->game_version);
6772 // clear unused playfield data (nicer if level gets resized in editor)
6773 for (x = 0; x < MAX_LEV_FIELDX; x++)
6774 for (y = 0; y < MAX_LEV_FIELDY; y++)
6775 if (x >= level->fieldx || y >= level->fieldy)
6776 level->field[x][y] = EL_EMPTY;
6778 // copy elements to runtime playfield array
6779 for (x = 0; x < MAX_LEV_FIELDX; x++)
6780 for (y = 0; y < MAX_LEV_FIELDY; y++)
6781 Tile[x][y] = level->field[x][y];
6783 // initialize level size variables for faster access
6784 lev_fieldx = level->fieldx;
6785 lev_fieldy = level->fieldy;
6787 // determine border element for this level
6788 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6789 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6794 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6796 struct LevelFileInfo *level_file_info = &level->file_info;
6798 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6799 CopyNativeLevel_RND_to_Native(level);
6802 static void LoadLevelTemplate_LoadAndInit(void)
6804 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6806 LoadLevel_InitVersion(&level_template);
6807 LoadLevel_InitElements(&level_template);
6808 LoadLevel_InitSettings(&level_template);
6810 ActivateLevelTemplate();
6813 void LoadLevelTemplate(int nr)
6815 if (!fileExists(getGlobalLevelTemplateFilename()))
6817 Warn("no level template found for this level");
6822 setLevelFileInfo(&level_template.file_info, nr);
6824 LoadLevelTemplate_LoadAndInit();
6827 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6829 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6831 LoadLevelTemplate_LoadAndInit();
6834 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6836 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6838 if (level.use_custom_template)
6840 if (network_level != NULL)
6841 LoadNetworkLevelTemplate(network_level);
6843 LoadLevelTemplate(-1);
6846 LoadLevel_InitVersion(&level);
6847 LoadLevel_InitElements(&level);
6848 LoadLevel_InitPlayfield(&level);
6849 LoadLevel_InitSettings(&level);
6851 LoadLevel_InitNativeEngines(&level);
6854 void LoadLevel(int nr)
6856 SetLevelSetInfo(leveldir_current->identifier, nr);
6858 setLevelFileInfo(&level.file_info, nr);
6860 LoadLevel_LoadAndInit(NULL);
6863 void LoadLevelInfoOnly(int nr)
6865 setLevelFileInfo(&level.file_info, nr);
6867 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6870 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6872 SetLevelSetInfo(network_level->leveldir_identifier,
6873 network_level->file_info.nr);
6875 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6877 LoadLevel_LoadAndInit(network_level);
6880 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6884 chunk_size += putFileVersion(file, level->file_version);
6885 chunk_size += putFileVersion(file, level->game_version);
6890 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6894 chunk_size += putFile16BitBE(file, level->creation_date.year);
6895 chunk_size += putFile8Bit(file, level->creation_date.month);
6896 chunk_size += putFile8Bit(file, level->creation_date.day);
6901 #if ENABLE_HISTORIC_CHUNKS
6902 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6906 putFile8Bit(file, level->fieldx);
6907 putFile8Bit(file, level->fieldy);
6909 putFile16BitBE(file, level->time);
6910 putFile16BitBE(file, level->gems_needed);
6912 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6913 putFile8Bit(file, level->name[i]);
6915 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6916 putFile8Bit(file, level->score[i]);
6918 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6919 for (y = 0; y < 3; y++)
6920 for (x = 0; x < 3; x++)
6921 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6922 level->yamyam_content[i].e[x][y]));
6923 putFile8Bit(file, level->amoeba_speed);
6924 putFile8Bit(file, level->time_magic_wall);
6925 putFile8Bit(file, level->time_wheel);
6926 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6927 level->amoeba_content));
6928 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6929 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6930 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6931 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6933 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6935 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6936 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6937 putFile32BitBE(file, level->can_move_into_acid_bits);
6938 putFile8Bit(file, level->dont_collide_with_bits);
6940 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6941 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6943 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6944 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6945 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6947 putFile8Bit(file, level->game_engine_type);
6949 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6953 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6958 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6959 chunk_size += putFile8Bit(file, level->name[i]);
6964 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6969 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6970 chunk_size += putFile8Bit(file, level->author[i]);
6975 #if ENABLE_HISTORIC_CHUNKS
6976 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6981 for (y = 0; y < level->fieldy; y++)
6982 for (x = 0; x < level->fieldx; x++)
6983 if (level->encoding_16bit_field)
6984 chunk_size += putFile16BitBE(file, level->field[x][y]);
6986 chunk_size += putFile8Bit(file, level->field[x][y]);
6992 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6997 for (y = 0; y < level->fieldy; y++)
6998 for (x = 0; x < level->fieldx; x++)
6999 chunk_size += putFile16BitBE(file, level->field[x][y]);
7004 #if ENABLE_HISTORIC_CHUNKS
7005 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7009 putFile8Bit(file, EL_YAMYAM);
7010 putFile8Bit(file, level->num_yamyam_contents);
7011 putFile8Bit(file, 0);
7012 putFile8Bit(file, 0);
7014 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7015 for (y = 0; y < 3; y++)
7016 for (x = 0; x < 3; x++)
7017 if (level->encoding_16bit_field)
7018 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7020 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7024 #if ENABLE_HISTORIC_CHUNKS
7025 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7028 int num_contents, content_xsize, content_ysize;
7029 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7031 if (element == EL_YAMYAM)
7033 num_contents = level->num_yamyam_contents;
7037 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7038 for (y = 0; y < 3; y++)
7039 for (x = 0; x < 3; x++)
7040 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7042 else if (element == EL_BD_AMOEBA)
7048 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7049 for (y = 0; y < 3; y++)
7050 for (x = 0; x < 3; x++)
7051 content_array[i][x][y] = EL_EMPTY;
7052 content_array[0][0][0] = level->amoeba_content;
7056 // chunk header already written -- write empty chunk data
7057 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7059 Warn("cannot save content for element '%d'", element);
7064 putFile16BitBE(file, element);
7065 putFile8Bit(file, num_contents);
7066 putFile8Bit(file, content_xsize);
7067 putFile8Bit(file, content_ysize);
7069 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7071 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7072 for (y = 0; y < 3; y++)
7073 for (x = 0; x < 3; x++)
7074 putFile16BitBE(file, content_array[i][x][y]);
7078 #if ENABLE_HISTORIC_CHUNKS
7079 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7081 int envelope_nr = element - EL_ENVELOPE_1;
7082 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7086 chunk_size += putFile16BitBE(file, element);
7087 chunk_size += putFile16BitBE(file, envelope_len);
7088 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7089 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7091 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7092 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7094 for (i = 0; i < envelope_len; i++)
7095 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7101 #if ENABLE_HISTORIC_CHUNKS
7102 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7103 int num_changed_custom_elements)
7107 putFile16BitBE(file, num_changed_custom_elements);
7109 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7111 int element = EL_CUSTOM_START + i;
7113 struct ElementInfo *ei = &element_info[element];
7115 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7117 if (check < num_changed_custom_elements)
7119 putFile16BitBE(file, element);
7120 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7127 if (check != num_changed_custom_elements) // should not happen
7128 Warn("inconsistent number of custom element properties");
7132 #if ENABLE_HISTORIC_CHUNKS
7133 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7134 int num_changed_custom_elements)
7138 putFile16BitBE(file, num_changed_custom_elements);
7140 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7142 int element = EL_CUSTOM_START + i;
7144 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7146 if (check < num_changed_custom_elements)
7148 putFile16BitBE(file, element);
7149 putFile16BitBE(file, element_info[element].change->target_element);
7156 if (check != num_changed_custom_elements) // should not happen
7157 Warn("inconsistent number of custom target elements");
7161 #if ENABLE_HISTORIC_CHUNKS
7162 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7163 int num_changed_custom_elements)
7165 int i, j, x, y, check = 0;
7167 putFile16BitBE(file, num_changed_custom_elements);
7169 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7171 int element = EL_CUSTOM_START + i;
7172 struct ElementInfo *ei = &element_info[element];
7174 if (ei->modified_settings)
7176 if (check < num_changed_custom_elements)
7178 putFile16BitBE(file, element);
7180 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7181 putFile8Bit(file, ei->description[j]);
7183 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7185 // some free bytes for future properties and padding
7186 WriteUnusedBytesToFile(file, 7);
7188 putFile8Bit(file, ei->use_gfx_element);
7189 putFile16BitBE(file, ei->gfx_element_initial);
7191 putFile8Bit(file, ei->collect_score_initial);
7192 putFile8Bit(file, ei->collect_count_initial);
7194 putFile16BitBE(file, ei->push_delay_fixed);
7195 putFile16BitBE(file, ei->push_delay_random);
7196 putFile16BitBE(file, ei->move_delay_fixed);
7197 putFile16BitBE(file, ei->move_delay_random);
7199 putFile16BitBE(file, ei->move_pattern);
7200 putFile8Bit(file, ei->move_direction_initial);
7201 putFile8Bit(file, ei->move_stepsize);
7203 for (y = 0; y < 3; y++)
7204 for (x = 0; x < 3; x++)
7205 putFile16BitBE(file, ei->content.e[x][y]);
7207 putFile32BitBE(file, ei->change->events);
7209 putFile16BitBE(file, ei->change->target_element);
7211 putFile16BitBE(file, ei->change->delay_fixed);
7212 putFile16BitBE(file, ei->change->delay_random);
7213 putFile16BitBE(file, ei->change->delay_frames);
7215 putFile16BitBE(file, ei->change->initial_trigger_element);
7217 putFile8Bit(file, ei->change->explode);
7218 putFile8Bit(file, ei->change->use_target_content);
7219 putFile8Bit(file, ei->change->only_if_complete);
7220 putFile8Bit(file, ei->change->use_random_replace);
7222 putFile8Bit(file, ei->change->random_percentage);
7223 putFile8Bit(file, ei->change->replace_when);
7225 for (y = 0; y < 3; y++)
7226 for (x = 0; x < 3; x++)
7227 putFile16BitBE(file, ei->change->content.e[x][y]);
7229 putFile8Bit(file, ei->slippery_type);
7231 // some free bytes for future properties and padding
7232 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7239 if (check != num_changed_custom_elements) // should not happen
7240 Warn("inconsistent number of custom element properties");
7244 #if ENABLE_HISTORIC_CHUNKS
7245 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7247 struct ElementInfo *ei = &element_info[element];
7250 // ---------- custom element base property values (96 bytes) ----------------
7252 putFile16BitBE(file, element);
7254 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7255 putFile8Bit(file, ei->description[i]);
7257 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7259 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7261 putFile8Bit(file, ei->num_change_pages);
7263 putFile16BitBE(file, ei->ce_value_fixed_initial);
7264 putFile16BitBE(file, ei->ce_value_random_initial);
7265 putFile8Bit(file, ei->use_last_ce_value);
7267 putFile8Bit(file, ei->use_gfx_element);
7268 putFile16BitBE(file, ei->gfx_element_initial);
7270 putFile8Bit(file, ei->collect_score_initial);
7271 putFile8Bit(file, ei->collect_count_initial);
7273 putFile8Bit(file, ei->drop_delay_fixed);
7274 putFile8Bit(file, ei->push_delay_fixed);
7275 putFile8Bit(file, ei->drop_delay_random);
7276 putFile8Bit(file, ei->push_delay_random);
7277 putFile16BitBE(file, ei->move_delay_fixed);
7278 putFile16BitBE(file, ei->move_delay_random);
7280 // bits 0 - 15 of "move_pattern" ...
7281 putFile16BitBE(file, ei->move_pattern & 0xffff);
7282 putFile8Bit(file, ei->move_direction_initial);
7283 putFile8Bit(file, ei->move_stepsize);
7285 putFile8Bit(file, ei->slippery_type);
7287 for (y = 0; y < 3; y++)
7288 for (x = 0; x < 3; x++)
7289 putFile16BitBE(file, ei->content.e[x][y]);
7291 putFile16BitBE(file, ei->move_enter_element);
7292 putFile16BitBE(file, ei->move_leave_element);
7293 putFile8Bit(file, ei->move_leave_type);
7295 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7296 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7298 putFile8Bit(file, ei->access_direction);
7300 putFile8Bit(file, ei->explosion_delay);
7301 putFile8Bit(file, ei->ignition_delay);
7302 putFile8Bit(file, ei->explosion_type);
7304 // some free bytes for future custom property values and padding
7305 WriteUnusedBytesToFile(file, 1);
7307 // ---------- change page property values (48 bytes) ------------------------
7309 for (i = 0; i < ei->num_change_pages; i++)
7311 struct ElementChangeInfo *change = &ei->change_page[i];
7312 unsigned int event_bits;
7314 // bits 0 - 31 of "has_event[]" ...
7316 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7317 if (change->has_event[j])
7318 event_bits |= (1 << j);
7319 putFile32BitBE(file, event_bits);
7321 putFile16BitBE(file, change->target_element);
7323 putFile16BitBE(file, change->delay_fixed);
7324 putFile16BitBE(file, change->delay_random);
7325 putFile16BitBE(file, change->delay_frames);
7327 putFile16BitBE(file, change->initial_trigger_element);
7329 putFile8Bit(file, change->explode);
7330 putFile8Bit(file, change->use_target_content);
7331 putFile8Bit(file, change->only_if_complete);
7332 putFile8Bit(file, change->use_random_replace);
7334 putFile8Bit(file, change->random_percentage);
7335 putFile8Bit(file, change->replace_when);
7337 for (y = 0; y < 3; y++)
7338 for (x = 0; x < 3; x++)
7339 putFile16BitBE(file, change->target_content.e[x][y]);
7341 putFile8Bit(file, change->can_change);
7343 putFile8Bit(file, change->trigger_side);
7345 putFile8Bit(file, change->trigger_player);
7346 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7347 log_2(change->trigger_page)));
7349 putFile8Bit(file, change->has_action);
7350 putFile8Bit(file, change->action_type);
7351 putFile8Bit(file, change->action_mode);
7352 putFile16BitBE(file, change->action_arg);
7354 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7356 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7357 if (change->has_event[j])
7358 event_bits |= (1 << (j - 32));
7359 putFile8Bit(file, event_bits);
7364 #if ENABLE_HISTORIC_CHUNKS
7365 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7367 struct ElementInfo *ei = &element_info[element];
7368 struct ElementGroupInfo *group = ei->group;
7371 putFile16BitBE(file, element);
7373 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7374 putFile8Bit(file, ei->description[i]);
7376 putFile8Bit(file, group->num_elements);
7378 putFile8Bit(file, ei->use_gfx_element);
7379 putFile16BitBE(file, ei->gfx_element_initial);
7381 putFile8Bit(file, group->choice_mode);
7383 // some free bytes for future values and padding
7384 WriteUnusedBytesToFile(file, 3);
7386 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7387 putFile16BitBE(file, group->element[i]);
7391 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7392 boolean write_element)
7394 int save_type = entry->save_type;
7395 int data_type = entry->data_type;
7396 int conf_type = entry->conf_type;
7397 int byte_mask = conf_type & CONF_MASK_BYTES;
7398 int element = entry->element;
7399 int default_value = entry->default_value;
7401 boolean modified = FALSE;
7403 if (byte_mask != CONF_MASK_MULTI_BYTES)
7405 void *value_ptr = entry->value;
7406 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7409 // check if any settings have been modified before saving them
7410 if (value != default_value)
7413 // do not save if explicitly told or if unmodified default settings
7414 if ((save_type == SAVE_CONF_NEVER) ||
7415 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7419 num_bytes += putFile16BitBE(file, element);
7421 num_bytes += putFile8Bit(file, conf_type);
7422 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7423 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7424 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7427 else if (data_type == TYPE_STRING)
7429 char *default_string = entry->default_string;
7430 char *string = (char *)(entry->value);
7431 int string_length = strlen(string);
7434 // check if any settings have been modified before saving them
7435 if (!strEqual(string, default_string))
7438 // do not save if explicitly told or if unmodified default settings
7439 if ((save_type == SAVE_CONF_NEVER) ||
7440 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7444 num_bytes += putFile16BitBE(file, element);
7446 num_bytes += putFile8Bit(file, conf_type);
7447 num_bytes += putFile16BitBE(file, string_length);
7449 for (i = 0; i < string_length; i++)
7450 num_bytes += putFile8Bit(file, string[i]);
7452 else if (data_type == TYPE_ELEMENT_LIST)
7454 int *element_array = (int *)(entry->value);
7455 int num_elements = *(int *)(entry->num_entities);
7458 // check if any settings have been modified before saving them
7459 for (i = 0; i < num_elements; i++)
7460 if (element_array[i] != default_value)
7463 // do not save if explicitly told or if unmodified default settings
7464 if ((save_type == SAVE_CONF_NEVER) ||
7465 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7469 num_bytes += putFile16BitBE(file, element);
7471 num_bytes += putFile8Bit(file, conf_type);
7472 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7474 for (i = 0; i < num_elements; i++)
7475 num_bytes += putFile16BitBE(file, element_array[i]);
7477 else if (data_type == TYPE_CONTENT_LIST)
7479 struct Content *content = (struct Content *)(entry->value);
7480 int num_contents = *(int *)(entry->num_entities);
7483 // check if any settings have been modified before saving them
7484 for (i = 0; i < num_contents; i++)
7485 for (y = 0; y < 3; y++)
7486 for (x = 0; x < 3; x++)
7487 if (content[i].e[x][y] != default_value)
7490 // do not save if explicitly told or if unmodified default settings
7491 if ((save_type == SAVE_CONF_NEVER) ||
7492 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7496 num_bytes += putFile16BitBE(file, element);
7498 num_bytes += putFile8Bit(file, conf_type);
7499 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7501 for (i = 0; i < num_contents; i++)
7502 for (y = 0; y < 3; y++)
7503 for (x = 0; x < 3; x++)
7504 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7510 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7515 li = *level; // copy level data into temporary buffer
7517 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7518 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7523 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7528 li = *level; // copy level data into temporary buffer
7530 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7531 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7536 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7538 int envelope_nr = element - EL_ENVELOPE_1;
7542 chunk_size += putFile16BitBE(file, element);
7544 // copy envelope data into temporary buffer
7545 xx_envelope = level->envelope[envelope_nr];
7547 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7548 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7553 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7555 struct ElementInfo *ei = &element_info[element];
7559 chunk_size += putFile16BitBE(file, element);
7561 xx_ei = *ei; // copy element data into temporary buffer
7563 // set default description string for this specific element
7564 strcpy(xx_default_description, getDefaultElementDescription(ei));
7566 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7567 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7569 for (i = 0; i < ei->num_change_pages; i++)
7571 struct ElementChangeInfo *change = &ei->change_page[i];
7573 xx_current_change_page = i;
7575 xx_change = *change; // copy change data into temporary buffer
7578 setEventBitsFromEventFlags(change);
7580 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7581 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7588 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7590 struct ElementInfo *ei = &element_info[element];
7591 struct ElementGroupInfo *group = ei->group;
7595 chunk_size += putFile16BitBE(file, element);
7597 xx_ei = *ei; // copy element data into temporary buffer
7598 xx_group = *group; // copy group data into temporary buffer
7600 // set default description string for this specific element
7601 strcpy(xx_default_description, getDefaultElementDescription(ei));
7603 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7604 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7609 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7611 struct ElementInfo *ei = &element_info[element];
7615 chunk_size += putFile16BitBE(file, element);
7617 xx_ei = *ei; // copy element data into temporary buffer
7619 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7620 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7625 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7626 boolean save_as_template)
7632 if (!(file = fopen(filename, MODE_WRITE)))
7634 Warn("cannot save level file '%s'", filename);
7639 level->file_version = FILE_VERSION_ACTUAL;
7640 level->game_version = GAME_VERSION_ACTUAL;
7642 level->creation_date = getCurrentDate();
7644 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7645 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7647 chunk_size = SaveLevel_VERS(NULL, level);
7648 putFileChunkBE(file, "VERS", chunk_size);
7649 SaveLevel_VERS(file, level);
7651 chunk_size = SaveLevel_DATE(NULL, level);
7652 putFileChunkBE(file, "DATE", chunk_size);
7653 SaveLevel_DATE(file, level);
7655 chunk_size = SaveLevel_NAME(NULL, level);
7656 putFileChunkBE(file, "NAME", chunk_size);
7657 SaveLevel_NAME(file, level);
7659 chunk_size = SaveLevel_AUTH(NULL, level);
7660 putFileChunkBE(file, "AUTH", chunk_size);
7661 SaveLevel_AUTH(file, level);
7663 chunk_size = SaveLevel_INFO(NULL, level);
7664 putFileChunkBE(file, "INFO", chunk_size);
7665 SaveLevel_INFO(file, level);
7667 chunk_size = SaveLevel_BODY(NULL, level);
7668 putFileChunkBE(file, "BODY", chunk_size);
7669 SaveLevel_BODY(file, level);
7671 chunk_size = SaveLevel_ELEM(NULL, level);
7672 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7674 putFileChunkBE(file, "ELEM", chunk_size);
7675 SaveLevel_ELEM(file, level);
7678 for (i = 0; i < NUM_ENVELOPES; i++)
7680 int element = EL_ENVELOPE_1 + i;
7682 chunk_size = SaveLevel_NOTE(NULL, level, element);
7683 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7685 putFileChunkBE(file, "NOTE", chunk_size);
7686 SaveLevel_NOTE(file, level, element);
7690 // if not using template level, check for non-default custom/group elements
7691 if (!level->use_custom_template || save_as_template)
7693 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7695 int element = EL_CUSTOM_START + i;
7697 chunk_size = SaveLevel_CUSX(NULL, level, element);
7698 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7700 putFileChunkBE(file, "CUSX", chunk_size);
7701 SaveLevel_CUSX(file, level, element);
7705 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7707 int element = EL_GROUP_START + i;
7709 chunk_size = SaveLevel_GRPX(NULL, level, element);
7710 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7712 putFileChunkBE(file, "GRPX", chunk_size);
7713 SaveLevel_GRPX(file, level, element);
7717 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7719 int element = GET_EMPTY_ELEMENT(i);
7721 chunk_size = SaveLevel_EMPX(NULL, level, element);
7722 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7724 putFileChunkBE(file, "EMPX", chunk_size);
7725 SaveLevel_EMPX(file, level, element);
7732 SetFilePermissions(filename, PERMS_PRIVATE);
7735 void SaveLevel(int nr)
7737 char *filename = getDefaultLevelFilename(nr);
7739 SaveLevelFromFilename(&level, filename, FALSE);
7742 void SaveLevelTemplate(void)
7744 char *filename = getLocalLevelTemplateFilename();
7746 SaveLevelFromFilename(&level, filename, TRUE);
7749 boolean SaveLevelChecked(int nr)
7751 char *filename = getDefaultLevelFilename(nr);
7752 boolean new_level = !fileExists(filename);
7753 boolean level_saved = FALSE;
7755 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7760 Request("Level saved!", REQ_CONFIRM);
7768 void DumpLevel(struct LevelInfo *level)
7770 if (level->no_level_file || level->no_valid_file)
7772 Warn("cannot dump -- no valid level file found");
7778 Print("Level xxx (file version %08d, game version %08d)\n",
7779 level->file_version, level->game_version);
7782 Print("Level author: '%s'\n", level->author);
7783 Print("Level title: '%s'\n", level->name);
7785 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7787 Print("Level time: %d seconds\n", level->time);
7788 Print("Gems needed: %d\n", level->gems_needed);
7790 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7791 Print("Time for wheel: %d seconds\n", level->time_wheel);
7792 Print("Time for light: %d seconds\n", level->time_light);
7793 Print("Time for timegate: %d seconds\n", level->time_timegate);
7795 Print("Amoeba speed: %d\n", level->amoeba_speed);
7798 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7799 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7800 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7801 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7802 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7803 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7809 for (i = 0; i < NUM_ENVELOPES; i++)
7811 char *text = level->envelope[i].text;
7812 int text_len = strlen(text);
7813 boolean has_text = FALSE;
7815 for (j = 0; j < text_len; j++)
7816 if (text[j] != ' ' && text[j] != '\n')
7822 Print("Envelope %d:\n'%s'\n", i + 1, text);
7830 void DumpLevels(void)
7832 static LevelDirTree *dumplevel_leveldir = NULL;
7834 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7835 global.dumplevel_leveldir);
7837 if (dumplevel_leveldir == NULL)
7838 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7840 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7841 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7842 Fail("no such level number: %d", global.dumplevel_level_nr);
7844 leveldir_current = dumplevel_leveldir;
7846 LoadLevel(global.dumplevel_level_nr);
7853 // ============================================================================
7854 // tape file functions
7855 // ============================================================================
7857 static void setTapeInfoToDefaults(void)
7861 // always start with reliable default values (empty tape)
7864 // default values (also for pre-1.2 tapes) with only the first player
7865 tape.player_participates[0] = TRUE;
7866 for (i = 1; i < MAX_PLAYERS; i++)
7867 tape.player_participates[i] = FALSE;
7869 // at least one (default: the first) player participates in every tape
7870 tape.num_participating_players = 1;
7872 tape.property_bits = TAPE_PROPERTY_NONE;
7874 tape.level_nr = level_nr;
7876 tape.changed = FALSE;
7878 tape.recording = FALSE;
7879 tape.playing = FALSE;
7880 tape.pausing = FALSE;
7882 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7883 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7885 tape.no_info_chunk = TRUE;
7886 tape.no_valid_file = FALSE;
7889 static int getTapePosSize(struct TapeInfo *tape)
7891 int tape_pos_size = 0;
7893 if (tape->use_key_actions)
7894 tape_pos_size += tape->num_participating_players;
7896 if (tape->use_mouse_actions)
7897 tape_pos_size += 3; // x and y position and mouse button mask
7899 tape_pos_size += 1; // tape action delay value
7901 return tape_pos_size;
7904 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7906 tape->use_key_actions = FALSE;
7907 tape->use_mouse_actions = FALSE;
7909 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7910 tape->use_key_actions = TRUE;
7912 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7913 tape->use_mouse_actions = TRUE;
7916 static int getTapeActionValue(struct TapeInfo *tape)
7918 return (tape->use_key_actions &&
7919 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7920 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7921 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7922 TAPE_ACTIONS_DEFAULT);
7925 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7927 tape->file_version = getFileVersion(file);
7928 tape->game_version = getFileVersion(file);
7933 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7937 tape->random_seed = getFile32BitBE(file);
7938 tape->date = getFile32BitBE(file);
7939 tape->length = getFile32BitBE(file);
7941 // read header fields that are new since version 1.2
7942 if (tape->file_version >= FILE_VERSION_1_2)
7944 byte store_participating_players = getFile8Bit(file);
7947 // since version 1.2, tapes store which players participate in the tape
7948 tape->num_participating_players = 0;
7949 for (i = 0; i < MAX_PLAYERS; i++)
7951 tape->player_participates[i] = FALSE;
7953 if (store_participating_players & (1 << i))
7955 tape->player_participates[i] = TRUE;
7956 tape->num_participating_players++;
7960 setTapeActionFlags(tape, getFile8Bit(file));
7962 tape->property_bits = getFile8Bit(file);
7964 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7966 engine_version = getFileVersion(file);
7967 if (engine_version > 0)
7968 tape->engine_version = engine_version;
7970 tape->engine_version = tape->game_version;
7976 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7978 tape->scr_fieldx = getFile8Bit(file);
7979 tape->scr_fieldy = getFile8Bit(file);
7984 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7986 char *level_identifier = NULL;
7987 int level_identifier_size;
7990 tape->no_info_chunk = FALSE;
7992 level_identifier_size = getFile16BitBE(file);
7994 level_identifier = checked_malloc(level_identifier_size);
7996 for (i = 0; i < level_identifier_size; i++)
7997 level_identifier[i] = getFile8Bit(file);
7999 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8000 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8002 checked_free(level_identifier);
8004 tape->level_nr = getFile16BitBE(file);
8006 chunk_size = 2 + level_identifier_size + 2;
8011 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8014 int tape_pos_size = getTapePosSize(tape);
8015 int chunk_size_expected = tape_pos_size * tape->length;
8017 if (chunk_size_expected != chunk_size)
8019 ReadUnusedBytesFromFile(file, chunk_size);
8020 return chunk_size_expected;
8023 for (i = 0; i < tape->length; i++)
8025 if (i >= MAX_TAPE_LEN)
8027 Warn("tape truncated -- size exceeds maximum tape size %d",
8030 // tape too large; read and ignore remaining tape data from this chunk
8031 for (;i < tape->length; i++)
8032 ReadUnusedBytesFromFile(file, tape_pos_size);
8037 if (tape->use_key_actions)
8039 for (j = 0; j < MAX_PLAYERS; j++)
8041 tape->pos[i].action[j] = MV_NONE;
8043 if (tape->player_participates[j])
8044 tape->pos[i].action[j] = getFile8Bit(file);
8048 if (tape->use_mouse_actions)
8050 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8051 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8052 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8055 tape->pos[i].delay = getFile8Bit(file);
8057 if (tape->file_version == FILE_VERSION_1_0)
8059 // eliminate possible diagonal moves in old tapes
8060 // this is only for backward compatibility
8062 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8063 byte action = tape->pos[i].action[0];
8064 int k, num_moves = 0;
8066 for (k = 0; k<4; k++)
8068 if (action & joy_dir[k])
8070 tape->pos[i + num_moves].action[0] = joy_dir[k];
8072 tape->pos[i + num_moves].delay = 0;
8081 tape->length += num_moves;
8084 else if (tape->file_version < FILE_VERSION_2_0)
8086 // convert pre-2.0 tapes to new tape format
8088 if (tape->pos[i].delay > 1)
8091 tape->pos[i + 1] = tape->pos[i];
8092 tape->pos[i + 1].delay = 1;
8095 for (j = 0; j < MAX_PLAYERS; j++)
8096 tape->pos[i].action[j] = MV_NONE;
8097 tape->pos[i].delay--;
8104 if (checkEndOfFile(file))
8108 if (i != tape->length)
8109 chunk_size = tape_pos_size * i;
8114 static void LoadTape_SokobanSolution(char *filename)
8117 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8119 if (!(file = openFile(filename, MODE_READ)))
8121 tape.no_valid_file = TRUE;
8126 while (!checkEndOfFile(file))
8128 unsigned char c = getByteFromFile(file);
8130 if (checkEndOfFile(file))
8137 tape.pos[tape.length].action[0] = MV_UP;
8138 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8144 tape.pos[tape.length].action[0] = MV_DOWN;
8145 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8151 tape.pos[tape.length].action[0] = MV_LEFT;
8152 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8158 tape.pos[tape.length].action[0] = MV_RIGHT;
8159 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8167 // ignore white-space characters
8171 tape.no_valid_file = TRUE;
8173 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8181 if (tape.no_valid_file)
8184 tape.length_frames = GetTapeLengthFrames();
8185 tape.length_seconds = GetTapeLengthSeconds();
8188 void LoadTapeFromFilename(char *filename)
8190 char cookie[MAX_LINE_LEN];
8191 char chunk_name[CHUNK_ID_LEN + 1];
8195 // always start with reliable default values
8196 setTapeInfoToDefaults();
8198 if (strSuffix(filename, ".sln"))
8200 LoadTape_SokobanSolution(filename);
8205 if (!(file = openFile(filename, MODE_READ)))
8207 tape.no_valid_file = TRUE;
8212 getFileChunkBE(file, chunk_name, NULL);
8213 if (strEqual(chunk_name, "RND1"))
8215 getFile32BitBE(file); // not used
8217 getFileChunkBE(file, chunk_name, NULL);
8218 if (!strEqual(chunk_name, "TAPE"))
8220 tape.no_valid_file = TRUE;
8222 Warn("unknown format of tape file '%s'", filename);
8229 else // check for pre-2.0 file format with cookie string
8231 strcpy(cookie, chunk_name);
8232 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8234 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8235 cookie[strlen(cookie) - 1] = '\0';
8237 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8239 tape.no_valid_file = TRUE;
8241 Warn("unknown format of tape file '%s'", filename);
8248 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8250 tape.no_valid_file = TRUE;
8252 Warn("unsupported version of tape file '%s'", filename);
8259 // pre-2.0 tape files have no game version, so use file version here
8260 tape.game_version = tape.file_version;
8263 if (tape.file_version < FILE_VERSION_1_2)
8265 // tape files from versions before 1.2.0 without chunk structure
8266 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8267 LoadTape_BODY(file, 2 * tape.length, &tape);
8275 int (*loader)(File *, int, struct TapeInfo *);
8279 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8280 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8281 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8282 { "INFO", -1, LoadTape_INFO },
8283 { "BODY", -1, LoadTape_BODY },
8287 while (getFileChunkBE(file, chunk_name, &chunk_size))
8291 while (chunk_info[i].name != NULL &&
8292 !strEqual(chunk_name, chunk_info[i].name))
8295 if (chunk_info[i].name == NULL)
8297 Warn("unknown chunk '%s' in tape file '%s'",
8298 chunk_name, filename);
8300 ReadUnusedBytesFromFile(file, chunk_size);
8302 else if (chunk_info[i].size != -1 &&
8303 chunk_info[i].size != chunk_size)
8305 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8306 chunk_size, chunk_name, filename);
8308 ReadUnusedBytesFromFile(file, chunk_size);
8312 // call function to load this tape chunk
8313 int chunk_size_expected =
8314 (chunk_info[i].loader)(file, chunk_size, &tape);
8316 // the size of some chunks cannot be checked before reading other
8317 // chunks first (like "HEAD" and "BODY") that contain some header
8318 // information, so check them here
8319 if (chunk_size_expected != chunk_size)
8321 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8322 chunk_size, chunk_name, filename);
8330 tape.length_frames = GetTapeLengthFrames();
8331 tape.length_seconds = GetTapeLengthSeconds();
8334 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8336 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8338 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8339 tape.engine_version);
8343 void LoadTape(int nr)
8345 char *filename = getTapeFilename(nr);
8347 LoadTapeFromFilename(filename);
8350 void LoadSolutionTape(int nr)
8352 char *filename = getSolutionTapeFilename(nr);
8354 LoadTapeFromFilename(filename);
8356 if (TAPE_IS_EMPTY(tape) &&
8357 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8358 level.native_sp_level->demo.is_available)
8359 CopyNativeTape_SP_to_RND(&level);
8362 void LoadScoreTape(char *score_tape_basename, int nr)
8364 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8366 LoadTapeFromFilename(filename);
8369 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8371 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8373 LoadTapeFromFilename(filename);
8376 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8378 // chunk required for team mode tapes with non-default screen size
8379 return (tape->num_participating_players > 1 &&
8380 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8381 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8384 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8386 putFileVersion(file, tape->file_version);
8387 putFileVersion(file, tape->game_version);
8390 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8393 byte store_participating_players = 0;
8395 // set bits for participating players for compact storage
8396 for (i = 0; i < MAX_PLAYERS; i++)
8397 if (tape->player_participates[i])
8398 store_participating_players |= (1 << i);
8400 putFile32BitBE(file, tape->random_seed);
8401 putFile32BitBE(file, tape->date);
8402 putFile32BitBE(file, tape->length);
8404 putFile8Bit(file, store_participating_players);
8406 putFile8Bit(file, getTapeActionValue(tape));
8408 putFile8Bit(file, tape->property_bits);
8410 // unused bytes not at the end here for 4-byte alignment of engine_version
8411 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8413 putFileVersion(file, tape->engine_version);
8416 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8418 putFile8Bit(file, tape->scr_fieldx);
8419 putFile8Bit(file, tape->scr_fieldy);
8422 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8424 int level_identifier_size = strlen(tape->level_identifier) + 1;
8427 putFile16BitBE(file, level_identifier_size);
8429 for (i = 0; i < level_identifier_size; i++)
8430 putFile8Bit(file, tape->level_identifier[i]);
8432 putFile16BitBE(file, tape->level_nr);
8435 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8439 for (i = 0; i < tape->length; i++)
8441 if (tape->use_key_actions)
8443 for (j = 0; j < MAX_PLAYERS; j++)
8444 if (tape->player_participates[j])
8445 putFile8Bit(file, tape->pos[i].action[j]);
8448 if (tape->use_mouse_actions)
8450 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8451 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8452 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8455 putFile8Bit(file, tape->pos[i].delay);
8459 void SaveTapeToFilename(char *filename)
8463 int info_chunk_size;
8464 int body_chunk_size;
8466 if (!(file = fopen(filename, MODE_WRITE)))
8468 Warn("cannot save level recording file '%s'", filename);
8473 tape_pos_size = getTapePosSize(&tape);
8475 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8476 body_chunk_size = tape_pos_size * tape.length;
8478 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8479 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8481 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8482 SaveTape_VERS(file, &tape);
8484 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8485 SaveTape_HEAD(file, &tape);
8487 if (checkSaveTape_SCRN(&tape))
8489 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8490 SaveTape_SCRN(file, &tape);
8493 putFileChunkBE(file, "INFO", info_chunk_size);
8494 SaveTape_INFO(file, &tape);
8496 putFileChunkBE(file, "BODY", body_chunk_size);
8497 SaveTape_BODY(file, &tape);
8501 SetFilePermissions(filename, PERMS_PRIVATE);
8504 static void SaveTapeExt(char *filename)
8508 tape.file_version = FILE_VERSION_ACTUAL;
8509 tape.game_version = GAME_VERSION_ACTUAL;
8511 tape.num_participating_players = 0;
8513 // count number of participating players
8514 for (i = 0; i < MAX_PLAYERS; i++)
8515 if (tape.player_participates[i])
8516 tape.num_participating_players++;
8518 SaveTapeToFilename(filename);
8520 tape.changed = FALSE;
8523 void SaveTape(int nr)
8525 char *filename = getTapeFilename(nr);
8527 InitTapeDirectory(leveldir_current->subdir);
8529 SaveTapeExt(filename);
8532 void SaveScoreTape(int nr)
8534 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8536 // used instead of "leveldir_current->subdir" (for network games)
8537 InitScoreTapeDirectory(levelset.identifier, nr);
8539 SaveTapeExt(filename);
8542 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8543 unsigned int req_state_added)
8545 char *filename = getTapeFilename(nr);
8546 boolean new_tape = !fileExists(filename);
8547 boolean tape_saved = FALSE;
8549 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8554 Request(msg_saved, REQ_CONFIRM | req_state_added);
8562 boolean SaveTapeChecked(int nr)
8564 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8567 boolean SaveTapeChecked_LevelSolved(int nr)
8569 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8570 "Level solved! Tape saved!", REQ_STAY_OPEN);
8573 void DumpTape(struct TapeInfo *tape)
8575 int tape_frame_counter;
8578 if (tape->no_valid_file)
8580 Warn("cannot dump -- no valid tape file found");
8587 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8588 tape->level_nr, tape->file_version, tape->game_version);
8589 Print(" (effective engine version %08d)\n",
8590 tape->engine_version);
8591 Print("Level series identifier: '%s'\n", tape->level_identifier);
8593 Print("Special tape properties: ");
8594 if (tape->property_bits == TAPE_PROPERTY_NONE)
8596 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8597 Print("[em_random_bug]");
8598 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8599 Print("[game_speed]");
8600 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8602 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8603 Print("[single_step]");
8604 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8605 Print("[snapshot]");
8606 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8607 Print("[replayed]");
8608 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8609 Print("[tas_keys]");
8610 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8611 Print("[small_graphics]");
8614 int year2 = tape->date / 10000;
8615 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8616 int month_index_raw = (tape->date / 100) % 100;
8617 int month_index = month_index_raw % 12; // prevent invalid index
8618 int month = month_index + 1;
8619 int day = tape->date % 100;
8621 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8625 tape_frame_counter = 0;
8627 for (i = 0; i < tape->length; i++)
8629 if (i >= MAX_TAPE_LEN)
8634 for (j = 0; j < MAX_PLAYERS; j++)
8636 if (tape->player_participates[j])
8638 int action = tape->pos[i].action[j];
8640 Print("%d:%02x ", j, action);
8641 Print("[%c%c%c%c|%c%c] - ",
8642 (action & JOY_LEFT ? '<' : ' '),
8643 (action & JOY_RIGHT ? '>' : ' '),
8644 (action & JOY_UP ? '^' : ' '),
8645 (action & JOY_DOWN ? 'v' : ' '),
8646 (action & JOY_BUTTON_1 ? '1' : ' '),
8647 (action & JOY_BUTTON_2 ? '2' : ' '));
8651 Print("(%03d) ", tape->pos[i].delay);
8652 Print("[%05d]\n", tape_frame_counter);
8654 tape_frame_counter += tape->pos[i].delay;
8660 void DumpTapes(void)
8662 static LevelDirTree *dumptape_leveldir = NULL;
8664 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8665 global.dumptape_leveldir);
8667 if (dumptape_leveldir == NULL)
8668 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8670 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8671 global.dumptape_level_nr > dumptape_leveldir->last_level)
8672 Fail("no such level number: %d", global.dumptape_level_nr);
8674 leveldir_current = dumptape_leveldir;
8676 if (options.mytapes)
8677 LoadTape(global.dumptape_level_nr);
8679 LoadSolutionTape(global.dumptape_level_nr);
8687 // ============================================================================
8688 // score file functions
8689 // ============================================================================
8691 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8695 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8697 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8698 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8699 scores->entry[i].score = 0;
8700 scores->entry[i].time = 0;
8702 scores->entry[i].id = -1;
8703 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8704 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8705 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8706 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8707 strcpy(scores->entry[i].country_code, "??");
8710 scores->num_entries = 0;
8711 scores->last_added = -1;
8712 scores->last_added_local = -1;
8714 scores->updated = FALSE;
8715 scores->uploaded = FALSE;
8716 scores->tape_downloaded = FALSE;
8717 scores->force_last_added = FALSE;
8719 // The following values are intentionally not reset here:
8723 // - continue_playing
8724 // - continue_on_return
8727 static void setScoreInfoToDefaults(void)
8729 setScoreInfoToDefaultsExt(&scores);
8732 static void setServerScoreInfoToDefaults(void)
8734 setScoreInfoToDefaultsExt(&server_scores);
8737 static void LoadScore_OLD(int nr)
8740 char *filename = getScoreFilename(nr);
8741 char cookie[MAX_LINE_LEN];
8742 char line[MAX_LINE_LEN];
8746 if (!(file = fopen(filename, MODE_READ)))
8749 // check file identifier
8750 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8752 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8753 cookie[strlen(cookie) - 1] = '\0';
8755 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8757 Warn("unknown format of score file '%s'", filename);
8764 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8766 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8767 Warn("fscanf() failed; %s", strerror(errno));
8769 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8772 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8773 line[strlen(line) - 1] = '\0';
8775 for (line_ptr = line; *line_ptr; line_ptr++)
8777 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8779 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8780 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8789 static void ConvertScore_OLD(void)
8791 // only convert score to time for levels that rate playing time over score
8792 if (!level.rate_time_over_score)
8795 // convert old score to playing time for score-less levels (like Supaplex)
8796 int time_final_max = 999;
8799 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8801 int score = scores.entry[i].score;
8803 if (score > 0 && score < time_final_max)
8804 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8808 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8810 scores->file_version = getFileVersion(file);
8811 scores->game_version = getFileVersion(file);
8816 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8818 char *level_identifier = NULL;
8819 int level_identifier_size;
8822 level_identifier_size = getFile16BitBE(file);
8824 level_identifier = checked_malloc(level_identifier_size);
8826 for (i = 0; i < level_identifier_size; i++)
8827 level_identifier[i] = getFile8Bit(file);
8829 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8830 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8832 checked_free(level_identifier);
8834 scores->level_nr = getFile16BitBE(file);
8835 scores->num_entries = getFile16BitBE(file);
8837 chunk_size = 2 + level_identifier_size + 2 + 2;
8842 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8846 for (i = 0; i < scores->num_entries; i++)
8848 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8849 scores->entry[i].name[j] = getFile8Bit(file);
8851 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8854 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8859 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8863 for (i = 0; i < scores->num_entries; i++)
8864 scores->entry[i].score = getFile16BitBE(file);
8866 chunk_size = scores->num_entries * 2;
8871 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8875 for (i = 0; i < scores->num_entries; i++)
8876 scores->entry[i].score = getFile32BitBE(file);
8878 chunk_size = scores->num_entries * 4;
8883 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8887 for (i = 0; i < scores->num_entries; i++)
8888 scores->entry[i].time = getFile32BitBE(file);
8890 chunk_size = scores->num_entries * 4;
8895 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8899 for (i = 0; i < scores->num_entries; i++)
8901 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8902 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8904 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8907 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8912 void LoadScore(int nr)
8914 char *filename = getScoreFilename(nr);
8915 char cookie[MAX_LINE_LEN];
8916 char chunk_name[CHUNK_ID_LEN + 1];
8918 boolean old_score_file_format = FALSE;
8921 // always start with reliable default values
8922 setScoreInfoToDefaults();
8924 if (!(file = openFile(filename, MODE_READ)))
8927 getFileChunkBE(file, chunk_name, NULL);
8928 if (strEqual(chunk_name, "RND1"))
8930 getFile32BitBE(file); // not used
8932 getFileChunkBE(file, chunk_name, NULL);
8933 if (!strEqual(chunk_name, "SCOR"))
8935 Warn("unknown format of score file '%s'", filename);
8942 else // check for old file format with cookie string
8944 strcpy(cookie, chunk_name);
8945 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8947 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8948 cookie[strlen(cookie) - 1] = '\0';
8950 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8952 Warn("unknown format of score file '%s'", filename);
8959 old_score_file_format = TRUE;
8962 if (old_score_file_format)
8964 // score files from versions before 4.2.4.0 without chunk structure
8967 // convert score to time, if possible (mainly for Supaplex levels)
8976 int (*loader)(File *, int, struct ScoreInfo *);
8980 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8981 { "INFO", -1, LoadScore_INFO },
8982 { "NAME", -1, LoadScore_NAME },
8983 { "SCOR", -1, LoadScore_SCOR },
8984 { "SC4R", -1, LoadScore_SC4R },
8985 { "TIME", -1, LoadScore_TIME },
8986 { "TAPE", -1, LoadScore_TAPE },
8991 while (getFileChunkBE(file, chunk_name, &chunk_size))
8995 while (chunk_info[i].name != NULL &&
8996 !strEqual(chunk_name, chunk_info[i].name))
8999 if (chunk_info[i].name == NULL)
9001 Warn("unknown chunk '%s' in score file '%s'",
9002 chunk_name, filename);
9004 ReadUnusedBytesFromFile(file, chunk_size);
9006 else if (chunk_info[i].size != -1 &&
9007 chunk_info[i].size != chunk_size)
9009 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9010 chunk_size, chunk_name, filename);
9012 ReadUnusedBytesFromFile(file, chunk_size);
9016 // call function to load this score chunk
9017 int chunk_size_expected =
9018 (chunk_info[i].loader)(file, chunk_size, &scores);
9020 // the size of some chunks cannot be checked before reading other
9021 // chunks first (like "HEAD" and "BODY") that contain some header
9022 // information, so check them here
9023 if (chunk_size_expected != chunk_size)
9025 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9026 chunk_size, chunk_name, filename);
9035 #if ENABLE_HISTORIC_CHUNKS
9036 void SaveScore_OLD(int nr)
9039 char *filename = getScoreFilename(nr);
9042 // used instead of "leveldir_current->subdir" (for network games)
9043 InitScoreDirectory(levelset.identifier);
9045 if (!(file = fopen(filename, MODE_WRITE)))
9047 Warn("cannot save score for level %d", nr);
9052 fprintf(file, "%s\n\n", SCORE_COOKIE);
9054 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9055 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9059 SetFilePermissions(filename, PERMS_PRIVATE);
9063 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9065 putFileVersion(file, scores->file_version);
9066 putFileVersion(file, scores->game_version);
9069 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9071 int level_identifier_size = strlen(scores->level_identifier) + 1;
9074 putFile16BitBE(file, level_identifier_size);
9076 for (i = 0; i < level_identifier_size; i++)
9077 putFile8Bit(file, scores->level_identifier[i]);
9079 putFile16BitBE(file, scores->level_nr);
9080 putFile16BitBE(file, scores->num_entries);
9083 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9087 for (i = 0; i < scores->num_entries; i++)
9089 int name_size = strlen(scores->entry[i].name);
9091 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9092 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9096 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9100 for (i = 0; i < scores->num_entries; i++)
9101 putFile16BitBE(file, scores->entry[i].score);
9104 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9108 for (i = 0; i < scores->num_entries; i++)
9109 putFile32BitBE(file, scores->entry[i].score);
9112 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9116 for (i = 0; i < scores->num_entries; i++)
9117 putFile32BitBE(file, scores->entry[i].time);
9120 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9124 for (i = 0; i < scores->num_entries; i++)
9126 int size = strlen(scores->entry[i].tape_basename);
9128 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9129 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9133 static void SaveScoreToFilename(char *filename)
9136 int info_chunk_size;
9137 int name_chunk_size;
9138 int scor_chunk_size;
9139 int sc4r_chunk_size;
9140 int time_chunk_size;
9141 int tape_chunk_size;
9142 boolean has_large_score_values;
9145 if (!(file = fopen(filename, MODE_WRITE)))
9147 Warn("cannot save score file '%s'", filename);
9152 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9153 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9154 scor_chunk_size = scores.num_entries * 2;
9155 sc4r_chunk_size = scores.num_entries * 4;
9156 time_chunk_size = scores.num_entries * 4;
9157 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9159 has_large_score_values = FALSE;
9160 for (i = 0; i < scores.num_entries; i++)
9161 if (scores.entry[i].score > 0xffff)
9162 has_large_score_values = TRUE;
9164 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9165 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9167 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9168 SaveScore_VERS(file, &scores);
9170 putFileChunkBE(file, "INFO", info_chunk_size);
9171 SaveScore_INFO(file, &scores);
9173 putFileChunkBE(file, "NAME", name_chunk_size);
9174 SaveScore_NAME(file, &scores);
9176 if (has_large_score_values)
9178 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9179 SaveScore_SC4R(file, &scores);
9183 putFileChunkBE(file, "SCOR", scor_chunk_size);
9184 SaveScore_SCOR(file, &scores);
9187 putFileChunkBE(file, "TIME", time_chunk_size);
9188 SaveScore_TIME(file, &scores);
9190 putFileChunkBE(file, "TAPE", tape_chunk_size);
9191 SaveScore_TAPE(file, &scores);
9195 SetFilePermissions(filename, PERMS_PRIVATE);
9198 void SaveScore(int nr)
9200 char *filename = getScoreFilename(nr);
9203 // used instead of "leveldir_current->subdir" (for network games)
9204 InitScoreDirectory(levelset.identifier);
9206 scores.file_version = FILE_VERSION_ACTUAL;
9207 scores.game_version = GAME_VERSION_ACTUAL;
9209 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9210 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9211 scores.level_nr = level_nr;
9213 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9214 if (scores.entry[i].score == 0 &&
9215 scores.entry[i].time == 0 &&
9216 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9219 scores.num_entries = i;
9221 if (scores.num_entries == 0)
9224 SaveScoreToFilename(filename);
9227 static void LoadServerScoreFromCache(int nr)
9229 struct ScoreEntry score_entry;
9238 { &score_entry.score, FALSE, 0 },
9239 { &score_entry.time, FALSE, 0 },
9240 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9241 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9242 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9243 { &score_entry.id, FALSE, 0 },
9244 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9245 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9246 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9247 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9251 char *filename = getScoreCacheFilename(nr);
9252 SetupFileHash *score_hash = loadSetupFileHash(filename);
9255 server_scores.num_entries = 0;
9257 if (score_hash == NULL)
9260 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9262 score_entry = server_scores.entry[i];
9264 for (j = 0; score_mapping[j].value != NULL; j++)
9268 sprintf(token, "%02d.%d", i, j);
9270 char *value = getHashEntry(score_hash, token);
9275 if (score_mapping[j].is_string)
9277 char *score_value = (char *)score_mapping[j].value;
9278 int value_size = score_mapping[j].string_size;
9280 strncpy(score_value, value, value_size);
9281 score_value[value_size] = '\0';
9285 int *score_value = (int *)score_mapping[j].value;
9287 *score_value = atoi(value);
9290 server_scores.num_entries = i + 1;
9293 server_scores.entry[i] = score_entry;
9296 freeSetupFileHash(score_hash);
9299 void LoadServerScore(int nr, boolean download_score)
9301 if (!setup.use_api_server)
9304 // always start with reliable default values
9305 setServerScoreInfoToDefaults();
9307 // 1st step: load server scores from cache file (which may not exist)
9308 // (this should prevent reading it while the thread is writing to it)
9309 LoadServerScoreFromCache(nr);
9311 if (download_score && runtime.use_api_server)
9313 // 2nd step: download server scores from score server to cache file
9314 // (as thread, as it might time out if the server is not reachable)
9315 ApiGetScoreAsThread(nr);
9319 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9321 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9323 // if score tape not uploaded, ask for uploading missing tapes later
9324 if (!setup.has_remaining_tapes)
9325 setup.ask_for_remaining_tapes = TRUE;
9327 setup.provide_uploading_tapes = TRUE;
9328 setup.has_remaining_tapes = TRUE;
9330 SaveSetup_ServerSetup();
9333 void SaveServerScore(int nr, boolean tape_saved)
9335 if (!runtime.use_api_server)
9337 PrepareScoreTapesForUpload(leveldir_current->subdir);
9342 ApiAddScoreAsThread(nr, tape_saved, NULL);
9345 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9346 char *score_tape_filename)
9348 if (!runtime.use_api_server)
9351 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9354 void LoadLocalAndServerScore(int nr, boolean download_score)
9356 int last_added_local = scores.last_added_local;
9357 boolean force_last_added = scores.force_last_added;
9359 // needed if only showing server scores
9360 setScoreInfoToDefaults();
9362 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9365 // restore last added local score entry (before merging server scores)
9366 scores.last_added = scores.last_added_local = last_added_local;
9368 if (setup.use_api_server &&
9369 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9371 // load server scores from cache file and trigger update from server
9372 LoadServerScore(nr, download_score);
9374 // merge local scores with scores from server
9378 if (force_last_added)
9379 scores.force_last_added = force_last_added;
9383 // ============================================================================
9384 // setup file functions
9385 // ============================================================================
9387 #define TOKEN_STR_PLAYER_PREFIX "player_"
9390 static struct TokenInfo global_setup_tokens[] =
9394 &setup.player_name, "player_name"
9398 &setup.multiple_users, "multiple_users"
9402 &setup.sound, "sound"
9406 &setup.sound_loops, "repeating_sound_loops"
9410 &setup.sound_music, "background_music"
9414 &setup.sound_simple, "simple_sound_effects"
9418 &setup.toons, "toons"
9422 &setup.scroll_delay, "scroll_delay"
9426 &setup.forced_scroll_delay, "forced_scroll_delay"
9430 &setup.scroll_delay_value, "scroll_delay_value"
9434 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9438 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9442 &setup.fade_screens, "fade_screens"
9446 &setup.autorecord, "automatic_tape_recording"
9450 &setup.auto_pause_on_start, "auto_pause_on_start"
9454 &setup.show_titlescreen, "show_titlescreen"
9458 &setup.quick_doors, "quick_doors"
9462 &setup.team_mode, "team_mode"
9466 &setup.handicap, "handicap"
9470 &setup.skip_levels, "skip_levels"
9474 &setup.increment_levels, "increment_levels"
9478 &setup.auto_play_next_level, "auto_play_next_level"
9482 &setup.count_score_after_game, "count_score_after_game"
9486 &setup.show_scores_after_game, "show_scores_after_game"
9490 &setup.time_limit, "time_limit"
9494 &setup.fullscreen, "fullscreen"
9498 &setup.window_scaling_percent, "window_scaling_percent"
9502 &setup.window_scaling_quality, "window_scaling_quality"
9506 &setup.screen_rendering_mode, "screen_rendering_mode"
9510 &setup.vsync_mode, "vsync_mode"
9514 &setup.ask_on_escape, "ask_on_escape"
9518 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9522 &setup.ask_on_game_over, "ask_on_game_over"
9526 &setup.ask_on_quit_game, "ask_on_quit_game"
9530 &setup.ask_on_quit_program, "ask_on_quit_program"
9534 &setup.quick_switch, "quick_player_switch"
9538 &setup.input_on_focus, "input_on_focus"
9542 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9546 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9550 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9554 &setup.game_speed_extended, "game_speed_extended"
9558 &setup.game_frame_delay, "game_frame_delay"
9562 &setup.sp_show_border_elements, "sp_show_border_elements"
9566 &setup.small_game_graphics, "small_game_graphics"
9570 &setup.show_load_save_buttons, "show_load_save_buttons"
9574 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9578 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9582 &setup.graphics_set, "graphics_set"
9586 &setup.sounds_set, "sounds_set"
9590 &setup.music_set, "music_set"
9594 &setup.override_level_graphics, "override_level_graphics"
9598 &setup.override_level_sounds, "override_level_sounds"
9602 &setup.override_level_music, "override_level_music"
9606 &setup.volume_simple, "volume_simple"
9610 &setup.volume_loops, "volume_loops"
9614 &setup.volume_music, "volume_music"
9618 &setup.network_mode, "network_mode"
9622 &setup.network_player_nr, "network_player"
9626 &setup.network_server_hostname, "network_server_hostname"
9630 &setup.touch.control_type, "touch.control_type"
9634 &setup.touch.move_distance, "touch.move_distance"
9638 &setup.touch.drop_distance, "touch.drop_distance"
9642 &setup.touch.transparency, "touch.transparency"
9646 &setup.touch.draw_outlined, "touch.draw_outlined"
9650 &setup.touch.draw_pressed, "touch.draw_pressed"
9654 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9658 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9662 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9666 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9670 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9674 static struct TokenInfo auto_setup_tokens[] =
9678 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9682 static struct TokenInfo server_setup_tokens[] =
9686 &setup.player_uuid, "player_uuid"
9690 &setup.player_version, "player_version"
9694 &setup.use_api_server, TEST_PREFIX "use_api_server"
9698 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9702 &setup.api_server_password, TEST_PREFIX "api_server_password"
9706 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9710 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9714 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9718 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9722 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9726 static struct TokenInfo editor_setup_tokens[] =
9730 &setup.editor.el_classic, "editor.el_classic"
9734 &setup.editor.el_custom, "editor.el_custom"
9738 &setup.editor.el_user_defined, "editor.el_user_defined"
9742 &setup.editor.el_dynamic, "editor.el_dynamic"
9746 &setup.editor.el_headlines, "editor.el_headlines"
9750 &setup.editor.show_element_token, "editor.show_element_token"
9754 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9758 static struct TokenInfo editor_cascade_setup_tokens[] =
9762 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9766 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9770 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9774 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9778 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9782 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9786 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9790 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9794 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9798 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9802 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9806 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9810 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9814 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9818 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9822 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9826 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9830 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9834 static struct TokenInfo shortcut_setup_tokens[] =
9838 &setup.shortcut.save_game, "shortcut.save_game"
9842 &setup.shortcut.load_game, "shortcut.load_game"
9846 &setup.shortcut.restart_game, "shortcut.restart_game"
9850 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9854 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9858 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9862 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9866 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9870 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9874 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9878 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9882 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9886 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9890 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9894 &setup.shortcut.tape_record, "shortcut.tape_record"
9898 &setup.shortcut.tape_play, "shortcut.tape_play"
9902 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9906 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9910 &setup.shortcut.sound_music, "shortcut.sound_music"
9914 &setup.shortcut.snap_left, "shortcut.snap_left"
9918 &setup.shortcut.snap_right, "shortcut.snap_right"
9922 &setup.shortcut.snap_up, "shortcut.snap_up"
9926 &setup.shortcut.snap_down, "shortcut.snap_down"
9930 static struct SetupInputInfo setup_input;
9931 static struct TokenInfo player_setup_tokens[] =
9935 &setup_input.use_joystick, ".use_joystick"
9939 &setup_input.joy.device_name, ".joy.device_name"
9943 &setup_input.joy.xleft, ".joy.xleft"
9947 &setup_input.joy.xmiddle, ".joy.xmiddle"
9951 &setup_input.joy.xright, ".joy.xright"
9955 &setup_input.joy.yupper, ".joy.yupper"
9959 &setup_input.joy.ymiddle, ".joy.ymiddle"
9963 &setup_input.joy.ylower, ".joy.ylower"
9967 &setup_input.joy.snap, ".joy.snap_field"
9971 &setup_input.joy.drop, ".joy.place_bomb"
9975 &setup_input.key.left, ".key.move_left"
9979 &setup_input.key.right, ".key.move_right"
9983 &setup_input.key.up, ".key.move_up"
9987 &setup_input.key.down, ".key.move_down"
9991 &setup_input.key.snap, ".key.snap_field"
9995 &setup_input.key.drop, ".key.place_bomb"
9999 static struct TokenInfo system_setup_tokens[] =
10003 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10007 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10011 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10015 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10019 static struct TokenInfo internal_setup_tokens[] =
10023 &setup.internal.program_title, "program_title"
10027 &setup.internal.program_version, "program_version"
10031 &setup.internal.program_author, "program_author"
10035 &setup.internal.program_email, "program_email"
10039 &setup.internal.program_website, "program_website"
10043 &setup.internal.program_copyright, "program_copyright"
10047 &setup.internal.program_company, "program_company"
10051 &setup.internal.program_icon_file, "program_icon_file"
10055 &setup.internal.default_graphics_set, "default_graphics_set"
10059 &setup.internal.default_sounds_set, "default_sounds_set"
10063 &setup.internal.default_music_set, "default_music_set"
10067 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10071 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10075 &setup.internal.fallback_music_file, "fallback_music_file"
10079 &setup.internal.default_level_series, "default_level_series"
10083 &setup.internal.default_window_width, "default_window_width"
10087 &setup.internal.default_window_height, "default_window_height"
10091 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10095 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10099 &setup.internal.create_user_levelset, "create_user_levelset"
10103 &setup.internal.menu_game, "menu_game"
10107 &setup.internal.menu_editor, "menu_editor"
10111 &setup.internal.menu_graphics, "menu_graphics"
10115 &setup.internal.menu_sound, "menu_sound"
10119 &setup.internal.menu_artwork, "menu_artwork"
10123 &setup.internal.menu_input, "menu_input"
10127 &setup.internal.menu_touch, "menu_touch"
10131 &setup.internal.menu_shortcuts, "menu_shortcuts"
10135 &setup.internal.menu_exit, "menu_exit"
10139 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10143 &setup.internal.info_title, "info_title"
10147 &setup.internal.info_elements, "info_elements"
10151 &setup.internal.info_music, "info_music"
10155 &setup.internal.info_credits, "info_credits"
10159 &setup.internal.info_program, "info_program"
10163 &setup.internal.info_version, "info_version"
10167 &setup.internal.info_levelset, "info_levelset"
10171 &setup.internal.info_exit, "info_exit"
10175 static struct TokenInfo debug_setup_tokens[] =
10179 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10183 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10187 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10191 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10195 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10199 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10203 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10207 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10211 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10215 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10219 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10223 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10227 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10231 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10235 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10239 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10243 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10247 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10251 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10255 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10259 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10262 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10266 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10270 &setup.debug.xsn_mode, "debug.xsn_mode"
10274 &setup.debug.xsn_percent, "debug.xsn_percent"
10278 static struct TokenInfo options_setup_tokens[] =
10282 &setup.options.verbose, "options.verbose"
10286 static void setSetupInfoToDefaults(struct SetupInfo *si)
10290 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10292 si->multiple_users = TRUE;
10295 si->sound_loops = TRUE;
10296 si->sound_music = TRUE;
10297 si->sound_simple = TRUE;
10299 si->scroll_delay = TRUE;
10300 si->forced_scroll_delay = FALSE;
10301 si->scroll_delay_value = STD_SCROLL_DELAY;
10302 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10303 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10304 si->fade_screens = TRUE;
10305 si->autorecord = TRUE;
10306 si->auto_pause_on_start = FALSE;
10307 si->show_titlescreen = TRUE;
10308 si->quick_doors = FALSE;
10309 si->team_mode = FALSE;
10310 si->handicap = TRUE;
10311 si->skip_levels = TRUE;
10312 si->increment_levels = TRUE;
10313 si->auto_play_next_level = TRUE;
10314 si->count_score_after_game = TRUE;
10315 si->show_scores_after_game = TRUE;
10316 si->time_limit = TRUE;
10317 si->fullscreen = FALSE;
10318 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10319 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10320 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10321 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10322 si->ask_on_escape = TRUE;
10323 si->ask_on_escape_editor = TRUE;
10324 si->ask_on_game_over = TRUE;
10325 si->ask_on_quit_game = TRUE;
10326 si->ask_on_quit_program = TRUE;
10327 si->quick_switch = FALSE;
10328 si->input_on_focus = FALSE;
10329 si->prefer_aga_graphics = TRUE;
10330 si->prefer_lowpass_sounds = FALSE;
10331 si->prefer_extra_panel_items = TRUE;
10332 si->game_speed_extended = FALSE;
10333 si->game_frame_delay = GAME_FRAME_DELAY;
10334 si->sp_show_border_elements = FALSE;
10335 si->small_game_graphics = FALSE;
10336 si->show_load_save_buttons = FALSE;
10337 si->show_undo_redo_buttons = FALSE;
10338 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10340 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10341 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10342 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10344 si->override_level_graphics = FALSE;
10345 si->override_level_sounds = FALSE;
10346 si->override_level_music = FALSE;
10348 si->volume_simple = 100; // percent
10349 si->volume_loops = 100; // percent
10350 si->volume_music = 100; // percent
10352 si->network_mode = FALSE;
10353 si->network_player_nr = 0; // first player
10354 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10356 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10357 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10358 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10359 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10360 si->touch.draw_outlined = TRUE;
10361 si->touch.draw_pressed = TRUE;
10363 for (i = 0; i < 2; i++)
10365 char *default_grid_button[6][2] =
10371 { "111222", " vv " },
10372 { "111222", " vv " }
10374 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10375 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10376 int min_xsize = MIN(6, grid_xsize);
10377 int min_ysize = MIN(6, grid_ysize);
10378 int startx = grid_xsize - min_xsize;
10379 int starty = grid_ysize - min_ysize;
10382 // virtual buttons grid can only be set to defaults if video is initialized
10383 // (this will be repeated if virtual buttons are not loaded from setup file)
10384 if (video.initialized)
10386 si->touch.grid_xsize[i] = grid_xsize;
10387 si->touch.grid_ysize[i] = grid_ysize;
10391 si->touch.grid_xsize[i] = -1;
10392 si->touch.grid_ysize[i] = -1;
10395 for (x = 0; x < MAX_GRID_XSIZE; x++)
10396 for (y = 0; y < MAX_GRID_YSIZE; y++)
10397 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10399 for (x = 0; x < min_xsize; x++)
10400 for (y = 0; y < min_ysize; y++)
10401 si->touch.grid_button[i][x][starty + y] =
10402 default_grid_button[y][0][x];
10404 for (x = 0; x < min_xsize; x++)
10405 for (y = 0; y < min_ysize; y++)
10406 si->touch.grid_button[i][startx + x][starty + y] =
10407 default_grid_button[y][1][x];
10410 si->touch.grid_initialized = video.initialized;
10412 si->touch.overlay_buttons = FALSE;
10414 si->editor.el_boulderdash = TRUE;
10415 si->editor.el_emerald_mine = TRUE;
10416 si->editor.el_emerald_mine_club = TRUE;
10417 si->editor.el_more = TRUE;
10418 si->editor.el_sokoban = TRUE;
10419 si->editor.el_supaplex = TRUE;
10420 si->editor.el_diamond_caves = TRUE;
10421 si->editor.el_dx_boulderdash = TRUE;
10423 si->editor.el_mirror_magic = TRUE;
10424 si->editor.el_deflektor = TRUE;
10426 si->editor.el_chars = TRUE;
10427 si->editor.el_steel_chars = TRUE;
10429 si->editor.el_classic = TRUE;
10430 si->editor.el_custom = TRUE;
10432 si->editor.el_user_defined = FALSE;
10433 si->editor.el_dynamic = TRUE;
10435 si->editor.el_headlines = TRUE;
10437 si->editor.show_element_token = FALSE;
10439 si->editor.show_read_only_warning = TRUE;
10441 si->editor.use_template_for_new_levels = TRUE;
10443 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10444 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10445 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10446 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10447 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10449 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10450 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10451 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10452 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10453 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10455 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10456 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10457 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10458 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10459 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10460 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10462 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10463 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10464 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10466 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10467 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10468 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10469 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10471 for (i = 0; i < MAX_PLAYERS; i++)
10473 si->input[i].use_joystick = FALSE;
10474 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10475 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10476 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10477 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10478 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10479 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10480 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10481 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10482 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10483 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10484 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10485 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10486 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10487 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10488 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10491 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10492 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10493 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10494 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10496 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10497 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10498 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10499 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10500 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10501 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10502 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10504 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10506 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10507 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10508 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10510 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10511 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10512 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10514 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10515 si->internal.choose_from_top_leveldir = FALSE;
10516 si->internal.show_scaling_in_title = TRUE;
10517 si->internal.create_user_levelset = TRUE;
10519 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10520 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10522 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10523 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10524 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10525 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10526 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10527 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10528 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10529 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10530 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10531 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10533 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10534 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10535 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10536 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10537 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10538 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10539 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10540 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10541 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10542 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10544 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10545 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10547 si->debug.show_frames_per_second = FALSE;
10549 si->debug.xsn_mode = AUTO;
10550 si->debug.xsn_percent = 0;
10552 si->options.verbose = FALSE;
10554 #if defined(PLATFORM_ANDROID)
10555 si->fullscreen = TRUE;
10556 si->touch.overlay_buttons = TRUE;
10559 setHideSetupEntry(&setup.debug.xsn_mode);
10562 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10564 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10567 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10569 si->player_uuid = NULL; // (will be set later)
10570 si->player_version = 1; // (will be set later)
10572 si->use_api_server = TRUE;
10573 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10574 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10575 si->ask_for_uploading_tapes = TRUE;
10576 si->ask_for_remaining_tapes = FALSE;
10577 si->provide_uploading_tapes = TRUE;
10578 si->ask_for_using_api_server = TRUE;
10579 si->has_remaining_tapes = FALSE;
10582 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10584 si->editor_cascade.el_bd = TRUE;
10585 si->editor_cascade.el_em = TRUE;
10586 si->editor_cascade.el_emc = TRUE;
10587 si->editor_cascade.el_rnd = TRUE;
10588 si->editor_cascade.el_sb = TRUE;
10589 si->editor_cascade.el_sp = TRUE;
10590 si->editor_cascade.el_dc = TRUE;
10591 si->editor_cascade.el_dx = TRUE;
10593 si->editor_cascade.el_mm = TRUE;
10594 si->editor_cascade.el_df = TRUE;
10596 si->editor_cascade.el_chars = FALSE;
10597 si->editor_cascade.el_steel_chars = FALSE;
10598 si->editor_cascade.el_ce = FALSE;
10599 si->editor_cascade.el_ge = FALSE;
10600 si->editor_cascade.el_es = FALSE;
10601 si->editor_cascade.el_ref = FALSE;
10602 si->editor_cascade.el_user = FALSE;
10603 si->editor_cascade.el_dynamic = FALSE;
10606 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10608 static char *getHideSetupToken(void *setup_value)
10610 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10612 if (setup_value != NULL)
10613 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10615 return hide_setup_token;
10618 void setHideSetupEntry(void *setup_value)
10620 char *hide_setup_token = getHideSetupToken(setup_value);
10622 if (hide_setup_hash == NULL)
10623 hide_setup_hash = newSetupFileHash();
10625 if (setup_value != NULL)
10626 setHashEntry(hide_setup_hash, hide_setup_token, "");
10629 void removeHideSetupEntry(void *setup_value)
10631 char *hide_setup_token = getHideSetupToken(setup_value);
10633 if (setup_value != NULL)
10634 removeHashEntry(hide_setup_hash, hide_setup_token);
10637 boolean hideSetupEntry(void *setup_value)
10639 char *hide_setup_token = getHideSetupToken(setup_value);
10641 return (setup_value != NULL &&
10642 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10645 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10646 struct TokenInfo *token_info,
10647 int token_nr, char *token_text)
10649 char *token_hide_text = getStringCat2(token_text, ".hide");
10650 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10652 // set the value of this setup option in the setup option structure
10653 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10655 // check if this setup option should be hidden in the setup menu
10656 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10657 setHideSetupEntry(token_info[token_nr].value);
10659 free(token_hide_text);
10662 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10663 struct TokenInfo *token_info,
10666 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10667 token_info[token_nr].text);
10670 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10674 if (!setup_file_hash)
10677 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10678 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10680 setup.touch.grid_initialized = TRUE;
10681 for (i = 0; i < 2; i++)
10683 int grid_xsize = setup.touch.grid_xsize[i];
10684 int grid_ysize = setup.touch.grid_ysize[i];
10687 // if virtual buttons are not loaded from setup file, repeat initializing
10688 // virtual buttons grid with default values later when video is initialized
10689 if (grid_xsize == -1 ||
10692 setup.touch.grid_initialized = FALSE;
10697 for (y = 0; y < grid_ysize; y++)
10699 char token_string[MAX_LINE_LEN];
10701 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10703 char *value_string = getHashEntry(setup_file_hash, token_string);
10705 if (value_string == NULL)
10708 for (x = 0; x < grid_xsize; x++)
10710 char c = value_string[x];
10712 setup.touch.grid_button[i][x][y] =
10713 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10718 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10719 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10721 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10722 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10724 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10728 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10730 setup_input = setup.input[pnr];
10731 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10733 char full_token[100];
10735 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10736 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10739 setup.input[pnr] = setup_input;
10742 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10743 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10745 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10746 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10748 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10749 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10751 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10752 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10754 setHideRelatedSetupEntries();
10757 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10761 if (!setup_file_hash)
10764 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10765 setSetupInfo(auto_setup_tokens, i,
10766 getHashEntry(setup_file_hash,
10767 auto_setup_tokens[i].text));
10770 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10774 if (!setup_file_hash)
10777 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10778 setSetupInfo(server_setup_tokens, i,
10779 getHashEntry(setup_file_hash,
10780 server_setup_tokens[i].text));
10783 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10787 if (!setup_file_hash)
10790 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10791 setSetupInfo(editor_cascade_setup_tokens, i,
10792 getHashEntry(setup_file_hash,
10793 editor_cascade_setup_tokens[i].text));
10796 void LoadUserNames(void)
10798 int last_user_nr = user.nr;
10801 if (global.user_names != NULL)
10803 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10804 checked_free(global.user_names[i]);
10806 checked_free(global.user_names);
10809 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10811 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10815 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10817 if (setup_file_hash)
10819 char *player_name = getHashEntry(setup_file_hash, "player_name");
10821 global.user_names[i] = getFixedUserName(player_name);
10823 freeSetupFileHash(setup_file_hash);
10826 if (global.user_names[i] == NULL)
10827 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10830 user.nr = last_user_nr;
10833 void LoadSetupFromFilename(char *filename)
10835 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10837 if (setup_file_hash)
10839 decodeSetupFileHash_Default(setup_file_hash);
10841 freeSetupFileHash(setup_file_hash);
10845 Debug("setup", "using default setup values");
10849 static void LoadSetup_SpecialPostProcessing(void)
10851 char *player_name_new;
10853 // needed to work around problems with fixed length strings
10854 player_name_new = getFixedUserName(setup.player_name);
10855 free(setup.player_name);
10856 setup.player_name = player_name_new;
10858 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10859 if (setup.scroll_delay == FALSE)
10861 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10862 setup.scroll_delay = TRUE; // now always "on"
10865 // make sure that scroll delay value stays inside valid range
10866 setup.scroll_delay_value =
10867 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10870 void LoadSetup_Default(void)
10874 // always start with reliable default values
10875 setSetupInfoToDefaults(&setup);
10877 // try to load setup values from default setup file
10878 filename = getDefaultSetupFilename();
10880 if (fileExists(filename))
10881 LoadSetupFromFilename(filename);
10883 // try to load setup values from platform setup file
10884 filename = getPlatformSetupFilename();
10886 if (fileExists(filename))
10887 LoadSetupFromFilename(filename);
10889 // try to load setup values from user setup file
10890 filename = getSetupFilename();
10892 LoadSetupFromFilename(filename);
10894 LoadSetup_SpecialPostProcessing();
10897 void LoadSetup_AutoSetup(void)
10899 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10900 SetupFileHash *setup_file_hash = NULL;
10902 // always start with reliable default values
10903 setSetupInfoToDefaults_AutoSetup(&setup);
10905 setup_file_hash = loadSetupFileHash(filename);
10907 if (setup_file_hash)
10909 decodeSetupFileHash_AutoSetup(setup_file_hash);
10911 freeSetupFileHash(setup_file_hash);
10917 void LoadSetup_ServerSetup(void)
10919 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
10920 SetupFileHash *setup_file_hash = NULL;
10922 // always start with reliable default values
10923 setSetupInfoToDefaults_ServerSetup(&setup);
10925 setup_file_hash = loadSetupFileHash(filename);
10927 if (setup_file_hash)
10929 decodeSetupFileHash_ServerSetup(setup_file_hash);
10931 freeSetupFileHash(setup_file_hash);
10936 if (setup.player_uuid == NULL)
10938 // player UUID does not yet exist in setup file
10939 setup.player_uuid = getStringCopy(getUUID());
10940 setup.player_version = 2;
10942 SaveSetup_ServerSetup();
10946 void LoadSetup_EditorCascade(void)
10948 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10949 SetupFileHash *setup_file_hash = NULL;
10951 // always start with reliable default values
10952 setSetupInfoToDefaults_EditorCascade(&setup);
10954 setup_file_hash = loadSetupFileHash(filename);
10956 if (setup_file_hash)
10958 decodeSetupFileHash_EditorCascade(setup_file_hash);
10960 freeSetupFileHash(setup_file_hash);
10966 void LoadSetup(void)
10968 LoadSetup_Default();
10969 LoadSetup_AutoSetup();
10970 LoadSetup_ServerSetup();
10971 LoadSetup_EditorCascade();
10974 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
10975 char *mapping_line)
10977 char mapping_guid[MAX_LINE_LEN];
10978 char *mapping_start, *mapping_end;
10980 // get GUID from game controller mapping line: copy complete line
10981 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
10982 mapping_guid[MAX_LINE_LEN - 1] = '\0';
10984 // get GUID from game controller mapping line: cut after GUID part
10985 mapping_start = strchr(mapping_guid, ',');
10986 if (mapping_start != NULL)
10987 *mapping_start = '\0';
10989 // cut newline from game controller mapping line
10990 mapping_end = strchr(mapping_line, '\n');
10991 if (mapping_end != NULL)
10992 *mapping_end = '\0';
10994 // add mapping entry to game controller mappings hash
10995 setHashEntry(mappings_hash, mapping_guid, mapping_line);
10998 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11003 if (!(file = fopen(filename, MODE_READ)))
11005 Warn("cannot read game controller mappings file '%s'", filename);
11010 while (!feof(file))
11012 char line[MAX_LINE_LEN];
11014 if (!fgets(line, MAX_LINE_LEN, file))
11017 addGameControllerMappingToHash(mappings_hash, line);
11023 void SaveSetup_Default(void)
11025 char *filename = getSetupFilename();
11029 InitUserDataDirectory();
11031 if (!(file = fopen(filename, MODE_WRITE)))
11033 Warn("cannot write setup file '%s'", filename);
11038 fprintFileHeader(file, SETUP_FILENAME);
11040 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11042 // just to make things nicer :)
11043 if (global_setup_tokens[i].value == &setup.multiple_users ||
11044 global_setup_tokens[i].value == &setup.sound ||
11045 global_setup_tokens[i].value == &setup.graphics_set ||
11046 global_setup_tokens[i].value == &setup.volume_simple ||
11047 global_setup_tokens[i].value == &setup.network_mode ||
11048 global_setup_tokens[i].value == &setup.touch.control_type ||
11049 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11050 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11051 fprintf(file, "\n");
11053 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11056 for (i = 0; i < 2; i++)
11058 int grid_xsize = setup.touch.grid_xsize[i];
11059 int grid_ysize = setup.touch.grid_ysize[i];
11062 fprintf(file, "\n");
11064 for (y = 0; y < grid_ysize; y++)
11066 char token_string[MAX_LINE_LEN];
11067 char value_string[MAX_LINE_LEN];
11069 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11071 for (x = 0; x < grid_xsize; x++)
11073 char c = setup.touch.grid_button[i][x][y];
11075 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11078 value_string[grid_xsize] = '\0';
11080 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11084 fprintf(file, "\n");
11085 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11086 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11088 fprintf(file, "\n");
11089 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11090 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11092 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11096 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11097 fprintf(file, "\n");
11099 setup_input = setup.input[pnr];
11100 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11101 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11104 fprintf(file, "\n");
11105 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11106 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11108 // (internal setup values not saved to user setup file)
11110 fprintf(file, "\n");
11111 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11112 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11113 setup.debug.xsn_mode != AUTO)
11114 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11116 fprintf(file, "\n");
11117 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11118 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11122 SetFilePermissions(filename, PERMS_PRIVATE);
11125 void SaveSetup_AutoSetup(void)
11127 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11131 InitUserDataDirectory();
11133 if (!(file = fopen(filename, MODE_WRITE)))
11135 Warn("cannot write auto setup file '%s'", filename);
11142 fprintFileHeader(file, AUTOSETUP_FILENAME);
11144 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11145 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11149 SetFilePermissions(filename, PERMS_PRIVATE);
11154 void SaveSetup_ServerSetup(void)
11156 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11160 InitUserDataDirectory();
11162 if (!(file = fopen(filename, MODE_WRITE)))
11164 Warn("cannot write server setup file '%s'", filename);
11171 fprintFileHeader(file, SERVERSETUP_FILENAME);
11173 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11175 // just to make things nicer :)
11176 if (server_setup_tokens[i].value == &setup.use_api_server)
11177 fprintf(file, "\n");
11179 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11184 SetFilePermissions(filename, PERMS_PRIVATE);
11189 void SaveSetup_EditorCascade(void)
11191 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11195 InitUserDataDirectory();
11197 if (!(file = fopen(filename, MODE_WRITE)))
11199 Warn("cannot write editor cascade state file '%s'", filename);
11206 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11208 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11209 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11213 SetFilePermissions(filename, PERMS_PRIVATE);
11218 void SaveSetup(void)
11220 SaveSetup_Default();
11221 SaveSetup_AutoSetup();
11222 SaveSetup_ServerSetup();
11223 SaveSetup_EditorCascade();
11226 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11231 if (!(file = fopen(filename, MODE_WRITE)))
11233 Warn("cannot write game controller mappings file '%s'", filename);
11238 BEGIN_HASH_ITERATION(mappings_hash, itr)
11240 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11242 END_HASH_ITERATION(mappings_hash, itr)
11247 void SaveSetup_AddGameControllerMapping(char *mapping)
11249 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11250 SetupFileHash *mappings_hash = newSetupFileHash();
11252 InitUserDataDirectory();
11254 // load existing personal game controller mappings
11255 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11257 // add new mapping to personal game controller mappings
11258 addGameControllerMappingToHash(mappings_hash, mapping);
11260 // save updated personal game controller mappings
11261 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11263 freeSetupFileHash(mappings_hash);
11267 void LoadCustomElementDescriptions(void)
11269 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11270 SetupFileHash *setup_file_hash;
11273 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11275 if (element_info[i].custom_description != NULL)
11277 free(element_info[i].custom_description);
11278 element_info[i].custom_description = NULL;
11282 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11285 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11287 char *token = getStringCat2(element_info[i].token_name, ".name");
11288 char *value = getHashEntry(setup_file_hash, token);
11291 element_info[i].custom_description = getStringCopy(value);
11296 freeSetupFileHash(setup_file_hash);
11299 static int getElementFromToken(char *token)
11301 char *value = getHashEntry(element_token_hash, token);
11304 return atoi(value);
11306 Warn("unknown element token '%s'", token);
11308 return EL_UNDEFINED;
11311 void FreeGlobalAnimEventInfo(void)
11313 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11315 if (gaei->event_list == NULL)
11320 for (i = 0; i < gaei->num_event_lists; i++)
11322 checked_free(gaei->event_list[i]->event_value);
11323 checked_free(gaei->event_list[i]);
11326 checked_free(gaei->event_list);
11328 gaei->event_list = NULL;
11329 gaei->num_event_lists = 0;
11332 static int AddGlobalAnimEventList(void)
11334 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11335 int list_pos = gaei->num_event_lists++;
11337 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11338 sizeof(struct GlobalAnimEventListInfo *));
11340 gaei->event_list[list_pos] =
11341 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11343 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11345 gaeli->event_value = NULL;
11346 gaeli->num_event_values = 0;
11351 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11353 // do not add empty global animation events
11354 if (event_value == ANIM_EVENT_NONE)
11357 // if list position is undefined, create new list
11358 if (list_pos == ANIM_EVENT_UNDEFINED)
11359 list_pos = AddGlobalAnimEventList();
11361 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11362 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11363 int value_pos = gaeli->num_event_values++;
11365 gaeli->event_value = checked_realloc(gaeli->event_value,
11366 gaeli->num_event_values * sizeof(int *));
11368 gaeli->event_value[value_pos] = event_value;
11373 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11375 if (list_pos == ANIM_EVENT_UNDEFINED)
11376 return ANIM_EVENT_NONE;
11378 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11379 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11381 return gaeli->event_value[value_pos];
11384 int GetGlobalAnimEventValueCount(int list_pos)
11386 if (list_pos == ANIM_EVENT_UNDEFINED)
11389 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11390 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11392 return gaeli->num_event_values;
11395 // This function checks if a string <s> of the format "string1, string2, ..."
11396 // exactly contains a string <s_contained>.
11398 static boolean string_has_parameter(char *s, char *s_contained)
11402 if (s == NULL || s_contained == NULL)
11405 if (strlen(s_contained) > strlen(s))
11408 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11410 char next_char = s[strlen(s_contained)];
11412 // check if next character is delimiter or whitespace
11413 return (next_char == ',' || next_char == '\0' ||
11414 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11417 // check if string contains another parameter string after a comma
11418 substring = strchr(s, ',');
11419 if (substring == NULL) // string does not contain a comma
11422 // advance string pointer to next character after the comma
11425 // skip potential whitespaces after the comma
11426 while (*substring == ' ' || *substring == '\t')
11429 return string_has_parameter(substring, s_contained);
11432 static int get_anim_parameter_value(char *s)
11434 int event_value[] =
11442 char *pattern_1[] =
11450 char *pattern_2 = ".part_";
11451 char *matching_char = NULL;
11453 int pattern_1_len = 0;
11454 int result = ANIM_EVENT_NONE;
11457 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11459 matching_char = strstr(s_ptr, pattern_1[i]);
11460 pattern_1_len = strlen(pattern_1[i]);
11461 result = event_value[i];
11463 if (matching_char != NULL)
11467 if (matching_char == NULL)
11468 return ANIM_EVENT_NONE;
11470 s_ptr = matching_char + pattern_1_len;
11472 // check for main animation number ("anim_X" or "anim_XX")
11473 if (*s_ptr >= '0' && *s_ptr <= '9')
11475 int gic_anim_nr = (*s_ptr++ - '0');
11477 if (*s_ptr >= '0' && *s_ptr <= '9')
11478 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11480 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11481 return ANIM_EVENT_NONE;
11483 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11487 // invalid main animation number specified
11489 return ANIM_EVENT_NONE;
11492 // check for animation part number ("part_X" or "part_XX") (optional)
11493 if (strPrefix(s_ptr, pattern_2))
11495 s_ptr += strlen(pattern_2);
11497 if (*s_ptr >= '0' && *s_ptr <= '9')
11499 int gic_part_nr = (*s_ptr++ - '0');
11501 if (*s_ptr >= '0' && *s_ptr <= '9')
11502 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11504 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11505 return ANIM_EVENT_NONE;
11507 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11511 // invalid animation part number specified
11513 return ANIM_EVENT_NONE;
11517 // discard result if next character is neither delimiter nor whitespace
11518 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11519 *s_ptr == ' ' || *s_ptr == '\t'))
11520 return ANIM_EVENT_NONE;
11525 static int get_anim_parameter_values(char *s)
11527 int list_pos = ANIM_EVENT_UNDEFINED;
11528 int event_value = ANIM_EVENT_DEFAULT;
11530 if (string_has_parameter(s, "any"))
11531 event_value |= ANIM_EVENT_ANY;
11533 if (string_has_parameter(s, "click:self") ||
11534 string_has_parameter(s, "click") ||
11535 string_has_parameter(s, "self"))
11536 event_value |= ANIM_EVENT_SELF;
11538 if (string_has_parameter(s, "unclick:any"))
11539 event_value |= ANIM_EVENT_UNCLICK_ANY;
11541 // if animation event found, add it to global animation event list
11542 if (event_value != ANIM_EVENT_NONE)
11543 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11547 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11548 event_value = get_anim_parameter_value(s);
11550 // if animation event found, add it to global animation event list
11551 if (event_value != ANIM_EVENT_NONE)
11552 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11554 // continue with next part of the string, starting with next comma
11555 s = strchr(s + 1, ',');
11561 static int get_anim_action_parameter_value(char *token)
11563 // check most common default case first to massively speed things up
11564 if (strEqual(token, ARG_UNDEFINED))
11565 return ANIM_EVENT_ACTION_NONE;
11567 int result = getImageIDFromToken(token);
11571 char *gfx_token = getStringCat2("gfx.", token);
11573 result = getImageIDFromToken(gfx_token);
11575 checked_free(gfx_token);
11580 Key key = getKeyFromX11KeyName(token);
11582 if (key != KSYM_UNDEFINED)
11583 result = -(int)key;
11590 result = get_hash_from_key(token); // unsigned int => int
11591 result = ABS(result); // may be negative now
11592 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11594 setHashEntry(anim_url_hash, int2str(result, 0), token);
11599 result = ANIM_EVENT_ACTION_NONE;
11604 int get_parameter_value(char *value_raw, char *suffix, int type)
11606 char *value = getStringToLower(value_raw);
11607 int result = 0; // probably a save default value
11609 if (strEqual(suffix, ".direction"))
11611 result = (strEqual(value, "left") ? MV_LEFT :
11612 strEqual(value, "right") ? MV_RIGHT :
11613 strEqual(value, "up") ? MV_UP :
11614 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11616 else if (strEqual(suffix, ".position"))
11618 result = (strEqual(value, "left") ? POS_LEFT :
11619 strEqual(value, "right") ? POS_RIGHT :
11620 strEqual(value, "top") ? POS_TOP :
11621 strEqual(value, "upper") ? POS_UPPER :
11622 strEqual(value, "middle") ? POS_MIDDLE :
11623 strEqual(value, "lower") ? POS_LOWER :
11624 strEqual(value, "bottom") ? POS_BOTTOM :
11625 strEqual(value, "any") ? POS_ANY :
11626 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11628 else if (strEqual(suffix, ".align"))
11630 result = (strEqual(value, "left") ? ALIGN_LEFT :
11631 strEqual(value, "right") ? ALIGN_RIGHT :
11632 strEqual(value, "center") ? ALIGN_CENTER :
11633 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11635 else if (strEqual(suffix, ".valign"))
11637 result = (strEqual(value, "top") ? VALIGN_TOP :
11638 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11639 strEqual(value, "middle") ? VALIGN_MIDDLE :
11640 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11642 else if (strEqual(suffix, ".anim_mode"))
11644 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11645 string_has_parameter(value, "loop") ? ANIM_LOOP :
11646 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11647 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11648 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11649 string_has_parameter(value, "random") ? ANIM_RANDOM :
11650 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11651 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11652 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11653 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11654 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11655 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11656 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11657 string_has_parameter(value, "all") ? ANIM_ALL :
11658 string_has_parameter(value, "tiled") ? ANIM_TILED :
11661 if (string_has_parameter(value, "once"))
11662 result |= ANIM_ONCE;
11664 if (string_has_parameter(value, "reverse"))
11665 result |= ANIM_REVERSE;
11667 if (string_has_parameter(value, "opaque_player"))
11668 result |= ANIM_OPAQUE_PLAYER;
11670 if (string_has_parameter(value, "static_panel"))
11671 result |= ANIM_STATIC_PANEL;
11673 else if (strEqual(suffix, ".init_event") ||
11674 strEqual(suffix, ".anim_event"))
11676 result = get_anim_parameter_values(value);
11678 else if (strEqual(suffix, ".init_delay_action") ||
11679 strEqual(suffix, ".anim_delay_action") ||
11680 strEqual(suffix, ".post_delay_action") ||
11681 strEqual(suffix, ".init_event_action") ||
11682 strEqual(suffix, ".anim_event_action"))
11684 result = get_anim_action_parameter_value(value_raw);
11686 else if (strEqual(suffix, ".class"))
11688 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11689 get_hash_from_key(value));
11691 else if (strEqual(suffix, ".style"))
11693 result = STYLE_DEFAULT;
11695 if (string_has_parameter(value, "accurate_borders"))
11696 result |= STYLE_ACCURATE_BORDERS;
11698 if (string_has_parameter(value, "inner_corners"))
11699 result |= STYLE_INNER_CORNERS;
11701 if (string_has_parameter(value, "reverse"))
11702 result |= STYLE_REVERSE;
11704 if (string_has_parameter(value, "leftmost_position"))
11705 result |= STYLE_LEFTMOST_POSITION;
11707 if (string_has_parameter(value, "block_clicks"))
11708 result |= STYLE_BLOCK;
11710 if (string_has_parameter(value, "passthrough_clicks"))
11711 result |= STYLE_PASSTHROUGH;
11713 if (string_has_parameter(value, "multiple_actions"))
11714 result |= STYLE_MULTIPLE_ACTIONS;
11716 else if (strEqual(suffix, ".fade_mode"))
11718 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11719 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11720 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11721 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11722 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11723 FADE_MODE_DEFAULT);
11725 else if (strEqual(suffix, ".auto_delay_unit"))
11727 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11728 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11729 AUTO_DELAY_UNIT_DEFAULT);
11731 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11733 result = gfx.get_font_from_token_function(value);
11735 else // generic parameter of type integer or boolean
11737 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11738 type == TYPE_INTEGER ? get_integer_from_string(value) :
11739 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11740 ARG_UNDEFINED_VALUE);
11748 static int get_token_parameter_value(char *token, char *value_raw)
11752 if (token == NULL || value_raw == NULL)
11753 return ARG_UNDEFINED_VALUE;
11755 suffix = strrchr(token, '.');
11756 if (suffix == NULL)
11759 if (strEqual(suffix, ".element"))
11760 return getElementFromToken(value_raw);
11762 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11763 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11766 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11767 boolean ignore_defaults)
11771 for (i = 0; image_config_vars[i].token != NULL; i++)
11773 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11775 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11776 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11780 *image_config_vars[i].value =
11781 get_token_parameter_value(image_config_vars[i].token, value);
11785 void InitMenuDesignSettings_Static(void)
11787 // always start with reliable default values from static default config
11788 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11791 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11795 // the following initializes hierarchical values from static configuration
11797 // special case: initialize "ARG_DEFAULT" values in static default config
11798 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11799 titlescreen_initial_first_default.fade_mode =
11800 title_initial_first_default.fade_mode;
11801 titlescreen_initial_first_default.fade_delay =
11802 title_initial_first_default.fade_delay;
11803 titlescreen_initial_first_default.post_delay =
11804 title_initial_first_default.post_delay;
11805 titlescreen_initial_first_default.auto_delay =
11806 title_initial_first_default.auto_delay;
11807 titlescreen_initial_first_default.auto_delay_unit =
11808 title_initial_first_default.auto_delay_unit;
11809 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
11810 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11811 titlescreen_first_default.post_delay = title_first_default.post_delay;
11812 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11813 titlescreen_first_default.auto_delay_unit =
11814 title_first_default.auto_delay_unit;
11815 titlemessage_initial_first_default.fade_mode =
11816 title_initial_first_default.fade_mode;
11817 titlemessage_initial_first_default.fade_delay =
11818 title_initial_first_default.fade_delay;
11819 titlemessage_initial_first_default.post_delay =
11820 title_initial_first_default.post_delay;
11821 titlemessage_initial_first_default.auto_delay =
11822 title_initial_first_default.auto_delay;
11823 titlemessage_initial_first_default.auto_delay_unit =
11824 title_initial_first_default.auto_delay_unit;
11825 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
11826 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11827 titlemessage_first_default.post_delay = title_first_default.post_delay;
11828 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11829 titlemessage_first_default.auto_delay_unit =
11830 title_first_default.auto_delay_unit;
11832 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
11833 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11834 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11835 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11836 titlescreen_initial_default.auto_delay_unit =
11837 title_initial_default.auto_delay_unit;
11838 titlescreen_default.fade_mode = title_default.fade_mode;
11839 titlescreen_default.fade_delay = title_default.fade_delay;
11840 titlescreen_default.post_delay = title_default.post_delay;
11841 titlescreen_default.auto_delay = title_default.auto_delay;
11842 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11843 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
11844 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11845 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11846 titlemessage_initial_default.auto_delay_unit =
11847 title_initial_default.auto_delay_unit;
11848 titlemessage_default.fade_mode = title_default.fade_mode;
11849 titlemessage_default.fade_delay = title_default.fade_delay;
11850 titlemessage_default.post_delay = title_default.post_delay;
11851 titlemessage_default.auto_delay = title_default.auto_delay;
11852 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11854 // special case: initialize "ARG_DEFAULT" values in static default config
11855 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11856 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11858 titlescreen_initial_first[i] = titlescreen_initial_first_default;
11859 titlescreen_first[i] = titlescreen_first_default;
11860 titlemessage_initial_first[i] = titlemessage_initial_first_default;
11861 titlemessage_first[i] = titlemessage_first_default;
11863 titlescreen_initial[i] = titlescreen_initial_default;
11864 titlescreen[i] = titlescreen_default;
11865 titlemessage_initial[i] = titlemessage_initial_default;
11866 titlemessage[i] = titlemessage_default;
11869 // special case: initialize "ARG_DEFAULT" values in static default config
11870 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11871 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11873 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11876 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11877 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11878 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11881 // special case: initialize "ARG_DEFAULT" values in static default config
11882 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11883 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11885 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11886 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11887 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11889 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
11892 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11896 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11900 struct XY *dst, *src;
11902 game_buttons_xy[] =
11904 { &game.button.save, &game.button.stop },
11905 { &game.button.pause2, &game.button.pause },
11906 { &game.button.load, &game.button.play },
11907 { &game.button.undo, &game.button.stop },
11908 { &game.button.redo, &game.button.play },
11914 // special case: initialize later added SETUP list size from LEVELS value
11915 if (menu.list_size[GAME_MODE_SETUP] == -1)
11916 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11918 // set default position for snapshot buttons to stop/pause/play buttons
11919 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11920 if ((*game_buttons_xy[i].dst).x == -1 &&
11921 (*game_buttons_xy[i].dst).y == -1)
11922 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11924 // --------------------------------------------------------------------------
11925 // dynamic viewports (including playfield margins, borders and alignments)
11926 // --------------------------------------------------------------------------
11928 // dynamic viewports currently only supported for landscape mode
11929 int display_width = MAX(video.display_width, video.display_height);
11930 int display_height = MIN(video.display_width, video.display_height);
11932 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11934 struct RectWithBorder *vp_window = &viewport.window[i];
11935 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11936 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
11937 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
11938 boolean dynamic_window_width = (vp_window->min_width != -1);
11939 boolean dynamic_window_height = (vp_window->min_height != -1);
11940 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
11941 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11943 // adjust window size if min/max width/height is specified
11945 if (vp_window->min_width != -1)
11947 int window_width = display_width;
11949 // when using static window height, use aspect ratio of display
11950 if (vp_window->min_height == -1)
11951 window_width = vp_window->height * display_width / display_height;
11953 vp_window->width = MAX(vp_window->min_width, window_width);
11956 if (vp_window->min_height != -1)
11958 int window_height = display_height;
11960 // when using static window width, use aspect ratio of display
11961 if (vp_window->min_width == -1)
11962 window_height = vp_window->width * display_height / display_width;
11964 vp_window->height = MAX(vp_window->min_height, window_height);
11967 if (vp_window->max_width != -1)
11968 vp_window->width = MIN(vp_window->width, vp_window->max_width);
11970 if (vp_window->max_height != -1)
11971 vp_window->height = MIN(vp_window->height, vp_window->max_height);
11973 int playfield_width = vp_window->width;
11974 int playfield_height = vp_window->height;
11976 // adjust playfield size and position according to specified margins
11978 playfield_width -= vp_playfield->margin_left;
11979 playfield_width -= vp_playfield->margin_right;
11981 playfield_height -= vp_playfield->margin_top;
11982 playfield_height -= vp_playfield->margin_bottom;
11984 // adjust playfield size if min/max width/height is specified
11986 if (vp_playfield->min_width != -1)
11987 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
11989 if (vp_playfield->min_height != -1)
11990 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
11992 if (vp_playfield->max_width != -1)
11993 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
11995 if (vp_playfield->max_height != -1)
11996 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
11998 // adjust playfield position according to specified alignment
12000 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12001 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12002 else if (vp_playfield->align == ALIGN_CENTER)
12003 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12004 else if (vp_playfield->align == ALIGN_RIGHT)
12005 vp_playfield->x += playfield_width - vp_playfield->width;
12007 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12008 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12009 else if (vp_playfield->valign == VALIGN_MIDDLE)
12010 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12011 else if (vp_playfield->valign == VALIGN_BOTTOM)
12012 vp_playfield->y += playfield_height - vp_playfield->height;
12014 vp_playfield->x += vp_playfield->margin_left;
12015 vp_playfield->y += vp_playfield->margin_top;
12017 // adjust individual playfield borders if only default border is specified
12019 if (vp_playfield->border_left == -1)
12020 vp_playfield->border_left = vp_playfield->border_size;
12021 if (vp_playfield->border_right == -1)
12022 vp_playfield->border_right = vp_playfield->border_size;
12023 if (vp_playfield->border_top == -1)
12024 vp_playfield->border_top = vp_playfield->border_size;
12025 if (vp_playfield->border_bottom == -1)
12026 vp_playfield->border_bottom = vp_playfield->border_size;
12028 // set dynamic playfield borders if borders are specified as undefined
12029 // (but only if window size was dynamic and playfield size was static)
12031 if (dynamic_window_width && !dynamic_playfield_width)
12033 if (vp_playfield->border_left == -1)
12035 vp_playfield->border_left = (vp_playfield->x -
12036 vp_playfield->margin_left);
12037 vp_playfield->x -= vp_playfield->border_left;
12038 vp_playfield->width += vp_playfield->border_left;
12041 if (vp_playfield->border_right == -1)
12043 vp_playfield->border_right = (vp_window->width -
12045 vp_playfield->width -
12046 vp_playfield->margin_right);
12047 vp_playfield->width += vp_playfield->border_right;
12051 if (dynamic_window_height && !dynamic_playfield_height)
12053 if (vp_playfield->border_top == -1)
12055 vp_playfield->border_top = (vp_playfield->y -
12056 vp_playfield->margin_top);
12057 vp_playfield->y -= vp_playfield->border_top;
12058 vp_playfield->height += vp_playfield->border_top;
12061 if (vp_playfield->border_bottom == -1)
12063 vp_playfield->border_bottom = (vp_window->height -
12065 vp_playfield->height -
12066 vp_playfield->margin_bottom);
12067 vp_playfield->height += vp_playfield->border_bottom;
12071 // adjust playfield size to be a multiple of a defined alignment tile size
12073 int align_size = vp_playfield->align_size;
12074 int playfield_xtiles = vp_playfield->width / align_size;
12075 int playfield_ytiles = vp_playfield->height / align_size;
12076 int playfield_width_corrected = playfield_xtiles * align_size;
12077 int playfield_height_corrected = playfield_ytiles * align_size;
12078 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12079 i == GFX_SPECIAL_ARG_EDITOR);
12081 if (is_playfield_mode &&
12082 dynamic_playfield_width &&
12083 vp_playfield->width != playfield_width_corrected)
12085 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12087 vp_playfield->width = playfield_width_corrected;
12089 if (vp_playfield->align == ALIGN_LEFT)
12091 vp_playfield->border_left += playfield_xdiff;
12093 else if (vp_playfield->align == ALIGN_RIGHT)
12095 vp_playfield->border_right += playfield_xdiff;
12097 else if (vp_playfield->align == ALIGN_CENTER)
12099 int border_left_diff = playfield_xdiff / 2;
12100 int border_right_diff = playfield_xdiff - border_left_diff;
12102 vp_playfield->border_left += border_left_diff;
12103 vp_playfield->border_right += border_right_diff;
12107 if (is_playfield_mode &&
12108 dynamic_playfield_height &&
12109 vp_playfield->height != playfield_height_corrected)
12111 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12113 vp_playfield->height = playfield_height_corrected;
12115 if (vp_playfield->valign == VALIGN_TOP)
12117 vp_playfield->border_top += playfield_ydiff;
12119 else if (vp_playfield->align == VALIGN_BOTTOM)
12121 vp_playfield->border_right += playfield_ydiff;
12123 else if (vp_playfield->align == VALIGN_MIDDLE)
12125 int border_top_diff = playfield_ydiff / 2;
12126 int border_bottom_diff = playfield_ydiff - border_top_diff;
12128 vp_playfield->border_top += border_top_diff;
12129 vp_playfield->border_bottom += border_bottom_diff;
12133 // adjust door positions according to specified alignment
12135 for (j = 0; j < 2; j++)
12137 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12139 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12140 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12141 else if (vp_door->align == ALIGN_CENTER)
12142 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12143 else if (vp_door->align == ALIGN_RIGHT)
12144 vp_door->x += vp_window->width - vp_door->width;
12146 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12147 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12148 else if (vp_door->valign == VALIGN_MIDDLE)
12149 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12150 else if (vp_door->valign == VALIGN_BOTTOM)
12151 vp_door->y += vp_window->height - vp_door->height;
12156 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12160 struct XYTileSize *dst, *src;
12163 editor_buttons_xy[] =
12166 &editor.button.element_left, &editor.palette.element_left,
12167 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12170 &editor.button.element_middle, &editor.palette.element_middle,
12171 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12174 &editor.button.element_right, &editor.palette.element_right,
12175 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12182 // set default position for element buttons to element graphics
12183 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12185 if ((*editor_buttons_xy[i].dst).x == -1 &&
12186 (*editor_buttons_xy[i].dst).y == -1)
12188 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12190 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12192 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12196 // adjust editor palette rows and columns if specified to be dynamic
12198 if (editor.palette.cols == -1)
12200 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12201 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12202 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12204 editor.palette.cols = (vp_width - sc_width) / bt_width;
12206 if (editor.palette.x == -1)
12208 int palette_width = editor.palette.cols * bt_width + sc_width;
12210 editor.palette.x = (vp_width - palette_width) / 2;
12214 if (editor.palette.rows == -1)
12216 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12217 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12218 int tx_height = getFontHeight(FONT_TEXT_2);
12220 editor.palette.rows = (vp_height - tx_height) / bt_height;
12222 if (editor.palette.y == -1)
12224 int palette_height = editor.palette.rows * bt_height + tx_height;
12226 editor.palette.y = (vp_height - palette_height) / 2;
12231 static void LoadMenuDesignSettingsFromFilename(char *filename)
12233 static struct TitleFadingInfo tfi;
12234 static struct TitleMessageInfo tmi;
12235 static struct TokenInfo title_tokens[] =
12237 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12238 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12239 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12240 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12241 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12245 static struct TokenInfo titlemessage_tokens[] =
12247 { TYPE_INTEGER, &tmi.x, ".x" },
12248 { TYPE_INTEGER, &tmi.y, ".y" },
12249 { TYPE_INTEGER, &tmi.width, ".width" },
12250 { TYPE_INTEGER, &tmi.height, ".height" },
12251 { TYPE_INTEGER, &tmi.chars, ".chars" },
12252 { TYPE_INTEGER, &tmi.lines, ".lines" },
12253 { TYPE_INTEGER, &tmi.align, ".align" },
12254 { TYPE_INTEGER, &tmi.valign, ".valign" },
12255 { TYPE_INTEGER, &tmi.font, ".font" },
12256 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12257 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12258 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12259 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12260 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12261 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12262 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12263 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12264 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12270 struct TitleFadingInfo *info;
12275 // initialize first titles from "enter screen" definitions, if defined
12276 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12277 { &title_first_default, "menu.enter_screen.TITLE" },
12279 // initialize title screens from "next screen" definitions, if defined
12280 { &title_initial_default, "menu.next_screen.TITLE" },
12281 { &title_default, "menu.next_screen.TITLE" },
12287 struct TitleMessageInfo *array;
12290 titlemessage_arrays[] =
12292 // initialize first titles from "enter screen" definitions, if defined
12293 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12294 { titlescreen_first, "menu.enter_screen.TITLE" },
12295 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12296 { titlemessage_first, "menu.enter_screen.TITLE" },
12298 // initialize titles from "next screen" definitions, if defined
12299 { titlescreen_initial, "menu.next_screen.TITLE" },
12300 { titlescreen, "menu.next_screen.TITLE" },
12301 { titlemessage_initial, "menu.next_screen.TITLE" },
12302 { titlemessage, "menu.next_screen.TITLE" },
12304 // overwrite titles with title definitions, if defined
12305 { titlescreen_initial_first, "[title_initial]" },
12306 { titlescreen_first, "[title]" },
12307 { titlemessage_initial_first, "[title_initial]" },
12308 { titlemessage_first, "[title]" },
12310 { titlescreen_initial, "[title_initial]" },
12311 { titlescreen, "[title]" },
12312 { titlemessage_initial, "[title_initial]" },
12313 { titlemessage, "[title]" },
12315 // overwrite titles with title screen/message definitions, if defined
12316 { titlescreen_initial_first, "[titlescreen_initial]" },
12317 { titlescreen_first, "[titlescreen]" },
12318 { titlemessage_initial_first, "[titlemessage_initial]" },
12319 { titlemessage_first, "[titlemessage]" },
12321 { titlescreen_initial, "[titlescreen_initial]" },
12322 { titlescreen, "[titlescreen]" },
12323 { titlemessage_initial, "[titlemessage_initial]" },
12324 { titlemessage, "[titlemessage]" },
12328 SetupFileHash *setup_file_hash;
12331 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12334 // the following initializes hierarchical values from dynamic configuration
12336 // special case: initialize with default values that may be overwritten
12337 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12338 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12340 struct TokenIntPtrInfo menu_config[] =
12342 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12343 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12344 { "menu.list_size", &menu.list_size[i] }
12347 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12349 char *token = menu_config[j].token;
12350 char *value = getHashEntry(setup_file_hash, token);
12353 *menu_config[j].value = get_integer_from_string(value);
12357 // special case: initialize with default values that may be overwritten
12358 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12359 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12361 struct TokenIntPtrInfo menu_config[] =
12363 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12364 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12365 { "menu.list_size.INFO", &menu.list_size_info[i] }
12368 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12370 char *token = menu_config[j].token;
12371 char *value = getHashEntry(setup_file_hash, token);
12374 *menu_config[j].value = get_integer_from_string(value);
12378 // special case: initialize with default values that may be overwritten
12379 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12380 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12382 struct TokenIntPtrInfo menu_config[] =
12384 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12385 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12388 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12390 char *token = menu_config[j].token;
12391 char *value = getHashEntry(setup_file_hash, token);
12394 *menu_config[j].value = get_integer_from_string(value);
12398 // special case: initialize with default values that may be overwritten
12399 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12400 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12402 struct TokenIntPtrInfo menu_config[] =
12404 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12405 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12406 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12407 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12408 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12409 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12410 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12411 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12412 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12415 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12417 char *token = menu_config[j].token;
12418 char *value = getHashEntry(setup_file_hash, token);
12421 *menu_config[j].value = get_integer_from_string(value);
12425 // special case: initialize with default values that may be overwritten
12426 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12427 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12429 struct TokenIntPtrInfo menu_config[] =
12431 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12432 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12433 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12434 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12435 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12436 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12437 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12438 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12439 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12442 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12444 char *token = menu_config[j].token;
12445 char *value = getHashEntry(setup_file_hash, token);
12448 *menu_config[j].value = get_token_parameter_value(token, value);
12452 // special case: initialize with default values that may be overwritten
12453 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12454 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12458 char *token_prefix;
12459 struct RectWithBorder *struct_ptr;
12463 { "viewport.window", &viewport.window[i] },
12464 { "viewport.playfield", &viewport.playfield[i] },
12465 { "viewport.door_1", &viewport.door_1[i] },
12466 { "viewport.door_2", &viewport.door_2[i] }
12469 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12471 struct TokenIntPtrInfo vp_config[] =
12473 { ".x", &vp_struct[j].struct_ptr->x },
12474 { ".y", &vp_struct[j].struct_ptr->y },
12475 { ".width", &vp_struct[j].struct_ptr->width },
12476 { ".height", &vp_struct[j].struct_ptr->height },
12477 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12478 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12479 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12480 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12481 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12482 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12483 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12484 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12485 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12486 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12487 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12488 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12489 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12490 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12491 { ".align", &vp_struct[j].struct_ptr->align },
12492 { ".valign", &vp_struct[j].struct_ptr->valign }
12495 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12497 char *token = getStringCat2(vp_struct[j].token_prefix,
12498 vp_config[k].token);
12499 char *value = getHashEntry(setup_file_hash, token);
12502 *vp_config[k].value = get_token_parameter_value(token, value);
12509 // special case: initialize with default values that may be overwritten
12510 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12511 for (i = 0; title_info[i].info != NULL; i++)
12513 struct TitleFadingInfo *info = title_info[i].info;
12514 char *base_token = title_info[i].text;
12516 for (j = 0; title_tokens[j].type != -1; j++)
12518 char *token = getStringCat2(base_token, title_tokens[j].text);
12519 char *value = getHashEntry(setup_file_hash, token);
12523 int parameter_value = get_token_parameter_value(token, value);
12527 *(int *)title_tokens[j].value = (int)parameter_value;
12536 // special case: initialize with default values that may be overwritten
12537 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12538 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12540 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12541 char *base_token = titlemessage_arrays[i].text;
12543 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12545 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12546 char *value = getHashEntry(setup_file_hash, token);
12550 int parameter_value = get_token_parameter_value(token, value);
12552 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12556 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12557 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12559 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12569 // special case: check if network and preview player positions are redefined,
12570 // to compare this later against the main menu level preview being redefined
12571 struct TokenIntPtrInfo menu_config_players[] =
12573 { "main.network_players.x", &menu.main.network_players.redefined },
12574 { "main.network_players.y", &menu.main.network_players.redefined },
12575 { "main.preview_players.x", &menu.main.preview_players.redefined },
12576 { "main.preview_players.y", &menu.main.preview_players.redefined },
12577 { "preview.x", &preview.redefined },
12578 { "preview.y", &preview.redefined }
12581 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12582 *menu_config_players[i].value = FALSE;
12584 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12585 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12586 *menu_config_players[i].value = TRUE;
12588 // read (and overwrite with) values that may be specified in config file
12589 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12591 freeSetupFileHash(setup_file_hash);
12594 void LoadMenuDesignSettings(void)
12596 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12598 InitMenuDesignSettings_Static();
12599 InitMenuDesignSettings_SpecialPreProcessing();
12601 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12603 // first look for special settings configured in level series config
12604 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12606 if (fileExists(filename_base))
12607 LoadMenuDesignSettingsFromFilename(filename_base);
12610 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12612 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12613 LoadMenuDesignSettingsFromFilename(filename_local);
12615 InitMenuDesignSettings_SpecialPostProcessing();
12618 void LoadMenuDesignSettings_AfterGraphics(void)
12620 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12623 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12625 char *filename = getEditorSetupFilename();
12626 SetupFileList *setup_file_list, *list;
12627 SetupFileHash *element_hash;
12628 int num_unknown_tokens = 0;
12631 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12634 element_hash = newSetupFileHash();
12636 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12637 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12639 // determined size may be larger than needed (due to unknown elements)
12641 for (list = setup_file_list; list != NULL; list = list->next)
12644 // add space for up to 3 more elements for padding that may be needed
12645 *num_elements += 3;
12647 // free memory for old list of elements, if needed
12648 checked_free(*elements);
12650 // allocate memory for new list of elements
12651 *elements = checked_malloc(*num_elements * sizeof(int));
12654 for (list = setup_file_list; list != NULL; list = list->next)
12656 char *value = getHashEntry(element_hash, list->token);
12658 if (value == NULL) // try to find obsolete token mapping
12660 char *mapped_token = get_mapped_token(list->token);
12662 if (mapped_token != NULL)
12664 value = getHashEntry(element_hash, mapped_token);
12666 free(mapped_token);
12672 (*elements)[(*num_elements)++] = atoi(value);
12676 if (num_unknown_tokens == 0)
12679 Warn("unknown token(s) found in config file:");
12680 Warn("- config file: '%s'", filename);
12682 num_unknown_tokens++;
12685 Warn("- token: '%s'", list->token);
12689 if (num_unknown_tokens > 0)
12692 while (*num_elements % 4) // pad with empty elements, if needed
12693 (*elements)[(*num_elements)++] = EL_EMPTY;
12695 freeSetupFileList(setup_file_list);
12696 freeSetupFileHash(element_hash);
12699 for (i = 0; i < *num_elements; i++)
12700 Debug("editor", "element '%s' [%d]\n",
12701 element_info[(*elements)[i]].token_name, (*elements)[i]);
12705 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12708 SetupFileHash *setup_file_hash = NULL;
12709 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12710 char *filename_music, *filename_prefix, *filename_info;
12716 token_to_value_ptr[] =
12718 { "title_header", &tmp_music_file_info.title_header },
12719 { "artist_header", &tmp_music_file_info.artist_header },
12720 { "album_header", &tmp_music_file_info.album_header },
12721 { "year_header", &tmp_music_file_info.year_header },
12723 { "title", &tmp_music_file_info.title },
12724 { "artist", &tmp_music_file_info.artist },
12725 { "album", &tmp_music_file_info.album },
12726 { "year", &tmp_music_file_info.year },
12732 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12733 getCustomMusicFilename(basename));
12735 if (filename_music == NULL)
12738 // ---------- try to replace file extension ----------
12740 filename_prefix = getStringCopy(filename_music);
12741 if (strrchr(filename_prefix, '.') != NULL)
12742 *strrchr(filename_prefix, '.') = '\0';
12743 filename_info = getStringCat2(filename_prefix, ".txt");
12745 if (fileExists(filename_info))
12746 setup_file_hash = loadSetupFileHash(filename_info);
12748 free(filename_prefix);
12749 free(filename_info);
12751 if (setup_file_hash == NULL)
12753 // ---------- try to add file extension ----------
12755 filename_prefix = getStringCopy(filename_music);
12756 filename_info = getStringCat2(filename_prefix, ".txt");
12758 if (fileExists(filename_info))
12759 setup_file_hash = loadSetupFileHash(filename_info);
12761 free(filename_prefix);
12762 free(filename_info);
12765 if (setup_file_hash == NULL)
12768 // ---------- music file info found ----------
12770 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12772 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12774 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12776 *token_to_value_ptr[i].value_ptr =
12777 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12780 tmp_music_file_info.basename = getStringCopy(basename);
12781 tmp_music_file_info.music = music;
12782 tmp_music_file_info.is_sound = is_sound;
12784 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12785 *new_music_file_info = tmp_music_file_info;
12787 return new_music_file_info;
12790 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12792 return get_music_file_info_ext(basename, music, FALSE);
12795 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12797 return get_music_file_info_ext(basename, sound, TRUE);
12800 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12801 char *basename, boolean is_sound)
12803 for (; list != NULL; list = list->next)
12804 if (list->is_sound == is_sound && strEqual(list->basename, basename))
12810 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12812 return music_info_listed_ext(list, basename, FALSE);
12815 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12817 return music_info_listed_ext(list, basename, TRUE);
12820 void LoadMusicInfo(void)
12822 char *music_directory = getCustomMusicDirectory();
12823 int num_music = getMusicListSize();
12824 int num_music_noconf = 0;
12825 int num_sounds = getSoundListSize();
12827 DirectoryEntry *dir_entry;
12828 struct FileInfo *music, *sound;
12829 struct MusicFileInfo *next, **new;
12832 while (music_file_info != NULL)
12834 next = music_file_info->next;
12836 checked_free(music_file_info->basename);
12838 checked_free(music_file_info->title_header);
12839 checked_free(music_file_info->artist_header);
12840 checked_free(music_file_info->album_header);
12841 checked_free(music_file_info->year_header);
12843 checked_free(music_file_info->title);
12844 checked_free(music_file_info->artist);
12845 checked_free(music_file_info->album);
12846 checked_free(music_file_info->year);
12848 free(music_file_info);
12850 music_file_info = next;
12853 new = &music_file_info;
12855 for (i = 0; i < num_music; i++)
12857 music = getMusicListEntry(i);
12859 if (music->filename == NULL)
12862 if (strEqual(music->filename, UNDEFINED_FILENAME))
12865 // a configured file may be not recognized as music
12866 if (!FileIsMusic(music->filename))
12869 if (!music_info_listed(music_file_info, music->filename))
12871 *new = get_music_file_info(music->filename, i);
12874 new = &(*new)->next;
12878 if ((dir = openDirectory(music_directory)) == NULL)
12880 Warn("cannot read music directory '%s'", music_directory);
12885 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12887 char *basename = dir_entry->basename;
12888 boolean music_already_used = FALSE;
12891 // skip all music files that are configured in music config file
12892 for (i = 0; i < num_music; i++)
12894 music = getMusicListEntry(i);
12896 if (music->filename == NULL)
12899 if (strEqual(basename, music->filename))
12901 music_already_used = TRUE;
12906 if (music_already_used)
12909 if (!FileIsMusic(dir_entry->filename))
12912 if (!music_info_listed(music_file_info, basename))
12914 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12917 new = &(*new)->next;
12920 num_music_noconf++;
12923 closeDirectory(dir);
12925 for (i = 0; i < num_sounds; i++)
12927 sound = getSoundListEntry(i);
12929 if (sound->filename == NULL)
12932 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12935 // a configured file may be not recognized as sound
12936 if (!FileIsSound(sound->filename))
12939 if (!sound_info_listed(music_file_info, sound->filename))
12941 *new = get_sound_file_info(sound->filename, i);
12943 new = &(*new)->next;
12947 // add pointers to previous list nodes
12949 struct MusicFileInfo *node = music_file_info;
12951 while (node != NULL)
12954 node->next->prev = node;
12960 static void add_helpanim_entry(int element, int action, int direction,
12961 int delay, int *num_list_entries)
12963 struct HelpAnimInfo *new_list_entry;
12964 (*num_list_entries)++;
12967 checked_realloc(helpanim_info,
12968 *num_list_entries * sizeof(struct HelpAnimInfo));
12969 new_list_entry = &helpanim_info[*num_list_entries - 1];
12971 new_list_entry->element = element;
12972 new_list_entry->action = action;
12973 new_list_entry->direction = direction;
12974 new_list_entry->delay = delay;
12977 static void print_unknown_token(char *filename, char *token, int token_nr)
12982 Warn("unknown token(s) found in config file:");
12983 Warn("- config file: '%s'", filename);
12986 Warn("- token: '%s'", token);
12989 static void print_unknown_token_end(int token_nr)
12995 void LoadHelpAnimInfo(void)
12997 char *filename = getHelpAnimFilename();
12998 SetupFileList *setup_file_list = NULL, *list;
12999 SetupFileHash *element_hash, *action_hash, *direction_hash;
13000 int num_list_entries = 0;
13001 int num_unknown_tokens = 0;
13004 if (fileExists(filename))
13005 setup_file_list = loadSetupFileList(filename);
13007 if (setup_file_list == NULL)
13009 // use reliable default values from static configuration
13010 SetupFileList *insert_ptr;
13012 insert_ptr = setup_file_list =
13013 newSetupFileList(helpanim_config[0].token,
13014 helpanim_config[0].value);
13016 for (i = 1; helpanim_config[i].token; i++)
13017 insert_ptr = addListEntry(insert_ptr,
13018 helpanim_config[i].token,
13019 helpanim_config[i].value);
13022 element_hash = newSetupFileHash();
13023 action_hash = newSetupFileHash();
13024 direction_hash = newSetupFileHash();
13026 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13027 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13029 for (i = 0; i < NUM_ACTIONS; i++)
13030 setHashEntry(action_hash, element_action_info[i].suffix,
13031 i_to_a(element_action_info[i].value));
13033 // do not store direction index (bit) here, but direction value!
13034 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13035 setHashEntry(direction_hash, element_direction_info[i].suffix,
13036 i_to_a(1 << element_direction_info[i].value));
13038 for (list = setup_file_list; list != NULL; list = list->next)
13040 char *element_token, *action_token, *direction_token;
13041 char *element_value, *action_value, *direction_value;
13042 int delay = atoi(list->value);
13044 if (strEqual(list->token, "end"))
13046 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13051 /* first try to break element into element/action/direction parts;
13052 if this does not work, also accept combined "element[.act][.dir]"
13053 elements (like "dynamite.active"), which are unique elements */
13055 if (strchr(list->token, '.') == NULL) // token contains no '.'
13057 element_value = getHashEntry(element_hash, list->token);
13058 if (element_value != NULL) // element found
13059 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13060 &num_list_entries);
13063 // no further suffixes found -- this is not an element
13064 print_unknown_token(filename, list->token, num_unknown_tokens++);
13070 // token has format "<prefix>.<something>"
13072 action_token = strchr(list->token, '.'); // suffix may be action ...
13073 direction_token = action_token; // ... or direction
13075 element_token = getStringCopy(list->token);
13076 *strchr(element_token, '.') = '\0';
13078 element_value = getHashEntry(element_hash, element_token);
13080 if (element_value == NULL) // this is no element
13082 element_value = getHashEntry(element_hash, list->token);
13083 if (element_value != NULL) // combined element found
13084 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13085 &num_list_entries);
13087 print_unknown_token(filename, list->token, num_unknown_tokens++);
13089 free(element_token);
13094 action_value = getHashEntry(action_hash, action_token);
13096 if (action_value != NULL) // action found
13098 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13099 &num_list_entries);
13101 free(element_token);
13106 direction_value = getHashEntry(direction_hash, direction_token);
13108 if (direction_value != NULL) // direction found
13110 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13111 &num_list_entries);
13113 free(element_token);
13118 if (strchr(action_token + 1, '.') == NULL)
13120 // no further suffixes found -- this is not an action nor direction
13122 element_value = getHashEntry(element_hash, list->token);
13123 if (element_value != NULL) // combined element found
13124 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13125 &num_list_entries);
13127 print_unknown_token(filename, list->token, num_unknown_tokens++);
13129 free(element_token);
13134 // token has format "<prefix>.<suffix>.<something>"
13136 direction_token = strchr(action_token + 1, '.');
13138 action_token = getStringCopy(action_token);
13139 *strchr(action_token + 1, '.') = '\0';
13141 action_value = getHashEntry(action_hash, action_token);
13143 if (action_value == NULL) // this is no action
13145 element_value = getHashEntry(element_hash, list->token);
13146 if (element_value != NULL) // combined element found
13147 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13148 &num_list_entries);
13150 print_unknown_token(filename, list->token, num_unknown_tokens++);
13152 free(element_token);
13153 free(action_token);
13158 direction_value = getHashEntry(direction_hash, direction_token);
13160 if (direction_value != NULL) // direction found
13162 add_helpanim_entry(atoi(element_value), atoi(action_value),
13163 atoi(direction_value), delay, &num_list_entries);
13165 free(element_token);
13166 free(action_token);
13171 // this is no direction
13173 element_value = getHashEntry(element_hash, list->token);
13174 if (element_value != NULL) // combined element found
13175 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13176 &num_list_entries);
13178 print_unknown_token(filename, list->token, num_unknown_tokens++);
13180 free(element_token);
13181 free(action_token);
13184 print_unknown_token_end(num_unknown_tokens);
13186 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13187 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13189 freeSetupFileList(setup_file_list);
13190 freeSetupFileHash(element_hash);
13191 freeSetupFileHash(action_hash);
13192 freeSetupFileHash(direction_hash);
13195 for (i = 0; i < num_list_entries; i++)
13196 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13197 EL_NAME(helpanim_info[i].element),
13198 helpanim_info[i].element,
13199 helpanim_info[i].action,
13200 helpanim_info[i].direction,
13201 helpanim_info[i].delay);
13205 void LoadHelpTextInfo(void)
13207 char *filename = getHelpTextFilename();
13210 if (helptext_info != NULL)
13212 freeSetupFileHash(helptext_info);
13213 helptext_info = NULL;
13216 if (fileExists(filename))
13217 helptext_info = loadSetupFileHash(filename);
13219 if (helptext_info == NULL)
13221 // use reliable default values from static configuration
13222 helptext_info = newSetupFileHash();
13224 for (i = 0; helptext_config[i].token; i++)
13225 setHashEntry(helptext_info,
13226 helptext_config[i].token,
13227 helptext_config[i].value);
13231 BEGIN_HASH_ITERATION(helptext_info, itr)
13233 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13234 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13236 END_HASH_ITERATION(hash, itr)
13241 // ----------------------------------------------------------------------------
13243 // ----------------------------------------------------------------------------
13245 #define MAX_NUM_CONVERT_LEVELS 1000
13247 void ConvertLevels(void)
13249 static LevelDirTree *convert_leveldir = NULL;
13250 static int convert_level_nr = -1;
13251 static int num_levels_handled = 0;
13252 static int num_levels_converted = 0;
13253 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13256 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13257 global.convert_leveldir);
13259 if (convert_leveldir == NULL)
13260 Fail("no such level identifier: '%s'", global.convert_leveldir);
13262 leveldir_current = convert_leveldir;
13264 if (global.convert_level_nr != -1)
13266 convert_leveldir->first_level = global.convert_level_nr;
13267 convert_leveldir->last_level = global.convert_level_nr;
13270 convert_level_nr = convert_leveldir->first_level;
13272 PrintLine("=", 79);
13273 Print("Converting levels\n");
13274 PrintLine("-", 79);
13275 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13276 Print("Level series name: '%s'\n", convert_leveldir->name);
13277 Print("Level series author: '%s'\n", convert_leveldir->author);
13278 Print("Number of levels: %d\n", convert_leveldir->levels);
13279 PrintLine("=", 79);
13282 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13283 levels_failed[i] = FALSE;
13285 while (convert_level_nr <= convert_leveldir->last_level)
13287 char *level_filename;
13290 level_nr = convert_level_nr++;
13292 Print("Level %03d: ", level_nr);
13294 LoadLevel(level_nr);
13295 if (level.no_level_file || level.no_valid_file)
13297 Print("(no level)\n");
13301 Print("converting level ... ");
13304 // special case: conversion of some EMC levels as requested by ACME
13305 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13308 level_filename = getDefaultLevelFilename(level_nr);
13309 new_level = !fileExists(level_filename);
13313 SaveLevel(level_nr);
13315 num_levels_converted++;
13317 Print("converted.\n");
13321 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13322 levels_failed[level_nr] = TRUE;
13324 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13327 num_levels_handled++;
13331 PrintLine("=", 79);
13332 Print("Number of levels handled: %d\n", num_levels_handled);
13333 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13334 (num_levels_handled ?
13335 num_levels_converted * 100 / num_levels_handled : 0));
13336 PrintLine("-", 79);
13337 Print("Summary (for automatic parsing by scripts):\n");
13338 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13339 convert_leveldir->identifier, num_levels_converted,
13340 num_levels_handled,
13341 (num_levels_handled ?
13342 num_levels_converted * 100 / num_levels_handled : 0));
13344 if (num_levels_handled != num_levels_converted)
13346 Print(", FAILED:");
13347 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13348 if (levels_failed[i])
13353 PrintLine("=", 79);
13355 CloseAllAndExit(0);
13359 // ----------------------------------------------------------------------------
13360 // create and save images for use in level sketches (raw BMP format)
13361 // ----------------------------------------------------------------------------
13363 void CreateLevelSketchImages(void)
13369 InitElementPropertiesGfxElement();
13371 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13372 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13374 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13376 int element = getMappedElement(i);
13377 char basename1[16];
13378 char basename2[16];
13382 sprintf(basename1, "%04d.bmp", i);
13383 sprintf(basename2, "%04ds.bmp", i);
13385 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13386 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13388 DrawSizedElement(0, 0, element, TILESIZE);
13389 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13391 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13392 Fail("cannot save level sketch image file '%s'", filename1);
13394 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13395 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13397 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13398 Fail("cannot save level sketch image file '%s'", filename2);
13403 // create corresponding SQL statements (for normal and small images)
13406 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13407 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13410 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13411 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13413 // optional: create content for forum level sketch demonstration post
13415 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13418 FreeBitmap(bitmap1);
13419 FreeBitmap(bitmap2);
13422 fprintf(stderr, "\n");
13424 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13426 CloseAllAndExit(0);
13430 // ----------------------------------------------------------------------------
13431 // create and save images for element collecting animations (raw BMP format)
13432 // ----------------------------------------------------------------------------
13434 static boolean createCollectImage(int element)
13436 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13439 void CreateCollectElementImages(void)
13443 int anim_frames = num_steps - 1;
13444 int tile_size = TILESIZE;
13445 int anim_width = tile_size * anim_frames;
13446 int anim_height = tile_size;
13447 int num_collect_images = 0;
13448 int pos_collect_images = 0;
13450 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13451 if (createCollectImage(i))
13452 num_collect_images++;
13454 Info("Creating %d element collecting animation images ...",
13455 num_collect_images);
13457 int dst_width = anim_width * 2;
13458 int dst_height = anim_height * num_collect_images / 2;
13459 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13460 char *basename_bmp = "RocksCollect.bmp";
13461 char *basename_png = "RocksCollect.png";
13462 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
13463 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
13464 int len_filename_bmp = strlen(filename_bmp);
13465 int len_filename_png = strlen(filename_png);
13466 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
13467 char cmd_convert[max_command_len];
13469 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
13473 // force using RGBA surface for destination bitmap
13474 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
13475 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
13477 dst_bitmap->surface =
13478 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_RGBA32, 0);
13480 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13482 if (!createCollectImage(i))
13485 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13486 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13487 int graphic = el2img(i);
13488 char *token_name = element_info[i].token_name;
13489 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13490 Bitmap *src_bitmap;
13493 Info("- creating collecting image for '%s' ...", token_name);
13495 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13497 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13498 tile_size, tile_size, 0, 0);
13500 // force using RGBA surface for temporary bitmap (using transparent black)
13501 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
13502 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
13504 tmp_bitmap->surface =
13505 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_RGBA32, 0);
13507 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13509 for (j = 0; j < anim_frames; j++)
13511 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13512 int frame_size = frame_size_final * num_steps;
13513 int offset = (tile_size - frame_size_final) / 2;
13514 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13516 while (frame_size > frame_size_final)
13520 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13522 FreeBitmap(frame_bitmap);
13524 frame_bitmap = half_bitmap;
13527 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
13528 frame_size_final, frame_size_final,
13529 dst_x + j * tile_size + offset, dst_y + offset);
13531 FreeBitmap(frame_bitmap);
13534 tmp_bitmap->surface_masked = NULL;
13536 FreeBitmap(tmp_bitmap);
13538 pos_collect_images++;
13541 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
13542 Fail("cannot save element collecting image file '%s'", filename_bmp);
13544 FreeBitmap(dst_bitmap);
13546 Info("Converting image file from BMP to PNG ...");
13548 system(cmd_convert);
13549 unlink(filename_bmp);
13553 CloseAllAndExit(0);
13557 // ----------------------------------------------------------------------------
13558 // create and save images for custom and group elements (raw BMP format)
13559 // ----------------------------------------------------------------------------
13561 void CreateCustomElementImages(char *directory)
13563 char *src_basename = "RocksCE-template.ilbm";
13564 char *dst_basename = "RocksCE.bmp";
13565 char *src_filename = getPath2(directory, src_basename);
13566 char *dst_filename = getPath2(directory, dst_basename);
13567 Bitmap *src_bitmap;
13569 int yoffset_ce = 0;
13570 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13573 InitVideoDefaults();
13575 ReCreateBitmap(&backbuffer, video.width, video.height);
13577 src_bitmap = LoadImage(src_filename);
13579 bitmap = CreateBitmap(TILEX * 16 * 2,
13580 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13583 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13590 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13591 TILEX * x, TILEY * y + yoffset_ce);
13593 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13595 TILEX * x + TILEX * 16,
13596 TILEY * y + yoffset_ce);
13598 for (j = 2; j >= 0; j--)
13602 BlitBitmap(src_bitmap, bitmap,
13603 TILEX + c * 7, 0, 6, 10,
13604 TILEX * x + 6 + j * 7,
13605 TILEY * y + 11 + yoffset_ce);
13607 BlitBitmap(src_bitmap, bitmap,
13608 TILEX + c * 8, TILEY, 6, 10,
13609 TILEX * 16 + TILEX * x + 6 + j * 8,
13610 TILEY * y + 10 + yoffset_ce);
13616 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13623 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13624 TILEX * x, TILEY * y + yoffset_ge);
13626 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13628 TILEX * x + TILEX * 16,
13629 TILEY * y + yoffset_ge);
13631 for (j = 1; j >= 0; j--)
13635 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13636 TILEX * x + 6 + j * 10,
13637 TILEY * y + 11 + yoffset_ge);
13639 BlitBitmap(src_bitmap, bitmap,
13640 TILEX + c * 8, TILEY + 12, 6, 10,
13641 TILEX * 16 + TILEX * x + 10 + j * 8,
13642 TILEY * y + 10 + yoffset_ge);
13648 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13649 Fail("cannot save CE graphics file '%s'", dst_filename);
13651 FreeBitmap(bitmap);
13653 CloseAllAndExit(0);