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 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3360 xx_change = *change; // copy change data into temporary buffer
3362 resetEventBits(); // reset bits; change page might have changed
3364 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3367 *change = xx_change;
3369 setEventFlagsFromEventBits(change);
3371 if (real_chunk_size >= chunk_size)
3375 level->file_has_custom_elements = TRUE;
3377 return real_chunk_size;
3380 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3382 int element = getMappedElement(getFile16BitBE(file));
3383 int real_chunk_size = 2;
3384 struct ElementInfo *ei = &element_info[element];
3385 struct ElementGroupInfo *group = ei->group;
3387 xx_ei = *ei; // copy element data into temporary buffer
3388 xx_group = *group; // copy group data into temporary buffer
3390 while (!checkEndOfFile(file))
3392 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3395 if (real_chunk_size >= chunk_size)
3402 level->file_has_custom_elements = TRUE;
3404 return real_chunk_size;
3407 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3409 int element = getMappedElement(getFile16BitBE(file));
3410 int real_chunk_size = 2;
3411 struct ElementInfo *ei = &element_info[element];
3413 xx_ei = *ei; // copy element data into temporary buffer
3415 while (!checkEndOfFile(file))
3417 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3420 if (real_chunk_size >= chunk_size)
3426 level->file_has_custom_elements = TRUE;
3428 return real_chunk_size;
3431 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3432 struct LevelFileInfo *level_file_info,
3433 boolean level_info_only)
3435 char *filename = level_file_info->filename;
3436 char cookie[MAX_LINE_LEN];
3437 char chunk_name[CHUNK_ID_LEN + 1];
3441 if (!(file = openFile(filename, MODE_READ)))
3443 level->no_valid_file = TRUE;
3444 level->no_level_file = TRUE;
3446 if (level_info_only)
3449 Warn("cannot read level '%s' -- using empty level", filename);
3451 if (!setup.editor.use_template_for_new_levels)
3454 // if level file not found, try to initialize level data from template
3455 filename = getGlobalLevelTemplateFilename();
3457 if (!(file = openFile(filename, MODE_READ)))
3460 // default: for empty levels, use level template for custom elements
3461 level->use_custom_template = TRUE;
3463 level->no_valid_file = FALSE;
3466 getFileChunkBE(file, chunk_name, NULL);
3467 if (strEqual(chunk_name, "RND1"))
3469 getFile32BitBE(file); // not used
3471 getFileChunkBE(file, chunk_name, NULL);
3472 if (!strEqual(chunk_name, "CAVE"))
3474 level->no_valid_file = TRUE;
3476 Warn("unknown format of level file '%s'", filename);
3483 else // check for pre-2.0 file format with cookie string
3485 strcpy(cookie, chunk_name);
3486 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3488 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3489 cookie[strlen(cookie) - 1] = '\0';
3491 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3493 level->no_valid_file = TRUE;
3495 Warn("unknown format of level file '%s'", filename);
3502 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3504 level->no_valid_file = TRUE;
3506 Warn("unsupported version of level file '%s'", filename);
3513 // pre-2.0 level files have no game version, so use file version here
3514 level->game_version = level->file_version;
3517 if (level->file_version < FILE_VERSION_1_2)
3519 // level files from versions before 1.2.0 without chunk structure
3520 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3521 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3529 int (*loader)(File *, int, struct LevelInfo *);
3533 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3534 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3535 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3536 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3537 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3538 { "INFO", -1, LoadLevel_INFO },
3539 { "BODY", -1, LoadLevel_BODY },
3540 { "CONT", -1, LoadLevel_CONT },
3541 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3542 { "CNT3", -1, LoadLevel_CNT3 },
3543 { "CUS1", -1, LoadLevel_CUS1 },
3544 { "CUS2", -1, LoadLevel_CUS2 },
3545 { "CUS3", -1, LoadLevel_CUS3 },
3546 { "CUS4", -1, LoadLevel_CUS4 },
3547 { "GRP1", -1, LoadLevel_GRP1 },
3548 { "CONF", -1, LoadLevel_CONF },
3549 { "ELEM", -1, LoadLevel_ELEM },
3550 { "NOTE", -1, LoadLevel_NOTE },
3551 { "CUSX", -1, LoadLevel_CUSX },
3552 { "GRPX", -1, LoadLevel_GRPX },
3553 { "EMPX", -1, LoadLevel_EMPX },
3558 while (getFileChunkBE(file, chunk_name, &chunk_size))
3562 while (chunk_info[i].name != NULL &&
3563 !strEqual(chunk_name, chunk_info[i].name))
3566 if (chunk_info[i].name == NULL)
3568 Warn("unknown chunk '%s' in level file '%s'",
3569 chunk_name, filename);
3571 ReadUnusedBytesFromFile(file, chunk_size);
3573 else if (chunk_info[i].size != -1 &&
3574 chunk_info[i].size != chunk_size)
3576 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3577 chunk_size, chunk_name, filename);
3579 ReadUnusedBytesFromFile(file, chunk_size);
3583 // call function to load this level chunk
3584 int chunk_size_expected =
3585 (chunk_info[i].loader)(file, chunk_size, level);
3587 // the size of some chunks cannot be checked before reading other
3588 // chunks first (like "HEAD" and "BODY") that contain some header
3589 // information, so check them here
3590 if (chunk_size_expected != chunk_size)
3592 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3593 chunk_size, chunk_name, filename);
3603 // ----------------------------------------------------------------------------
3604 // functions for loading EM level
3605 // ----------------------------------------------------------------------------
3607 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3609 static int ball_xy[8][2] =
3620 struct LevelInfo_EM *level_em = level->native_em_level;
3621 struct CAVE *cav = level_em->cav;
3624 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3625 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3627 cav->time_seconds = level->time;
3628 cav->gems_needed = level->gems_needed;
3630 cav->emerald_score = level->score[SC_EMERALD];
3631 cav->diamond_score = level->score[SC_DIAMOND];
3632 cav->alien_score = level->score[SC_ROBOT];
3633 cav->tank_score = level->score[SC_SPACESHIP];
3634 cav->bug_score = level->score[SC_BUG];
3635 cav->eater_score = level->score[SC_YAMYAM];
3636 cav->nut_score = level->score[SC_NUT];
3637 cav->dynamite_score = level->score[SC_DYNAMITE];
3638 cav->key_score = level->score[SC_KEY];
3639 cav->exit_score = level->score[SC_TIME_BONUS];
3641 cav->num_eater_arrays = level->num_yamyam_contents;
3643 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3644 for (y = 0; y < 3; y++)
3645 for (x = 0; x < 3; x++)
3646 cav->eater_array[i][y * 3 + x] =
3647 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3649 cav->amoeba_time = level->amoeba_speed;
3650 cav->wonderwall_time = level->time_magic_wall;
3651 cav->wheel_time = level->time_wheel;
3653 cav->android_move_time = level->android_move_time;
3654 cav->android_clone_time = level->android_clone_time;
3655 cav->ball_random = level->ball_random;
3656 cav->ball_active = level->ball_active_initial;
3657 cav->ball_time = level->ball_time;
3658 cav->num_ball_arrays = level->num_ball_contents;
3660 cav->lenses_score = level->lenses_score;
3661 cav->magnify_score = level->magnify_score;
3662 cav->slurp_score = level->slurp_score;
3664 cav->lenses_time = level->lenses_time;
3665 cav->magnify_time = level->magnify_time;
3667 cav->wind_direction =
3668 map_direction_RND_to_EM(level->wind_direction_initial);
3670 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3671 for (j = 0; j < 8; j++)
3672 cav->ball_array[i][j] =
3673 map_element_RND_to_EM_cave(level->ball_content[i].
3674 e[ball_xy[j][0]][ball_xy[j][1]]);
3676 map_android_clone_elements_RND_to_EM(level);
3678 // first fill the complete playfield with the empty space element
3679 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3680 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3681 cav->cave[x][y] = Cblank;
3683 // then copy the real level contents from level file into the playfield
3684 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3686 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3688 if (level->field[x][y] == EL_AMOEBA_DEAD)
3689 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3691 cav->cave[x][y] = new_element;
3694 for (i = 0; i < MAX_PLAYERS; i++)
3696 cav->player_x[i] = -1;
3697 cav->player_y[i] = -1;
3700 // initialize player positions and delete players from the playfield
3701 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3703 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3705 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3707 cav->player_x[player_nr] = x;
3708 cav->player_y[player_nr] = y;
3710 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3715 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3717 static int ball_xy[8][2] =
3728 struct LevelInfo_EM *level_em = level->native_em_level;
3729 struct CAVE *cav = level_em->cav;
3732 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
3733 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
3735 level->time = cav->time_seconds;
3736 level->gems_needed = cav->gems_needed;
3738 sprintf(level->name, "Level %d", level->file_info.nr);
3740 level->score[SC_EMERALD] = cav->emerald_score;
3741 level->score[SC_DIAMOND] = cav->diamond_score;
3742 level->score[SC_ROBOT] = cav->alien_score;
3743 level->score[SC_SPACESHIP] = cav->tank_score;
3744 level->score[SC_BUG] = cav->bug_score;
3745 level->score[SC_YAMYAM] = cav->eater_score;
3746 level->score[SC_NUT] = cav->nut_score;
3747 level->score[SC_DYNAMITE] = cav->dynamite_score;
3748 level->score[SC_KEY] = cav->key_score;
3749 level->score[SC_TIME_BONUS] = cav->exit_score;
3751 level->num_yamyam_contents = cav->num_eater_arrays;
3753 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3754 for (y = 0; y < 3; y++)
3755 for (x = 0; x < 3; x++)
3756 level->yamyam_content[i].e[x][y] =
3757 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
3759 level->amoeba_speed = cav->amoeba_time;
3760 level->time_magic_wall = cav->wonderwall_time;
3761 level->time_wheel = cav->wheel_time;
3763 level->android_move_time = cav->android_move_time;
3764 level->android_clone_time = cav->android_clone_time;
3765 level->ball_random = cav->ball_random;
3766 level->ball_active_initial = cav->ball_active;
3767 level->ball_time = cav->ball_time;
3768 level->num_ball_contents = cav->num_ball_arrays;
3770 level->lenses_score = cav->lenses_score;
3771 level->magnify_score = cav->magnify_score;
3772 level->slurp_score = cav->slurp_score;
3774 level->lenses_time = cav->lenses_time;
3775 level->magnify_time = cav->magnify_time;
3777 level->wind_direction_initial =
3778 map_direction_EM_to_RND(cav->wind_direction);
3780 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3781 for (j = 0; j < 8; j++)
3782 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3783 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
3785 map_android_clone_elements_EM_to_RND(level);
3787 // convert the playfield (some elements need special treatment)
3788 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3790 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
3792 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3793 new_element = EL_AMOEBA_DEAD;
3795 level->field[x][y] = new_element;
3798 for (i = 0; i < MAX_PLAYERS; i++)
3800 // in case of all players set to the same field, use the first player
3801 int nr = MAX_PLAYERS - i - 1;
3802 int jx = cav->player_x[nr];
3803 int jy = cav->player_y[nr];
3805 if (jx != -1 && jy != -1)
3806 level->field[jx][jy] = EL_PLAYER_1 + nr;
3809 // time score is counted for each 10 seconds left in Emerald Mine levels
3810 level->time_score_base = 10;
3814 // ----------------------------------------------------------------------------
3815 // functions for loading SP level
3816 // ----------------------------------------------------------------------------
3818 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3820 struct LevelInfo_SP *level_sp = level->native_sp_level;
3821 LevelInfoType *header = &level_sp->header;
3824 level_sp->width = level->fieldx;
3825 level_sp->height = level->fieldy;
3827 for (x = 0; x < level->fieldx; x++)
3828 for (y = 0; y < level->fieldy; y++)
3829 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3831 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3833 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3834 header->LevelTitle[i] = level->name[i];
3835 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
3837 header->InfotronsNeeded = level->gems_needed;
3839 header->SpecialPortCount = 0;
3841 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3843 boolean gravity_port_found = FALSE;
3844 boolean gravity_port_valid = FALSE;
3845 int gravity_port_flag;
3846 int gravity_port_base_element;
3847 int element = level->field[x][y];
3849 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3850 element <= EL_SP_GRAVITY_ON_PORT_UP)
3852 gravity_port_found = TRUE;
3853 gravity_port_valid = TRUE;
3854 gravity_port_flag = 1;
3855 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3857 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3858 element <= EL_SP_GRAVITY_OFF_PORT_UP)
3860 gravity_port_found = TRUE;
3861 gravity_port_valid = TRUE;
3862 gravity_port_flag = 0;
3863 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3865 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3866 element <= EL_SP_GRAVITY_PORT_UP)
3868 // change R'n'D style gravity inverting special port to normal port
3869 // (there are no gravity inverting ports in native Supaplex engine)
3871 gravity_port_found = TRUE;
3872 gravity_port_valid = FALSE;
3873 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3876 if (gravity_port_found)
3878 if (gravity_port_valid &&
3879 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3881 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3883 port->PortLocation = (y * level->fieldx + x) * 2;
3884 port->Gravity = gravity_port_flag;
3886 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3888 header->SpecialPortCount++;
3892 // change special gravity port to normal port
3894 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3897 level_sp->playfield[x][y] = element - EL_SP_START;
3902 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3904 struct LevelInfo_SP *level_sp = level->native_sp_level;
3905 LevelInfoType *header = &level_sp->header;
3906 boolean num_invalid_elements = 0;
3909 level->fieldx = level_sp->width;
3910 level->fieldy = level_sp->height;
3912 for (x = 0; x < level->fieldx; x++)
3914 for (y = 0; y < level->fieldy; y++)
3916 int element_old = level_sp->playfield[x][y];
3917 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3919 if (element_new == EL_UNKNOWN)
3921 num_invalid_elements++;
3923 Debug("level:native:SP", "invalid element %d at position %d, %d",
3927 level->field[x][y] = element_new;
3931 if (num_invalid_elements > 0)
3932 Warn("found %d invalid elements%s", num_invalid_elements,
3933 (!options.debug ? " (use '--debug' for more details)" : ""));
3935 for (i = 0; i < MAX_PLAYERS; i++)
3936 level->initial_player_gravity[i] =
3937 (header->InitialGravity == 1 ? TRUE : FALSE);
3939 // skip leading spaces
3940 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3941 if (header->LevelTitle[i] != ' ')
3945 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3946 level->name[j] = header->LevelTitle[i];
3947 level->name[j] = '\0';
3949 // cut trailing spaces
3951 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3952 level->name[j - 1] = '\0';
3954 level->gems_needed = header->InfotronsNeeded;
3956 for (i = 0; i < header->SpecialPortCount; i++)
3958 SpecialPortType *port = &header->SpecialPort[i];
3959 int port_location = port->PortLocation;
3960 int gravity = port->Gravity;
3961 int port_x, port_y, port_element;
3963 port_x = (port_location / 2) % level->fieldx;
3964 port_y = (port_location / 2) / level->fieldx;
3966 if (port_x < 0 || port_x >= level->fieldx ||
3967 port_y < 0 || port_y >= level->fieldy)
3969 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
3974 port_element = level->field[port_x][port_y];
3976 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3977 port_element > EL_SP_GRAVITY_PORT_UP)
3979 Warn("no special port at position (%d, %d)", port_x, port_y);
3984 // change previous (wrong) gravity inverting special port to either
3985 // gravity enabling special port or gravity disabling special port
3986 level->field[port_x][port_y] +=
3987 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3988 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3991 // change special gravity ports without database entries to normal ports
3992 for (x = 0; x < level->fieldx; x++)
3993 for (y = 0; y < level->fieldy; y++)
3994 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3995 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3996 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3998 level->time = 0; // no time limit
3999 level->amoeba_speed = 0;
4000 level->time_magic_wall = 0;
4001 level->time_wheel = 0;
4002 level->amoeba_content = EL_EMPTY;
4004 // original Supaplex does not use score values -- rate by playing time
4005 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4006 level->score[i] = 0;
4008 level->rate_time_over_score = TRUE;
4010 // there are no yamyams in supaplex levels
4011 for (i = 0; i < level->num_yamyam_contents; i++)
4012 for (x = 0; x < 3; x++)
4013 for (y = 0; y < 3; y++)
4014 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4017 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4019 struct LevelInfo_SP *level_sp = level->native_sp_level;
4020 struct DemoInfo_SP *demo = &level_sp->demo;
4023 // always start with reliable default values
4024 demo->is_available = FALSE;
4027 if (TAPE_IS_EMPTY(tape))
4030 demo->level_nr = tape.level_nr; // (currently not used)
4032 level_sp->header.DemoRandomSeed = tape.random_seed;
4036 for (i = 0; i < tape.length; i++)
4038 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4039 int demo_repeat = tape.pos[i].delay;
4040 int demo_entries = (demo_repeat + 15) / 16;
4042 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4044 Warn("tape truncated: size exceeds maximum SP demo size %d",
4050 for (j = 0; j < demo_repeat / 16; j++)
4051 demo->data[demo->length++] = 0xf0 | demo_action;
4053 if (demo_repeat % 16)
4054 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4057 demo->is_available = TRUE;
4060 static void setTapeInfoToDefaults(void);
4062 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4064 struct LevelInfo_SP *level_sp = level->native_sp_level;
4065 struct DemoInfo_SP *demo = &level_sp->demo;
4066 char *filename = level->file_info.filename;
4069 // always start with reliable default values
4070 setTapeInfoToDefaults();
4072 if (!demo->is_available)
4075 tape.level_nr = demo->level_nr; // (currently not used)
4076 tape.random_seed = level_sp->header.DemoRandomSeed;
4078 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4081 tape.pos[tape.counter].delay = 0;
4083 for (i = 0; i < demo->length; i++)
4085 int demo_action = demo->data[i] & 0x0f;
4086 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4087 int tape_action = map_key_SP_to_RND(demo_action);
4088 int tape_repeat = demo_repeat + 1;
4089 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4090 boolean success = 0;
4093 for (j = 0; j < tape_repeat; j++)
4094 success = TapeAddAction(action);
4098 Warn("SP demo truncated: size exceeds maximum tape size %d",
4105 TapeHaltRecording();
4109 // ----------------------------------------------------------------------------
4110 // functions for loading MM level
4111 // ----------------------------------------------------------------------------
4113 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4115 struct LevelInfo_MM *level_mm = level->native_mm_level;
4118 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4119 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4121 level_mm->time = level->time;
4122 level_mm->kettles_needed = level->gems_needed;
4123 level_mm->auto_count_kettles = level->auto_count_gems;
4125 level_mm->laser_red = level->mm_laser_red;
4126 level_mm->laser_green = level->mm_laser_green;
4127 level_mm->laser_blue = level->mm_laser_blue;
4129 strcpy(level_mm->name, level->name);
4130 strcpy(level_mm->author, level->author);
4132 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4133 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4134 level_mm->score[SC_KEY] = level->score[SC_KEY];
4135 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4136 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4138 level_mm->amoeba_speed = level->amoeba_speed;
4139 level_mm->time_fuse = level->mm_time_fuse;
4140 level_mm->time_bomb = level->mm_time_bomb;
4141 level_mm->time_ball = level->mm_time_ball;
4142 level_mm->time_block = level->mm_time_block;
4144 for (x = 0; x < level->fieldx; x++)
4145 for (y = 0; y < level->fieldy; y++)
4147 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4150 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4152 struct LevelInfo_MM *level_mm = level->native_mm_level;
4155 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4156 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4158 level->time = level_mm->time;
4159 level->gems_needed = level_mm->kettles_needed;
4160 level->auto_count_gems = level_mm->auto_count_kettles;
4162 level->mm_laser_red = level_mm->laser_red;
4163 level->mm_laser_green = level_mm->laser_green;
4164 level->mm_laser_blue = level_mm->laser_blue;
4166 strcpy(level->name, level_mm->name);
4168 // only overwrite author from 'levelinfo.conf' if author defined in level
4169 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4170 strcpy(level->author, level_mm->author);
4172 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4173 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4174 level->score[SC_KEY] = level_mm->score[SC_KEY];
4175 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4176 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4178 level->amoeba_speed = level_mm->amoeba_speed;
4179 level->mm_time_fuse = level_mm->time_fuse;
4180 level->mm_time_bomb = level_mm->time_bomb;
4181 level->mm_time_ball = level_mm->time_ball;
4182 level->mm_time_block = level_mm->time_block;
4184 for (x = 0; x < level->fieldx; x++)
4185 for (y = 0; y < level->fieldy; y++)
4186 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4190 // ----------------------------------------------------------------------------
4191 // functions for loading DC level
4192 // ----------------------------------------------------------------------------
4194 #define DC_LEVEL_HEADER_SIZE 344
4196 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4199 static int last_data_encoded;
4203 int diff_hi, diff_lo;
4204 int data_hi, data_lo;
4205 unsigned short data_decoded;
4209 last_data_encoded = 0;
4216 diff = data_encoded - last_data_encoded;
4217 diff_hi = diff & ~0xff;
4218 diff_lo = diff & 0xff;
4222 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4223 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4224 data_hi = data_hi & 0xff00;
4226 data_decoded = data_hi | data_lo;
4228 last_data_encoded = data_encoded;
4230 offset1 = (offset1 + 1) % 31;
4231 offset2 = offset2 & 0xff;
4233 return data_decoded;
4236 static int getMappedElement_DC(int element)
4244 // 0x0117 - 0x036e: (?)
4247 // 0x042d - 0x0684: (?)
4263 element = EL_CRYSTAL;
4266 case 0x0e77: // quicksand (boulder)
4267 element = EL_QUICKSAND_FAST_FULL;
4270 case 0x0e99: // slow quicksand (boulder)
4271 element = EL_QUICKSAND_FULL;
4275 element = EL_EM_EXIT_OPEN;
4279 element = EL_EM_EXIT_CLOSED;
4283 element = EL_EM_STEEL_EXIT_OPEN;
4287 element = EL_EM_STEEL_EXIT_CLOSED;
4290 case 0x0f4f: // dynamite (lit 1)
4291 element = EL_EM_DYNAMITE_ACTIVE;
4294 case 0x0f57: // dynamite (lit 2)
4295 element = EL_EM_DYNAMITE_ACTIVE;
4298 case 0x0f5f: // dynamite (lit 3)
4299 element = EL_EM_DYNAMITE_ACTIVE;
4302 case 0x0f67: // dynamite (lit 4)
4303 element = EL_EM_DYNAMITE_ACTIVE;
4310 element = EL_AMOEBA_WET;
4314 element = EL_AMOEBA_DROP;
4318 element = EL_DC_MAGIC_WALL;
4322 element = EL_SPACESHIP_UP;
4326 element = EL_SPACESHIP_DOWN;
4330 element = EL_SPACESHIP_LEFT;
4334 element = EL_SPACESHIP_RIGHT;
4338 element = EL_BUG_UP;
4342 element = EL_BUG_DOWN;
4346 element = EL_BUG_LEFT;
4350 element = EL_BUG_RIGHT;
4354 element = EL_MOLE_UP;
4358 element = EL_MOLE_DOWN;
4362 element = EL_MOLE_LEFT;
4366 element = EL_MOLE_RIGHT;
4374 element = EL_YAMYAM_UP;
4378 element = EL_SWITCHGATE_OPEN;
4382 element = EL_SWITCHGATE_CLOSED;
4386 element = EL_DC_SWITCHGATE_SWITCH_UP;
4390 element = EL_TIMEGATE_CLOSED;
4393 case 0x144c: // conveyor belt switch (green)
4394 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4397 case 0x144f: // conveyor belt switch (red)
4398 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4401 case 0x1452: // conveyor belt switch (blue)
4402 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4406 element = EL_CONVEYOR_BELT_3_MIDDLE;
4410 element = EL_CONVEYOR_BELT_3_LEFT;
4414 element = EL_CONVEYOR_BELT_3_RIGHT;
4418 element = EL_CONVEYOR_BELT_1_MIDDLE;
4422 element = EL_CONVEYOR_BELT_1_LEFT;
4426 element = EL_CONVEYOR_BELT_1_RIGHT;
4430 element = EL_CONVEYOR_BELT_4_MIDDLE;
4434 element = EL_CONVEYOR_BELT_4_LEFT;
4438 element = EL_CONVEYOR_BELT_4_RIGHT;
4442 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4446 element = EL_EXPANDABLE_WALL_VERTICAL;
4450 element = EL_EXPANDABLE_WALL_ANY;
4453 case 0x14ce: // growing steel wall (left/right)
4454 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4457 case 0x14df: // growing steel wall (up/down)
4458 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4461 case 0x14e8: // growing steel wall (up/down/left/right)
4462 element = EL_EXPANDABLE_STEELWALL_ANY;
4466 element = EL_SHIELD_DEADLY;
4470 element = EL_EXTRA_TIME;
4478 element = EL_EMPTY_SPACE;
4481 case 0x1578: // quicksand (empty)
4482 element = EL_QUICKSAND_FAST_EMPTY;
4485 case 0x1579: // slow quicksand (empty)
4486 element = EL_QUICKSAND_EMPTY;
4496 element = EL_EM_DYNAMITE;
4499 case 0x15a1: // key (red)
4500 element = EL_EM_KEY_1;
4503 case 0x15a2: // key (yellow)
4504 element = EL_EM_KEY_2;
4507 case 0x15a3: // key (blue)
4508 element = EL_EM_KEY_4;
4511 case 0x15a4: // key (green)
4512 element = EL_EM_KEY_3;
4515 case 0x15a5: // key (white)
4516 element = EL_DC_KEY_WHITE;
4520 element = EL_WALL_SLIPPERY;
4527 case 0x15a8: // wall (not round)
4531 case 0x15a9: // (blue)
4532 element = EL_CHAR_A;
4535 case 0x15aa: // (blue)
4536 element = EL_CHAR_B;
4539 case 0x15ab: // (blue)
4540 element = EL_CHAR_C;
4543 case 0x15ac: // (blue)
4544 element = EL_CHAR_D;
4547 case 0x15ad: // (blue)
4548 element = EL_CHAR_E;
4551 case 0x15ae: // (blue)
4552 element = EL_CHAR_F;
4555 case 0x15af: // (blue)
4556 element = EL_CHAR_G;
4559 case 0x15b0: // (blue)
4560 element = EL_CHAR_H;
4563 case 0x15b1: // (blue)
4564 element = EL_CHAR_I;
4567 case 0x15b2: // (blue)
4568 element = EL_CHAR_J;
4571 case 0x15b3: // (blue)
4572 element = EL_CHAR_K;
4575 case 0x15b4: // (blue)
4576 element = EL_CHAR_L;
4579 case 0x15b5: // (blue)
4580 element = EL_CHAR_M;
4583 case 0x15b6: // (blue)
4584 element = EL_CHAR_N;
4587 case 0x15b7: // (blue)
4588 element = EL_CHAR_O;
4591 case 0x15b8: // (blue)
4592 element = EL_CHAR_P;
4595 case 0x15b9: // (blue)
4596 element = EL_CHAR_Q;
4599 case 0x15ba: // (blue)
4600 element = EL_CHAR_R;
4603 case 0x15bb: // (blue)
4604 element = EL_CHAR_S;
4607 case 0x15bc: // (blue)
4608 element = EL_CHAR_T;
4611 case 0x15bd: // (blue)
4612 element = EL_CHAR_U;
4615 case 0x15be: // (blue)
4616 element = EL_CHAR_V;
4619 case 0x15bf: // (blue)
4620 element = EL_CHAR_W;
4623 case 0x15c0: // (blue)
4624 element = EL_CHAR_X;
4627 case 0x15c1: // (blue)
4628 element = EL_CHAR_Y;
4631 case 0x15c2: // (blue)
4632 element = EL_CHAR_Z;
4635 case 0x15c3: // (blue)
4636 element = EL_CHAR_AUMLAUT;
4639 case 0x15c4: // (blue)
4640 element = EL_CHAR_OUMLAUT;
4643 case 0x15c5: // (blue)
4644 element = EL_CHAR_UUMLAUT;
4647 case 0x15c6: // (blue)
4648 element = EL_CHAR_0;
4651 case 0x15c7: // (blue)
4652 element = EL_CHAR_1;
4655 case 0x15c8: // (blue)
4656 element = EL_CHAR_2;
4659 case 0x15c9: // (blue)
4660 element = EL_CHAR_3;
4663 case 0x15ca: // (blue)
4664 element = EL_CHAR_4;
4667 case 0x15cb: // (blue)
4668 element = EL_CHAR_5;
4671 case 0x15cc: // (blue)
4672 element = EL_CHAR_6;
4675 case 0x15cd: // (blue)
4676 element = EL_CHAR_7;
4679 case 0x15ce: // (blue)
4680 element = EL_CHAR_8;
4683 case 0x15cf: // (blue)
4684 element = EL_CHAR_9;
4687 case 0x15d0: // (blue)
4688 element = EL_CHAR_PERIOD;
4691 case 0x15d1: // (blue)
4692 element = EL_CHAR_EXCLAM;
4695 case 0x15d2: // (blue)
4696 element = EL_CHAR_COLON;
4699 case 0x15d3: // (blue)
4700 element = EL_CHAR_LESS;
4703 case 0x15d4: // (blue)
4704 element = EL_CHAR_GREATER;
4707 case 0x15d5: // (blue)
4708 element = EL_CHAR_QUESTION;
4711 case 0x15d6: // (blue)
4712 element = EL_CHAR_COPYRIGHT;
4715 case 0x15d7: // (blue)
4716 element = EL_CHAR_UP;
4719 case 0x15d8: // (blue)
4720 element = EL_CHAR_DOWN;
4723 case 0x15d9: // (blue)
4724 element = EL_CHAR_BUTTON;
4727 case 0x15da: // (blue)
4728 element = EL_CHAR_PLUS;
4731 case 0x15db: // (blue)
4732 element = EL_CHAR_MINUS;
4735 case 0x15dc: // (blue)
4736 element = EL_CHAR_APOSTROPHE;
4739 case 0x15dd: // (blue)
4740 element = EL_CHAR_PARENLEFT;
4743 case 0x15de: // (blue)
4744 element = EL_CHAR_PARENRIGHT;
4747 case 0x15df: // (green)
4748 element = EL_CHAR_A;
4751 case 0x15e0: // (green)
4752 element = EL_CHAR_B;
4755 case 0x15e1: // (green)
4756 element = EL_CHAR_C;
4759 case 0x15e2: // (green)
4760 element = EL_CHAR_D;
4763 case 0x15e3: // (green)
4764 element = EL_CHAR_E;
4767 case 0x15e4: // (green)
4768 element = EL_CHAR_F;
4771 case 0x15e5: // (green)
4772 element = EL_CHAR_G;
4775 case 0x15e6: // (green)
4776 element = EL_CHAR_H;
4779 case 0x15e7: // (green)
4780 element = EL_CHAR_I;
4783 case 0x15e8: // (green)
4784 element = EL_CHAR_J;
4787 case 0x15e9: // (green)
4788 element = EL_CHAR_K;
4791 case 0x15ea: // (green)
4792 element = EL_CHAR_L;
4795 case 0x15eb: // (green)
4796 element = EL_CHAR_M;
4799 case 0x15ec: // (green)
4800 element = EL_CHAR_N;
4803 case 0x15ed: // (green)
4804 element = EL_CHAR_O;
4807 case 0x15ee: // (green)
4808 element = EL_CHAR_P;
4811 case 0x15ef: // (green)
4812 element = EL_CHAR_Q;
4815 case 0x15f0: // (green)
4816 element = EL_CHAR_R;
4819 case 0x15f1: // (green)
4820 element = EL_CHAR_S;
4823 case 0x15f2: // (green)
4824 element = EL_CHAR_T;
4827 case 0x15f3: // (green)
4828 element = EL_CHAR_U;
4831 case 0x15f4: // (green)
4832 element = EL_CHAR_V;
4835 case 0x15f5: // (green)
4836 element = EL_CHAR_W;
4839 case 0x15f6: // (green)
4840 element = EL_CHAR_X;
4843 case 0x15f7: // (green)
4844 element = EL_CHAR_Y;
4847 case 0x15f8: // (green)
4848 element = EL_CHAR_Z;
4851 case 0x15f9: // (green)
4852 element = EL_CHAR_AUMLAUT;
4855 case 0x15fa: // (green)
4856 element = EL_CHAR_OUMLAUT;
4859 case 0x15fb: // (green)
4860 element = EL_CHAR_UUMLAUT;
4863 case 0x15fc: // (green)
4864 element = EL_CHAR_0;
4867 case 0x15fd: // (green)
4868 element = EL_CHAR_1;
4871 case 0x15fe: // (green)
4872 element = EL_CHAR_2;
4875 case 0x15ff: // (green)
4876 element = EL_CHAR_3;
4879 case 0x1600: // (green)
4880 element = EL_CHAR_4;
4883 case 0x1601: // (green)
4884 element = EL_CHAR_5;
4887 case 0x1602: // (green)
4888 element = EL_CHAR_6;
4891 case 0x1603: // (green)
4892 element = EL_CHAR_7;
4895 case 0x1604: // (green)
4896 element = EL_CHAR_8;
4899 case 0x1605: // (green)
4900 element = EL_CHAR_9;
4903 case 0x1606: // (green)
4904 element = EL_CHAR_PERIOD;
4907 case 0x1607: // (green)
4908 element = EL_CHAR_EXCLAM;
4911 case 0x1608: // (green)
4912 element = EL_CHAR_COLON;
4915 case 0x1609: // (green)
4916 element = EL_CHAR_LESS;
4919 case 0x160a: // (green)
4920 element = EL_CHAR_GREATER;
4923 case 0x160b: // (green)
4924 element = EL_CHAR_QUESTION;
4927 case 0x160c: // (green)
4928 element = EL_CHAR_COPYRIGHT;
4931 case 0x160d: // (green)
4932 element = EL_CHAR_UP;
4935 case 0x160e: // (green)
4936 element = EL_CHAR_DOWN;
4939 case 0x160f: // (green)
4940 element = EL_CHAR_BUTTON;
4943 case 0x1610: // (green)
4944 element = EL_CHAR_PLUS;
4947 case 0x1611: // (green)
4948 element = EL_CHAR_MINUS;
4951 case 0x1612: // (green)
4952 element = EL_CHAR_APOSTROPHE;
4955 case 0x1613: // (green)
4956 element = EL_CHAR_PARENLEFT;
4959 case 0x1614: // (green)
4960 element = EL_CHAR_PARENRIGHT;
4963 case 0x1615: // (blue steel)
4964 element = EL_STEEL_CHAR_A;
4967 case 0x1616: // (blue steel)
4968 element = EL_STEEL_CHAR_B;
4971 case 0x1617: // (blue steel)
4972 element = EL_STEEL_CHAR_C;
4975 case 0x1618: // (blue steel)
4976 element = EL_STEEL_CHAR_D;
4979 case 0x1619: // (blue steel)
4980 element = EL_STEEL_CHAR_E;
4983 case 0x161a: // (blue steel)
4984 element = EL_STEEL_CHAR_F;
4987 case 0x161b: // (blue steel)
4988 element = EL_STEEL_CHAR_G;
4991 case 0x161c: // (blue steel)
4992 element = EL_STEEL_CHAR_H;
4995 case 0x161d: // (blue steel)
4996 element = EL_STEEL_CHAR_I;
4999 case 0x161e: // (blue steel)
5000 element = EL_STEEL_CHAR_J;
5003 case 0x161f: // (blue steel)
5004 element = EL_STEEL_CHAR_K;
5007 case 0x1620: // (blue steel)
5008 element = EL_STEEL_CHAR_L;
5011 case 0x1621: // (blue steel)
5012 element = EL_STEEL_CHAR_M;
5015 case 0x1622: // (blue steel)
5016 element = EL_STEEL_CHAR_N;
5019 case 0x1623: // (blue steel)
5020 element = EL_STEEL_CHAR_O;
5023 case 0x1624: // (blue steel)
5024 element = EL_STEEL_CHAR_P;
5027 case 0x1625: // (blue steel)
5028 element = EL_STEEL_CHAR_Q;
5031 case 0x1626: // (blue steel)
5032 element = EL_STEEL_CHAR_R;
5035 case 0x1627: // (blue steel)
5036 element = EL_STEEL_CHAR_S;
5039 case 0x1628: // (blue steel)
5040 element = EL_STEEL_CHAR_T;
5043 case 0x1629: // (blue steel)
5044 element = EL_STEEL_CHAR_U;
5047 case 0x162a: // (blue steel)
5048 element = EL_STEEL_CHAR_V;
5051 case 0x162b: // (blue steel)
5052 element = EL_STEEL_CHAR_W;
5055 case 0x162c: // (blue steel)
5056 element = EL_STEEL_CHAR_X;
5059 case 0x162d: // (blue steel)
5060 element = EL_STEEL_CHAR_Y;
5063 case 0x162e: // (blue steel)
5064 element = EL_STEEL_CHAR_Z;
5067 case 0x162f: // (blue steel)
5068 element = EL_STEEL_CHAR_AUMLAUT;
5071 case 0x1630: // (blue steel)
5072 element = EL_STEEL_CHAR_OUMLAUT;
5075 case 0x1631: // (blue steel)
5076 element = EL_STEEL_CHAR_UUMLAUT;
5079 case 0x1632: // (blue steel)
5080 element = EL_STEEL_CHAR_0;
5083 case 0x1633: // (blue steel)
5084 element = EL_STEEL_CHAR_1;
5087 case 0x1634: // (blue steel)
5088 element = EL_STEEL_CHAR_2;
5091 case 0x1635: // (blue steel)
5092 element = EL_STEEL_CHAR_3;
5095 case 0x1636: // (blue steel)
5096 element = EL_STEEL_CHAR_4;
5099 case 0x1637: // (blue steel)
5100 element = EL_STEEL_CHAR_5;
5103 case 0x1638: // (blue steel)
5104 element = EL_STEEL_CHAR_6;
5107 case 0x1639: // (blue steel)
5108 element = EL_STEEL_CHAR_7;
5111 case 0x163a: // (blue steel)
5112 element = EL_STEEL_CHAR_8;
5115 case 0x163b: // (blue steel)
5116 element = EL_STEEL_CHAR_9;
5119 case 0x163c: // (blue steel)
5120 element = EL_STEEL_CHAR_PERIOD;
5123 case 0x163d: // (blue steel)
5124 element = EL_STEEL_CHAR_EXCLAM;
5127 case 0x163e: // (blue steel)
5128 element = EL_STEEL_CHAR_COLON;
5131 case 0x163f: // (blue steel)
5132 element = EL_STEEL_CHAR_LESS;
5135 case 0x1640: // (blue steel)
5136 element = EL_STEEL_CHAR_GREATER;
5139 case 0x1641: // (blue steel)
5140 element = EL_STEEL_CHAR_QUESTION;
5143 case 0x1642: // (blue steel)
5144 element = EL_STEEL_CHAR_COPYRIGHT;
5147 case 0x1643: // (blue steel)
5148 element = EL_STEEL_CHAR_UP;
5151 case 0x1644: // (blue steel)
5152 element = EL_STEEL_CHAR_DOWN;
5155 case 0x1645: // (blue steel)
5156 element = EL_STEEL_CHAR_BUTTON;
5159 case 0x1646: // (blue steel)
5160 element = EL_STEEL_CHAR_PLUS;
5163 case 0x1647: // (blue steel)
5164 element = EL_STEEL_CHAR_MINUS;
5167 case 0x1648: // (blue steel)
5168 element = EL_STEEL_CHAR_APOSTROPHE;
5171 case 0x1649: // (blue steel)
5172 element = EL_STEEL_CHAR_PARENLEFT;
5175 case 0x164a: // (blue steel)
5176 element = EL_STEEL_CHAR_PARENRIGHT;
5179 case 0x164b: // (green steel)
5180 element = EL_STEEL_CHAR_A;
5183 case 0x164c: // (green steel)
5184 element = EL_STEEL_CHAR_B;
5187 case 0x164d: // (green steel)
5188 element = EL_STEEL_CHAR_C;
5191 case 0x164e: // (green steel)
5192 element = EL_STEEL_CHAR_D;
5195 case 0x164f: // (green steel)
5196 element = EL_STEEL_CHAR_E;
5199 case 0x1650: // (green steel)
5200 element = EL_STEEL_CHAR_F;
5203 case 0x1651: // (green steel)
5204 element = EL_STEEL_CHAR_G;
5207 case 0x1652: // (green steel)
5208 element = EL_STEEL_CHAR_H;
5211 case 0x1653: // (green steel)
5212 element = EL_STEEL_CHAR_I;
5215 case 0x1654: // (green steel)
5216 element = EL_STEEL_CHAR_J;
5219 case 0x1655: // (green steel)
5220 element = EL_STEEL_CHAR_K;
5223 case 0x1656: // (green steel)
5224 element = EL_STEEL_CHAR_L;
5227 case 0x1657: // (green steel)
5228 element = EL_STEEL_CHAR_M;
5231 case 0x1658: // (green steel)
5232 element = EL_STEEL_CHAR_N;
5235 case 0x1659: // (green steel)
5236 element = EL_STEEL_CHAR_O;
5239 case 0x165a: // (green steel)
5240 element = EL_STEEL_CHAR_P;
5243 case 0x165b: // (green steel)
5244 element = EL_STEEL_CHAR_Q;
5247 case 0x165c: // (green steel)
5248 element = EL_STEEL_CHAR_R;
5251 case 0x165d: // (green steel)
5252 element = EL_STEEL_CHAR_S;
5255 case 0x165e: // (green steel)
5256 element = EL_STEEL_CHAR_T;
5259 case 0x165f: // (green steel)
5260 element = EL_STEEL_CHAR_U;
5263 case 0x1660: // (green steel)
5264 element = EL_STEEL_CHAR_V;
5267 case 0x1661: // (green steel)
5268 element = EL_STEEL_CHAR_W;
5271 case 0x1662: // (green steel)
5272 element = EL_STEEL_CHAR_X;
5275 case 0x1663: // (green steel)
5276 element = EL_STEEL_CHAR_Y;
5279 case 0x1664: // (green steel)
5280 element = EL_STEEL_CHAR_Z;
5283 case 0x1665: // (green steel)
5284 element = EL_STEEL_CHAR_AUMLAUT;
5287 case 0x1666: // (green steel)
5288 element = EL_STEEL_CHAR_OUMLAUT;
5291 case 0x1667: // (green steel)
5292 element = EL_STEEL_CHAR_UUMLAUT;
5295 case 0x1668: // (green steel)
5296 element = EL_STEEL_CHAR_0;
5299 case 0x1669: // (green steel)
5300 element = EL_STEEL_CHAR_1;
5303 case 0x166a: // (green steel)
5304 element = EL_STEEL_CHAR_2;
5307 case 0x166b: // (green steel)
5308 element = EL_STEEL_CHAR_3;
5311 case 0x166c: // (green steel)
5312 element = EL_STEEL_CHAR_4;
5315 case 0x166d: // (green steel)
5316 element = EL_STEEL_CHAR_5;
5319 case 0x166e: // (green steel)
5320 element = EL_STEEL_CHAR_6;
5323 case 0x166f: // (green steel)
5324 element = EL_STEEL_CHAR_7;
5327 case 0x1670: // (green steel)
5328 element = EL_STEEL_CHAR_8;
5331 case 0x1671: // (green steel)
5332 element = EL_STEEL_CHAR_9;
5335 case 0x1672: // (green steel)
5336 element = EL_STEEL_CHAR_PERIOD;
5339 case 0x1673: // (green steel)
5340 element = EL_STEEL_CHAR_EXCLAM;
5343 case 0x1674: // (green steel)
5344 element = EL_STEEL_CHAR_COLON;
5347 case 0x1675: // (green steel)
5348 element = EL_STEEL_CHAR_LESS;
5351 case 0x1676: // (green steel)
5352 element = EL_STEEL_CHAR_GREATER;
5355 case 0x1677: // (green steel)
5356 element = EL_STEEL_CHAR_QUESTION;
5359 case 0x1678: // (green steel)
5360 element = EL_STEEL_CHAR_COPYRIGHT;
5363 case 0x1679: // (green steel)
5364 element = EL_STEEL_CHAR_UP;
5367 case 0x167a: // (green steel)
5368 element = EL_STEEL_CHAR_DOWN;
5371 case 0x167b: // (green steel)
5372 element = EL_STEEL_CHAR_BUTTON;
5375 case 0x167c: // (green steel)
5376 element = EL_STEEL_CHAR_PLUS;
5379 case 0x167d: // (green steel)
5380 element = EL_STEEL_CHAR_MINUS;
5383 case 0x167e: // (green steel)
5384 element = EL_STEEL_CHAR_APOSTROPHE;
5387 case 0x167f: // (green steel)
5388 element = EL_STEEL_CHAR_PARENLEFT;
5391 case 0x1680: // (green steel)
5392 element = EL_STEEL_CHAR_PARENRIGHT;
5395 case 0x1681: // gate (red)
5396 element = EL_EM_GATE_1;
5399 case 0x1682: // secret gate (red)
5400 element = EL_EM_GATE_1_GRAY;
5403 case 0x1683: // gate (yellow)
5404 element = EL_EM_GATE_2;
5407 case 0x1684: // secret gate (yellow)
5408 element = EL_EM_GATE_2_GRAY;
5411 case 0x1685: // gate (blue)
5412 element = EL_EM_GATE_4;
5415 case 0x1686: // secret gate (blue)
5416 element = EL_EM_GATE_4_GRAY;
5419 case 0x1687: // gate (green)
5420 element = EL_EM_GATE_3;
5423 case 0x1688: // secret gate (green)
5424 element = EL_EM_GATE_3_GRAY;
5427 case 0x1689: // gate (white)
5428 element = EL_DC_GATE_WHITE;
5431 case 0x168a: // secret gate (white)
5432 element = EL_DC_GATE_WHITE_GRAY;
5435 case 0x168b: // secret gate (no key)
5436 element = EL_DC_GATE_FAKE_GRAY;
5440 element = EL_ROBOT_WHEEL;
5444 element = EL_DC_TIMEGATE_SWITCH;
5448 element = EL_ACID_POOL_BOTTOM;
5452 element = EL_ACID_POOL_TOPLEFT;
5456 element = EL_ACID_POOL_TOPRIGHT;
5460 element = EL_ACID_POOL_BOTTOMLEFT;
5464 element = EL_ACID_POOL_BOTTOMRIGHT;
5468 element = EL_STEELWALL;
5472 element = EL_STEELWALL_SLIPPERY;
5475 case 0x1695: // steel wall (not round)
5476 element = EL_STEELWALL;
5479 case 0x1696: // steel wall (left)
5480 element = EL_DC_STEELWALL_1_LEFT;
5483 case 0x1697: // steel wall (bottom)
5484 element = EL_DC_STEELWALL_1_BOTTOM;
5487 case 0x1698: // steel wall (right)
5488 element = EL_DC_STEELWALL_1_RIGHT;
5491 case 0x1699: // steel wall (top)
5492 element = EL_DC_STEELWALL_1_TOP;
5495 case 0x169a: // steel wall (left/bottom)
5496 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5499 case 0x169b: // steel wall (right/bottom)
5500 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5503 case 0x169c: // steel wall (right/top)
5504 element = EL_DC_STEELWALL_1_TOPRIGHT;
5507 case 0x169d: // steel wall (left/top)
5508 element = EL_DC_STEELWALL_1_TOPLEFT;
5511 case 0x169e: // steel wall (right/bottom small)
5512 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5515 case 0x169f: // steel wall (left/bottom small)
5516 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5519 case 0x16a0: // steel wall (right/top small)
5520 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5523 case 0x16a1: // steel wall (left/top small)
5524 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5527 case 0x16a2: // steel wall (left/right)
5528 element = EL_DC_STEELWALL_1_VERTICAL;
5531 case 0x16a3: // steel wall (top/bottom)
5532 element = EL_DC_STEELWALL_1_HORIZONTAL;
5535 case 0x16a4: // steel wall 2 (left end)
5536 element = EL_DC_STEELWALL_2_LEFT;
5539 case 0x16a5: // steel wall 2 (right end)
5540 element = EL_DC_STEELWALL_2_RIGHT;
5543 case 0x16a6: // steel wall 2 (top end)
5544 element = EL_DC_STEELWALL_2_TOP;
5547 case 0x16a7: // steel wall 2 (bottom end)
5548 element = EL_DC_STEELWALL_2_BOTTOM;
5551 case 0x16a8: // steel wall 2 (left/right)
5552 element = EL_DC_STEELWALL_2_HORIZONTAL;
5555 case 0x16a9: // steel wall 2 (up/down)
5556 element = EL_DC_STEELWALL_2_VERTICAL;
5559 case 0x16aa: // steel wall 2 (mid)
5560 element = EL_DC_STEELWALL_2_MIDDLE;
5564 element = EL_SIGN_EXCLAMATION;
5568 element = EL_SIGN_RADIOACTIVITY;
5572 element = EL_SIGN_STOP;
5576 element = EL_SIGN_WHEELCHAIR;
5580 element = EL_SIGN_PARKING;
5584 element = EL_SIGN_NO_ENTRY;
5588 element = EL_SIGN_HEART;
5592 element = EL_SIGN_GIVE_WAY;
5596 element = EL_SIGN_ENTRY_FORBIDDEN;
5600 element = EL_SIGN_EMERGENCY_EXIT;
5604 element = EL_SIGN_YIN_YANG;
5608 element = EL_WALL_EMERALD;
5612 element = EL_WALL_DIAMOND;
5616 element = EL_WALL_PEARL;
5620 element = EL_WALL_CRYSTAL;
5624 element = EL_INVISIBLE_WALL;
5628 element = EL_INVISIBLE_STEELWALL;
5632 // EL_INVISIBLE_SAND
5635 element = EL_LIGHT_SWITCH;
5639 element = EL_ENVELOPE_1;
5643 if (element >= 0x0117 && element <= 0x036e) // (?)
5644 element = EL_DIAMOND;
5645 else if (element >= 0x042d && element <= 0x0684) // (?)
5646 element = EL_EMERALD;
5647 else if (element >= 0x157c && element <= 0x158b)
5649 else if (element >= 0x1590 && element <= 0x159f)
5650 element = EL_DC_LANDMINE;
5651 else if (element >= 0x16bc && element <= 0x16cb)
5652 element = EL_INVISIBLE_SAND;
5655 Warn("unknown Diamond Caves element 0x%04x", element);
5657 element = EL_UNKNOWN;
5662 return getMappedElement(element);
5665 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5668 byte header[DC_LEVEL_HEADER_SIZE];
5670 int envelope_header_pos = 62;
5671 int envelope_content_pos = 94;
5672 int level_name_pos = 251;
5673 int level_author_pos = 292;
5674 int envelope_header_len;
5675 int envelope_content_len;
5677 int level_author_len;
5679 int num_yamyam_contents;
5682 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5684 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5686 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5688 header[i * 2 + 0] = header_word >> 8;
5689 header[i * 2 + 1] = header_word & 0xff;
5692 // read some values from level header to check level decoding integrity
5693 fieldx = header[6] | (header[7] << 8);
5694 fieldy = header[8] | (header[9] << 8);
5695 num_yamyam_contents = header[60] | (header[61] << 8);
5697 // do some simple sanity checks to ensure that level was correctly decoded
5698 if (fieldx < 1 || fieldx > 256 ||
5699 fieldy < 1 || fieldy > 256 ||
5700 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5702 level->no_valid_file = TRUE;
5704 Warn("cannot decode level from stream -- using empty level");
5709 // maximum envelope header size is 31 bytes
5710 envelope_header_len = header[envelope_header_pos];
5711 // maximum envelope content size is 110 (156?) bytes
5712 envelope_content_len = header[envelope_content_pos];
5714 // maximum level title size is 40 bytes
5715 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
5716 // maximum level author size is 30 (51?) bytes
5717 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5721 for (i = 0; i < envelope_header_len; i++)
5722 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5723 level->envelope[0].text[envelope_size++] =
5724 header[envelope_header_pos + 1 + i];
5726 if (envelope_header_len > 0 && envelope_content_len > 0)
5728 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5729 level->envelope[0].text[envelope_size++] = '\n';
5730 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5731 level->envelope[0].text[envelope_size++] = '\n';
5734 for (i = 0; i < envelope_content_len; i++)
5735 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5736 level->envelope[0].text[envelope_size++] =
5737 header[envelope_content_pos + 1 + i];
5739 level->envelope[0].text[envelope_size] = '\0';
5741 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5742 level->envelope[0].ysize = 10;
5743 level->envelope[0].autowrap = TRUE;
5744 level->envelope[0].centered = TRUE;
5746 for (i = 0; i < level_name_len; i++)
5747 level->name[i] = header[level_name_pos + 1 + i];
5748 level->name[level_name_len] = '\0';
5750 for (i = 0; i < level_author_len; i++)
5751 level->author[i] = header[level_author_pos + 1 + i];
5752 level->author[level_author_len] = '\0';
5754 num_yamyam_contents = header[60] | (header[61] << 8);
5755 level->num_yamyam_contents =
5756 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5758 for (i = 0; i < num_yamyam_contents; i++)
5760 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5762 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5763 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5765 if (i < MAX_ELEMENT_CONTENTS)
5766 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5770 fieldx = header[6] | (header[7] << 8);
5771 fieldy = header[8] | (header[9] << 8);
5772 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5773 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5775 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5777 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5778 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5780 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5781 level->field[x][y] = getMappedElement_DC(element_dc);
5784 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5785 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5786 level->field[x][y] = EL_PLAYER_1;
5788 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5789 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5790 level->field[x][y] = EL_PLAYER_2;
5792 level->gems_needed = header[18] | (header[19] << 8);
5794 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
5795 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
5796 level->score[SC_PEARL] = header[24] | (header[25] << 8);
5797 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
5798 level->score[SC_NUT] = header[28] | (header[29] << 8);
5799 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
5800 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
5801 level->score[SC_BUG] = header[34] | (header[35] << 8);
5802 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
5803 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
5804 level->score[SC_KEY] = header[40] | (header[41] << 8);
5805 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
5807 level->time = header[44] | (header[45] << 8);
5809 level->amoeba_speed = header[46] | (header[47] << 8);
5810 level->time_light = header[48] | (header[49] << 8);
5811 level->time_timegate = header[50] | (header[51] << 8);
5812 level->time_wheel = header[52] | (header[53] << 8);
5813 level->time_magic_wall = header[54] | (header[55] << 8);
5814 level->extra_time = header[56] | (header[57] << 8);
5815 level->shield_normal_time = header[58] | (header[59] << 8);
5817 // shield and extra time elements do not have a score
5818 level->score[SC_SHIELD] = 0;
5819 level->extra_time_score = 0;
5821 // set time for normal and deadly shields to the same value
5822 level->shield_deadly_time = level->shield_normal_time;
5824 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5825 // can slip down from flat walls, like normal walls and steel walls
5826 level->em_slippery_gems = TRUE;
5828 // time score is counted for each 10 seconds left in Diamond Caves levels
5829 level->time_score_base = 10;
5832 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5833 struct LevelFileInfo *level_file_info,
5834 boolean level_info_only)
5836 char *filename = level_file_info->filename;
5838 int num_magic_bytes = 8;
5839 char magic_bytes[num_magic_bytes + 1];
5840 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5842 if (!(file = openFile(filename, MODE_READ)))
5844 level->no_valid_file = TRUE;
5846 if (!level_info_only)
5847 Warn("cannot read level '%s' -- using empty level", filename);
5852 // fseek(file, 0x0000, SEEK_SET);
5854 if (level_file_info->packed)
5856 // read "magic bytes" from start of file
5857 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5858 magic_bytes[0] = '\0';
5860 // check "magic bytes" for correct file format
5861 if (!strPrefix(magic_bytes, "DC2"))
5863 level->no_valid_file = TRUE;
5865 Warn("unknown DC level file '%s' -- using empty level", filename);
5870 if (strPrefix(magic_bytes, "DC2Win95") ||
5871 strPrefix(magic_bytes, "DC2Win98"))
5873 int position_first_level = 0x00fa;
5874 int extra_bytes = 4;
5877 // advance file stream to first level inside the level package
5878 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5880 // each block of level data is followed by block of non-level data
5881 num_levels_to_skip *= 2;
5883 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
5884 while (num_levels_to_skip >= 0)
5886 // advance file stream to next level inside the level package
5887 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5889 level->no_valid_file = TRUE;
5891 Warn("cannot fseek in file '%s' -- using empty level", filename);
5896 // skip apparently unused extra bytes following each level
5897 ReadUnusedBytesFromFile(file, extra_bytes);
5899 // read size of next level in level package
5900 skip_bytes = getFile32BitLE(file);
5902 num_levels_to_skip--;
5907 level->no_valid_file = TRUE;
5909 Warn("unknown DC2 level file '%s' -- using empty level", filename);
5915 LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5921 // ----------------------------------------------------------------------------
5922 // functions for loading SB level
5923 // ----------------------------------------------------------------------------
5925 int getMappedElement_SB(int element_ascii, boolean use_ces)
5933 sb_element_mapping[] =
5935 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
5936 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
5937 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
5938 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
5939 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
5940 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
5941 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
5942 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
5949 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5950 if (element_ascii == sb_element_mapping[i].ascii)
5951 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5953 return EL_UNDEFINED;
5956 static void SetLevelSettings_SB(struct LevelInfo *level)
5960 level->use_step_counter = TRUE;
5963 level->score[SC_TIME_BONUS] = 0;
5964 level->time_score_base = 1;
5965 level->rate_time_over_score = TRUE;
5968 level->auto_exit_sokoban = TRUE;
5971 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5972 struct LevelFileInfo *level_file_info,
5973 boolean level_info_only)
5975 char *filename = level_file_info->filename;
5976 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5977 char last_comment[MAX_LINE_LEN];
5978 char level_name[MAX_LINE_LEN];
5981 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5982 boolean read_continued_line = FALSE;
5983 boolean reading_playfield = FALSE;
5984 boolean got_valid_playfield_line = FALSE;
5985 boolean invalid_playfield_char = FALSE;
5986 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5987 int file_level_nr = 0;
5989 int x = 0, y = 0; // initialized to make compilers happy
5991 last_comment[0] = '\0';
5992 level_name[0] = '\0';
5994 if (!(file = openFile(filename, MODE_READ)))
5996 level->no_valid_file = TRUE;
5998 if (!level_info_only)
5999 Warn("cannot read level '%s' -- using empty level", filename);
6004 while (!checkEndOfFile(file))
6006 // level successfully read, but next level may follow here
6007 if (!got_valid_playfield_line && reading_playfield)
6009 // read playfield from single level file -- skip remaining file
6010 if (!level_file_info->packed)
6013 if (file_level_nr >= num_levels_to_skip)
6018 last_comment[0] = '\0';
6019 level_name[0] = '\0';
6021 reading_playfield = FALSE;
6024 got_valid_playfield_line = FALSE;
6026 // read next line of input file
6027 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6030 // check if line was completely read and is terminated by line break
6031 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
6034 // cut trailing line break (this can be newline and/or carriage return)
6035 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6036 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6039 // copy raw input line for later use (mainly debugging output)
6040 strcpy(line_raw, line);
6042 if (read_continued_line)
6044 // append new line to existing line, if there is enough space
6045 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6046 strcat(previous_line, line_ptr);
6048 strcpy(line, previous_line); // copy storage buffer to line
6050 read_continued_line = FALSE;
6053 // if the last character is '\', continue at next line
6054 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6056 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6057 strcpy(previous_line, line); // copy line to storage buffer
6059 read_continued_line = TRUE;
6065 if (line[0] == '\0')
6068 // extract comment text from comment line
6071 for (line_ptr = line; *line_ptr; line_ptr++)
6072 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6075 strcpy(last_comment, line_ptr);
6080 // extract level title text from line containing level title
6081 if (line[0] == '\'')
6083 strcpy(level_name, &line[1]);
6085 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6086 level_name[strlen(level_name) - 1] = '\0';
6091 // skip lines containing only spaces (or empty lines)
6092 for (line_ptr = line; *line_ptr; line_ptr++)
6093 if (*line_ptr != ' ')
6095 if (*line_ptr == '\0')
6098 // at this point, we have found a line containing part of a playfield
6100 got_valid_playfield_line = TRUE;
6102 if (!reading_playfield)
6104 reading_playfield = TRUE;
6105 invalid_playfield_char = FALSE;
6107 for (x = 0; x < MAX_LEV_FIELDX; x++)
6108 for (y = 0; y < MAX_LEV_FIELDY; y++)
6109 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6114 // start with topmost tile row
6118 // skip playfield line if larger row than allowed
6119 if (y >= MAX_LEV_FIELDY)
6122 // start with leftmost tile column
6125 // read playfield elements from line
6126 for (line_ptr = line; *line_ptr; line_ptr++)
6128 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6130 // stop parsing playfield line if larger column than allowed
6131 if (x >= MAX_LEV_FIELDX)
6134 if (mapped_sb_element == EL_UNDEFINED)
6136 invalid_playfield_char = TRUE;
6141 level->field[x][y] = mapped_sb_element;
6143 // continue with next tile column
6146 level->fieldx = MAX(x, level->fieldx);
6149 if (invalid_playfield_char)
6151 // if first playfield line, treat invalid lines as comment lines
6153 reading_playfield = FALSE;
6158 // continue with next tile row
6166 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6167 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6169 if (!reading_playfield)
6171 level->no_valid_file = TRUE;
6173 Warn("cannot read level '%s' -- using empty level", filename);
6178 if (*level_name != '\0')
6180 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6181 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6183 else if (*last_comment != '\0')
6185 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6186 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6190 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6193 // set all empty fields beyond the border walls to invisible steel wall
6194 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6196 if ((x == 0 || x == level->fieldx - 1 ||
6197 y == 0 || y == level->fieldy - 1) &&
6198 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6199 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6200 level->field, level->fieldx, level->fieldy);
6203 // set special level settings for Sokoban levels
6204 SetLevelSettings_SB(level);
6206 if (load_xsb_to_ces)
6208 // special global settings can now be set in level template
6209 level->use_custom_template = TRUE;
6214 // -------------------------------------------------------------------------
6215 // functions for handling native levels
6216 // -------------------------------------------------------------------------
6218 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6219 struct LevelFileInfo *level_file_info,
6220 boolean level_info_only)
6222 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6223 level->no_valid_file = TRUE;
6226 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6227 struct LevelFileInfo *level_file_info,
6228 boolean level_info_only)
6232 // determine position of requested level inside level package
6233 if (level_file_info->packed)
6234 pos = level_file_info->nr - leveldir_current->first_level;
6236 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6237 level->no_valid_file = TRUE;
6240 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6241 struct LevelFileInfo *level_file_info,
6242 boolean level_info_only)
6244 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6245 level->no_valid_file = TRUE;
6248 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6250 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6251 CopyNativeLevel_RND_to_EM(level);
6252 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6253 CopyNativeLevel_RND_to_SP(level);
6254 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6255 CopyNativeLevel_RND_to_MM(level);
6258 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6260 if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6261 CopyNativeLevel_EM_to_RND(level);
6262 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6263 CopyNativeLevel_SP_to_RND(level);
6264 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6265 CopyNativeLevel_MM_to_RND(level);
6268 void SaveNativeLevel(struct LevelInfo *level)
6270 if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6272 char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
6273 char *filename = getLevelFilenameFromBasename(basename);
6275 CopyNativeLevel_RND_to_SP(level);
6276 CopyNativeTape_RND_to_SP(level);
6278 SaveNativeLevel_SP(filename);
6283 // ----------------------------------------------------------------------------
6284 // functions for loading generic level
6285 // ----------------------------------------------------------------------------
6287 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6288 struct LevelFileInfo *level_file_info,
6289 boolean level_info_only)
6291 // always start with reliable default values
6292 setLevelInfoToDefaults(level, level_info_only, TRUE);
6294 switch (level_file_info->type)
6296 case LEVEL_FILE_TYPE_RND:
6297 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6300 case LEVEL_FILE_TYPE_EM:
6301 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6302 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6305 case LEVEL_FILE_TYPE_SP:
6306 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6307 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6310 case LEVEL_FILE_TYPE_MM:
6311 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6312 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6315 case LEVEL_FILE_TYPE_DC:
6316 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6319 case LEVEL_FILE_TYPE_SB:
6320 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6324 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6328 // if level file is invalid, restore level structure to default values
6329 if (level->no_valid_file)
6330 setLevelInfoToDefaults(level, level_info_only, FALSE);
6332 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6333 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6335 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6336 CopyNativeLevel_Native_to_RND(level);
6339 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6341 static struct LevelFileInfo level_file_info;
6343 // always start with reliable default values
6344 setFileInfoToDefaults(&level_file_info);
6346 level_file_info.nr = 0; // unknown level number
6347 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6349 setString(&level_file_info.filename, filename);
6351 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6354 static void LoadLevel_InitVersion(struct LevelInfo *level)
6358 if (leveldir_current == NULL) // only when dumping level
6361 // all engine modifications also valid for levels which use latest engine
6362 if (level->game_version < VERSION_IDENT(3,2,0,5))
6364 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6365 level->time_score_base = 10;
6368 if (leveldir_current->latest_engine)
6370 // ---------- use latest game engine --------------------------------------
6372 /* For all levels which are forced to use the latest game engine version
6373 (normally all but user contributed, private and undefined levels), set
6374 the game engine version to the actual version; this allows for actual
6375 corrections in the game engine to take effect for existing, converted
6376 levels (from "classic" or other existing games) to make the emulation
6377 of the corresponding game more accurate, while (hopefully) not breaking
6378 existing levels created from other players. */
6380 level->game_version = GAME_VERSION_ACTUAL;
6382 /* Set special EM style gems behaviour: EM style gems slip down from
6383 normal, steel and growing wall. As this is a more fundamental change,
6384 it seems better to set the default behaviour to "off" (as it is more
6385 natural) and make it configurable in the level editor (as a property
6386 of gem style elements). Already existing converted levels (neither
6387 private nor contributed levels) are changed to the new behaviour. */
6389 if (level->file_version < FILE_VERSION_2_0)
6390 level->em_slippery_gems = TRUE;
6395 // ---------- use game engine the level was created with --------------------
6397 /* For all levels which are not forced to use the latest game engine
6398 version (normally user contributed, private and undefined levels),
6399 use the version of the game engine the levels were created for.
6401 Since 2.0.1, the game engine version is now directly stored
6402 in the level file (chunk "VERS"), so there is no need anymore
6403 to set the game version from the file version (except for old,
6404 pre-2.0 levels, where the game version is still taken from the
6405 file format version used to store the level -- see above). */
6407 // player was faster than enemies in 1.0.0 and before
6408 if (level->file_version == FILE_VERSION_1_0)
6409 for (i = 0; i < MAX_PLAYERS; i++)
6410 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6412 // default behaviour for EM style gems was "slippery" only in 2.0.1
6413 if (level->game_version == VERSION_IDENT(2,0,1,0))
6414 level->em_slippery_gems = TRUE;
6416 // springs could be pushed over pits before (pre-release version) 2.2.0
6417 if (level->game_version < VERSION_IDENT(2,2,0,0))
6418 level->use_spring_bug = TRUE;
6420 if (level->game_version < VERSION_IDENT(3,2,0,5))
6422 // time orb caused limited time in endless time levels before 3.2.0-5
6423 level->use_time_orb_bug = TRUE;
6425 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6426 level->block_snap_field = FALSE;
6428 // extra time score was same value as time left score before 3.2.0-5
6429 level->extra_time_score = level->score[SC_TIME_BONUS];
6432 if (level->game_version < VERSION_IDENT(3,2,0,7))
6434 // default behaviour for snapping was "not continuous" before 3.2.0-7
6435 level->continuous_snapping = FALSE;
6438 // only few elements were able to actively move into acid before 3.1.0
6439 // trigger settings did not exist before 3.1.0; set to default "any"
6440 if (level->game_version < VERSION_IDENT(3,1,0,0))
6442 // correct "can move into acid" settings (all zero in old levels)
6444 level->can_move_into_acid_bits = 0; // nothing can move into acid
6445 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6447 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6448 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6449 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6450 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6452 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6453 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6455 // correct trigger settings (stored as zero == "none" in old levels)
6457 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6459 int element = EL_CUSTOM_START + i;
6460 struct ElementInfo *ei = &element_info[element];
6462 for (j = 0; j < ei->num_change_pages; j++)
6464 struct ElementChangeInfo *change = &ei->change_page[j];
6466 change->trigger_player = CH_PLAYER_ANY;
6467 change->trigger_page = CH_PAGE_ANY;
6472 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6474 int element = EL_CUSTOM_256;
6475 struct ElementInfo *ei = &element_info[element];
6476 struct ElementChangeInfo *change = &ei->change_page[0];
6478 /* This is needed to fix a problem that was caused by a bugfix in function
6479 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6480 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6481 not replace walkable elements, but instead just placed the player on it,
6482 without placing the Sokoban field under the player). Unfortunately, this
6483 breaks "Snake Bite" style levels when the snake is halfway through a door
6484 that just closes (the snake head is still alive and can be moved in this
6485 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6486 player (without Sokoban element) which then gets killed as designed). */
6488 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6489 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6490 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6491 change->target_element = EL_PLAYER_1;
6494 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6495 if (level->game_version < VERSION_IDENT(3,2,5,0))
6497 /* This is needed to fix a problem that was caused by a bugfix in function
6498 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6499 corrects the behaviour when a custom element changes to another custom
6500 element with a higher element number that has change actions defined.
6501 Normally, only one change per frame is allowed for custom elements.
6502 Therefore, it is checked if a custom element already changed in the
6503 current frame; if it did, subsequent changes are suppressed.
6504 Unfortunately, this is only checked for element changes, but not for
6505 change actions, which are still executed. As the function above loops
6506 through all custom elements from lower to higher, an element change
6507 resulting in a lower CE number won't be checked again, while a target
6508 element with a higher number will also be checked, and potential change
6509 actions will get executed for this CE, too (which is wrong), while
6510 further changes are ignored (which is correct). As this bugfix breaks
6511 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6512 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6513 behaviour for existing levels and tapes that make use of this bug */
6515 level->use_action_after_change_bug = TRUE;
6518 // not centering level after relocating player was default only in 3.2.3
6519 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6520 level->shifted_relocation = TRUE;
6522 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6523 if (level->game_version < VERSION_IDENT(3,2,6,0))
6524 level->em_explodes_by_fire = TRUE;
6526 // levels were solved by the first player entering an exit up to 4.1.0.0
6527 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6528 level->solved_by_one_player = TRUE;
6530 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6531 if (level->game_version < VERSION_IDENT(4,1,1,1))
6532 level->use_life_bugs = TRUE;
6534 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6535 if (level->game_version < VERSION_IDENT(4,1,1,1))
6536 level->sb_objects_needed = FALSE;
6538 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6539 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6540 level->finish_dig_collect = FALSE;
6542 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6543 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6544 level->keep_walkable_ce = TRUE;
6547 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6549 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6552 // check if this level is (not) a Sokoban level
6553 for (y = 0; y < level->fieldy; y++)
6554 for (x = 0; x < level->fieldx; x++)
6555 if (!IS_SB_ELEMENT(Tile[x][y]))
6556 is_sokoban_level = FALSE;
6558 if (is_sokoban_level)
6560 // set special level settings for Sokoban levels
6561 SetLevelSettings_SB(level);
6565 static void LoadLevel_InitSettings(struct LevelInfo *level)
6567 // adjust level settings for (non-native) Sokoban-style levels
6568 LoadLevel_InitSettings_SB(level);
6570 // rename levels with title "nameless level" or if renaming is forced
6571 if (leveldir_current->empty_level_name != NULL &&
6572 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6573 leveldir_current->force_level_name))
6574 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6575 leveldir_current->empty_level_name, level_nr);
6578 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6582 // map elements that have changed in newer versions
6583 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6584 level->game_version);
6585 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6586 for (x = 0; x < 3; x++)
6587 for (y = 0; y < 3; y++)
6588 level->yamyam_content[i].e[x][y] =
6589 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6590 level->game_version);
6594 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6598 // map custom element change events that have changed in newer versions
6599 // (these following values were accidentally changed in version 3.0.1)
6600 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6601 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6603 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6605 int element = EL_CUSTOM_START + i;
6607 // order of checking and copying events to be mapped is important
6608 // (do not change the start and end value -- they are constant)
6609 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6611 if (HAS_CHANGE_EVENT(element, j - 2))
6613 SET_CHANGE_EVENT(element, j - 2, FALSE);
6614 SET_CHANGE_EVENT(element, j, TRUE);
6618 // order of checking and copying events to be mapped is important
6619 // (do not change the start and end value -- they are constant)
6620 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6622 if (HAS_CHANGE_EVENT(element, j - 1))
6624 SET_CHANGE_EVENT(element, j - 1, FALSE);
6625 SET_CHANGE_EVENT(element, j, TRUE);
6631 // initialize "can_change" field for old levels with only one change page
6632 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6634 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6636 int element = EL_CUSTOM_START + i;
6638 if (CAN_CHANGE(element))
6639 element_info[element].change->can_change = TRUE;
6643 // correct custom element values (for old levels without these options)
6644 if (level->game_version < VERSION_IDENT(3,1,1,0))
6646 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6648 int element = EL_CUSTOM_START + i;
6649 struct ElementInfo *ei = &element_info[element];
6651 if (ei->access_direction == MV_NO_DIRECTION)
6652 ei->access_direction = MV_ALL_DIRECTIONS;
6656 // correct custom element values (fix invalid values for all versions)
6659 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6661 int element = EL_CUSTOM_START + i;
6662 struct ElementInfo *ei = &element_info[element];
6664 for (j = 0; j < ei->num_change_pages; j++)
6666 struct ElementChangeInfo *change = &ei->change_page[j];
6668 if (change->trigger_player == CH_PLAYER_NONE)
6669 change->trigger_player = CH_PLAYER_ANY;
6671 if (change->trigger_side == CH_SIDE_NONE)
6672 change->trigger_side = CH_SIDE_ANY;
6677 // initialize "can_explode" field for old levels which did not store this
6678 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
6679 if (level->game_version <= VERSION_IDENT(3,1,0,0))
6681 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6683 int element = EL_CUSTOM_START + i;
6685 if (EXPLODES_1X1_OLD(element))
6686 element_info[element].explosion_type = EXPLODES_1X1;
6688 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6689 EXPLODES_SMASHED(element) ||
6690 EXPLODES_IMPACT(element)));
6694 // correct previously hard-coded move delay values for maze runner style
6695 if (level->game_version < VERSION_IDENT(3,1,1,0))
6697 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6699 int element = EL_CUSTOM_START + i;
6701 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6703 // previously hard-coded and therefore ignored
6704 element_info[element].move_delay_fixed = 9;
6705 element_info[element].move_delay_random = 0;
6710 // set some other uninitialized values of custom elements in older levels
6711 if (level->game_version < VERSION_IDENT(3,1,0,0))
6713 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6715 int element = EL_CUSTOM_START + i;
6717 element_info[element].access_direction = MV_ALL_DIRECTIONS;
6719 element_info[element].explosion_delay = 17;
6720 element_info[element].ignition_delay = 8;
6724 // set mouse click change events to work for left/middle/right mouse button
6725 if (level->game_version < VERSION_IDENT(4,2,3,0))
6727 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6729 int element = EL_CUSTOM_START + i;
6730 struct ElementInfo *ei = &element_info[element];
6732 for (j = 0; j < ei->num_change_pages; j++)
6734 struct ElementChangeInfo *change = &ei->change_page[j];
6736 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
6737 change->has_event[CE_PRESSED_BY_MOUSE] ||
6738 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
6739 change->has_event[CE_MOUSE_PRESSED_ON_X])
6740 change->trigger_side = CH_SIDE_ANY;
6746 static void LoadLevel_InitElements(struct LevelInfo *level)
6748 LoadLevel_InitStandardElements(level);
6750 if (level->file_has_custom_elements)
6751 LoadLevel_InitCustomElements(level);
6753 // initialize element properties for level editor etc.
6754 InitElementPropertiesEngine(level->game_version);
6755 InitElementPropertiesGfxElement();
6758 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
6762 // map elements that have changed in newer versions
6763 for (y = 0; y < level->fieldy; y++)
6764 for (x = 0; x < level->fieldx; x++)
6765 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6766 level->game_version);
6768 // clear unused playfield data (nicer if level gets resized in editor)
6769 for (x = 0; x < MAX_LEV_FIELDX; x++)
6770 for (y = 0; y < MAX_LEV_FIELDY; y++)
6771 if (x >= level->fieldx || y >= level->fieldy)
6772 level->field[x][y] = EL_EMPTY;
6774 // copy elements to runtime playfield array
6775 for (x = 0; x < MAX_LEV_FIELDX; x++)
6776 for (y = 0; y < MAX_LEV_FIELDY; y++)
6777 Tile[x][y] = level->field[x][y];
6779 // initialize level size variables for faster access
6780 lev_fieldx = level->fieldx;
6781 lev_fieldy = level->fieldy;
6783 // determine border element for this level
6784 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6785 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
6790 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
6792 struct LevelFileInfo *level_file_info = &level->file_info;
6794 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6795 CopyNativeLevel_RND_to_Native(level);
6798 static void LoadLevelTemplate_LoadAndInit(void)
6800 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6802 LoadLevel_InitVersion(&level_template);
6803 LoadLevel_InitElements(&level_template);
6804 LoadLevel_InitSettings(&level_template);
6806 ActivateLevelTemplate();
6809 void LoadLevelTemplate(int nr)
6811 if (!fileExists(getGlobalLevelTemplateFilename()))
6813 Warn("no level template found for this level");
6818 setLevelFileInfo(&level_template.file_info, nr);
6820 LoadLevelTemplate_LoadAndInit();
6823 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
6825 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
6827 LoadLevelTemplate_LoadAndInit();
6830 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
6832 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6834 if (level.use_custom_template)
6836 if (network_level != NULL)
6837 LoadNetworkLevelTemplate(network_level);
6839 LoadLevelTemplate(-1);
6842 LoadLevel_InitVersion(&level);
6843 LoadLevel_InitElements(&level);
6844 LoadLevel_InitPlayfield(&level);
6845 LoadLevel_InitSettings(&level);
6847 LoadLevel_InitNativeEngines(&level);
6850 void LoadLevel(int nr)
6852 SetLevelSetInfo(leveldir_current->identifier, nr);
6854 setLevelFileInfo(&level.file_info, nr);
6856 LoadLevel_LoadAndInit(NULL);
6859 void LoadLevelInfoOnly(int nr)
6861 setLevelFileInfo(&level.file_info, nr);
6863 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6866 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
6868 SetLevelSetInfo(network_level->leveldir_identifier,
6869 network_level->file_info.nr);
6871 copyLevelFileInfo(&network_level->file_info, &level.file_info);
6873 LoadLevel_LoadAndInit(network_level);
6876 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6880 chunk_size += putFileVersion(file, level->file_version);
6881 chunk_size += putFileVersion(file, level->game_version);
6886 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6890 chunk_size += putFile16BitBE(file, level->creation_date.year);
6891 chunk_size += putFile8Bit(file, level->creation_date.month);
6892 chunk_size += putFile8Bit(file, level->creation_date.day);
6897 #if ENABLE_HISTORIC_CHUNKS
6898 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6902 putFile8Bit(file, level->fieldx);
6903 putFile8Bit(file, level->fieldy);
6905 putFile16BitBE(file, level->time);
6906 putFile16BitBE(file, level->gems_needed);
6908 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6909 putFile8Bit(file, level->name[i]);
6911 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6912 putFile8Bit(file, level->score[i]);
6914 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6915 for (y = 0; y < 3; y++)
6916 for (x = 0; x < 3; x++)
6917 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6918 level->yamyam_content[i].e[x][y]));
6919 putFile8Bit(file, level->amoeba_speed);
6920 putFile8Bit(file, level->time_magic_wall);
6921 putFile8Bit(file, level->time_wheel);
6922 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6923 level->amoeba_content));
6924 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6925 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6926 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6927 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6929 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6931 putFile8Bit(file, (level->block_last_field ? 1 : 0));
6932 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6933 putFile32BitBE(file, level->can_move_into_acid_bits);
6934 putFile8Bit(file, level->dont_collide_with_bits);
6936 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6937 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6939 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6940 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6941 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6943 putFile8Bit(file, level->game_engine_type);
6945 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6949 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6954 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6955 chunk_size += putFile8Bit(file, level->name[i]);
6960 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6965 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6966 chunk_size += putFile8Bit(file, level->author[i]);
6971 #if ENABLE_HISTORIC_CHUNKS
6972 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6977 for (y = 0; y < level->fieldy; y++)
6978 for (x = 0; x < level->fieldx; x++)
6979 if (level->encoding_16bit_field)
6980 chunk_size += putFile16BitBE(file, level->field[x][y]);
6982 chunk_size += putFile8Bit(file, level->field[x][y]);
6988 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6993 for (y = 0; y < level->fieldy; y++)
6994 for (x = 0; x < level->fieldx; x++)
6995 chunk_size += putFile16BitBE(file, level->field[x][y]);
7000 #if ENABLE_HISTORIC_CHUNKS
7001 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7005 putFile8Bit(file, EL_YAMYAM);
7006 putFile8Bit(file, level->num_yamyam_contents);
7007 putFile8Bit(file, 0);
7008 putFile8Bit(file, 0);
7010 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7011 for (y = 0; y < 3; y++)
7012 for (x = 0; x < 3; x++)
7013 if (level->encoding_16bit_field)
7014 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7016 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7020 #if ENABLE_HISTORIC_CHUNKS
7021 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7024 int num_contents, content_xsize, content_ysize;
7025 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7027 if (element == EL_YAMYAM)
7029 num_contents = level->num_yamyam_contents;
7033 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7034 for (y = 0; y < 3; y++)
7035 for (x = 0; x < 3; x++)
7036 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7038 else if (element == EL_BD_AMOEBA)
7044 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7045 for (y = 0; y < 3; y++)
7046 for (x = 0; x < 3; x++)
7047 content_array[i][x][y] = EL_EMPTY;
7048 content_array[0][0][0] = level->amoeba_content;
7052 // chunk header already written -- write empty chunk data
7053 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7055 Warn("cannot save content for element '%d'", element);
7060 putFile16BitBE(file, element);
7061 putFile8Bit(file, num_contents);
7062 putFile8Bit(file, content_xsize);
7063 putFile8Bit(file, content_ysize);
7065 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7067 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7068 for (y = 0; y < 3; y++)
7069 for (x = 0; x < 3; x++)
7070 putFile16BitBE(file, content_array[i][x][y]);
7074 #if ENABLE_HISTORIC_CHUNKS
7075 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7077 int envelope_nr = element - EL_ENVELOPE_1;
7078 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7082 chunk_size += putFile16BitBE(file, element);
7083 chunk_size += putFile16BitBE(file, envelope_len);
7084 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7085 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7087 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7088 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7090 for (i = 0; i < envelope_len; i++)
7091 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7097 #if ENABLE_HISTORIC_CHUNKS
7098 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7099 int num_changed_custom_elements)
7103 putFile16BitBE(file, num_changed_custom_elements);
7105 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7107 int element = EL_CUSTOM_START + i;
7109 struct ElementInfo *ei = &element_info[element];
7111 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7113 if (check < num_changed_custom_elements)
7115 putFile16BitBE(file, element);
7116 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7123 if (check != num_changed_custom_elements) // should not happen
7124 Warn("inconsistent number of custom element properties");
7128 #if ENABLE_HISTORIC_CHUNKS
7129 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7130 int num_changed_custom_elements)
7134 putFile16BitBE(file, num_changed_custom_elements);
7136 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7138 int element = EL_CUSTOM_START + i;
7140 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7142 if (check < num_changed_custom_elements)
7144 putFile16BitBE(file, element);
7145 putFile16BitBE(file, element_info[element].change->target_element);
7152 if (check != num_changed_custom_elements) // should not happen
7153 Warn("inconsistent number of custom target elements");
7157 #if ENABLE_HISTORIC_CHUNKS
7158 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7159 int num_changed_custom_elements)
7161 int i, j, x, y, check = 0;
7163 putFile16BitBE(file, num_changed_custom_elements);
7165 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7167 int element = EL_CUSTOM_START + i;
7168 struct ElementInfo *ei = &element_info[element];
7170 if (ei->modified_settings)
7172 if (check < num_changed_custom_elements)
7174 putFile16BitBE(file, element);
7176 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7177 putFile8Bit(file, ei->description[j]);
7179 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7181 // some free bytes for future properties and padding
7182 WriteUnusedBytesToFile(file, 7);
7184 putFile8Bit(file, ei->use_gfx_element);
7185 putFile16BitBE(file, ei->gfx_element_initial);
7187 putFile8Bit(file, ei->collect_score_initial);
7188 putFile8Bit(file, ei->collect_count_initial);
7190 putFile16BitBE(file, ei->push_delay_fixed);
7191 putFile16BitBE(file, ei->push_delay_random);
7192 putFile16BitBE(file, ei->move_delay_fixed);
7193 putFile16BitBE(file, ei->move_delay_random);
7195 putFile16BitBE(file, ei->move_pattern);
7196 putFile8Bit(file, ei->move_direction_initial);
7197 putFile8Bit(file, ei->move_stepsize);
7199 for (y = 0; y < 3; y++)
7200 for (x = 0; x < 3; x++)
7201 putFile16BitBE(file, ei->content.e[x][y]);
7203 putFile32BitBE(file, ei->change->events);
7205 putFile16BitBE(file, ei->change->target_element);
7207 putFile16BitBE(file, ei->change->delay_fixed);
7208 putFile16BitBE(file, ei->change->delay_random);
7209 putFile16BitBE(file, ei->change->delay_frames);
7211 putFile16BitBE(file, ei->change->initial_trigger_element);
7213 putFile8Bit(file, ei->change->explode);
7214 putFile8Bit(file, ei->change->use_target_content);
7215 putFile8Bit(file, ei->change->only_if_complete);
7216 putFile8Bit(file, ei->change->use_random_replace);
7218 putFile8Bit(file, ei->change->random_percentage);
7219 putFile8Bit(file, ei->change->replace_when);
7221 for (y = 0; y < 3; y++)
7222 for (x = 0; x < 3; x++)
7223 putFile16BitBE(file, ei->change->content.e[x][y]);
7225 putFile8Bit(file, ei->slippery_type);
7227 // some free bytes for future properties and padding
7228 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7235 if (check != num_changed_custom_elements) // should not happen
7236 Warn("inconsistent number of custom element properties");
7240 #if ENABLE_HISTORIC_CHUNKS
7241 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7243 struct ElementInfo *ei = &element_info[element];
7246 // ---------- custom element base property values (96 bytes) ----------------
7248 putFile16BitBE(file, element);
7250 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7251 putFile8Bit(file, ei->description[i]);
7253 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7255 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7257 putFile8Bit(file, ei->num_change_pages);
7259 putFile16BitBE(file, ei->ce_value_fixed_initial);
7260 putFile16BitBE(file, ei->ce_value_random_initial);
7261 putFile8Bit(file, ei->use_last_ce_value);
7263 putFile8Bit(file, ei->use_gfx_element);
7264 putFile16BitBE(file, ei->gfx_element_initial);
7266 putFile8Bit(file, ei->collect_score_initial);
7267 putFile8Bit(file, ei->collect_count_initial);
7269 putFile8Bit(file, ei->drop_delay_fixed);
7270 putFile8Bit(file, ei->push_delay_fixed);
7271 putFile8Bit(file, ei->drop_delay_random);
7272 putFile8Bit(file, ei->push_delay_random);
7273 putFile16BitBE(file, ei->move_delay_fixed);
7274 putFile16BitBE(file, ei->move_delay_random);
7276 // bits 0 - 15 of "move_pattern" ...
7277 putFile16BitBE(file, ei->move_pattern & 0xffff);
7278 putFile8Bit(file, ei->move_direction_initial);
7279 putFile8Bit(file, ei->move_stepsize);
7281 putFile8Bit(file, ei->slippery_type);
7283 for (y = 0; y < 3; y++)
7284 for (x = 0; x < 3; x++)
7285 putFile16BitBE(file, ei->content.e[x][y]);
7287 putFile16BitBE(file, ei->move_enter_element);
7288 putFile16BitBE(file, ei->move_leave_element);
7289 putFile8Bit(file, ei->move_leave_type);
7291 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7292 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7294 putFile8Bit(file, ei->access_direction);
7296 putFile8Bit(file, ei->explosion_delay);
7297 putFile8Bit(file, ei->ignition_delay);
7298 putFile8Bit(file, ei->explosion_type);
7300 // some free bytes for future custom property values and padding
7301 WriteUnusedBytesToFile(file, 1);
7303 // ---------- change page property values (48 bytes) ------------------------
7305 for (i = 0; i < ei->num_change_pages; i++)
7307 struct ElementChangeInfo *change = &ei->change_page[i];
7308 unsigned int event_bits;
7310 // bits 0 - 31 of "has_event[]" ...
7312 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7313 if (change->has_event[j])
7314 event_bits |= (1 << j);
7315 putFile32BitBE(file, event_bits);
7317 putFile16BitBE(file, change->target_element);
7319 putFile16BitBE(file, change->delay_fixed);
7320 putFile16BitBE(file, change->delay_random);
7321 putFile16BitBE(file, change->delay_frames);
7323 putFile16BitBE(file, change->initial_trigger_element);
7325 putFile8Bit(file, change->explode);
7326 putFile8Bit(file, change->use_target_content);
7327 putFile8Bit(file, change->only_if_complete);
7328 putFile8Bit(file, change->use_random_replace);
7330 putFile8Bit(file, change->random_percentage);
7331 putFile8Bit(file, change->replace_when);
7333 for (y = 0; y < 3; y++)
7334 for (x = 0; x < 3; x++)
7335 putFile16BitBE(file, change->target_content.e[x][y]);
7337 putFile8Bit(file, change->can_change);
7339 putFile8Bit(file, change->trigger_side);
7341 putFile8Bit(file, change->trigger_player);
7342 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7343 log_2(change->trigger_page)));
7345 putFile8Bit(file, change->has_action);
7346 putFile8Bit(file, change->action_type);
7347 putFile8Bit(file, change->action_mode);
7348 putFile16BitBE(file, change->action_arg);
7350 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7352 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7353 if (change->has_event[j])
7354 event_bits |= (1 << (j - 32));
7355 putFile8Bit(file, event_bits);
7360 #if ENABLE_HISTORIC_CHUNKS
7361 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7363 struct ElementInfo *ei = &element_info[element];
7364 struct ElementGroupInfo *group = ei->group;
7367 putFile16BitBE(file, element);
7369 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7370 putFile8Bit(file, ei->description[i]);
7372 putFile8Bit(file, group->num_elements);
7374 putFile8Bit(file, ei->use_gfx_element);
7375 putFile16BitBE(file, ei->gfx_element_initial);
7377 putFile8Bit(file, group->choice_mode);
7379 // some free bytes for future values and padding
7380 WriteUnusedBytesToFile(file, 3);
7382 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7383 putFile16BitBE(file, group->element[i]);
7387 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7388 boolean write_element)
7390 int save_type = entry->save_type;
7391 int data_type = entry->data_type;
7392 int conf_type = entry->conf_type;
7393 int byte_mask = conf_type & CONF_MASK_BYTES;
7394 int element = entry->element;
7395 int default_value = entry->default_value;
7397 boolean modified = FALSE;
7399 if (byte_mask != CONF_MASK_MULTI_BYTES)
7401 void *value_ptr = entry->value;
7402 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7405 // check if any settings have been modified before saving them
7406 if (value != default_value)
7409 // do not save if explicitly told or if unmodified default settings
7410 if ((save_type == SAVE_CONF_NEVER) ||
7411 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7415 num_bytes += putFile16BitBE(file, element);
7417 num_bytes += putFile8Bit(file, conf_type);
7418 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7419 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7420 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7423 else if (data_type == TYPE_STRING)
7425 char *default_string = entry->default_string;
7426 char *string = (char *)(entry->value);
7427 int string_length = strlen(string);
7430 // check if any settings have been modified before saving them
7431 if (!strEqual(string, default_string))
7434 // do not save if explicitly told or if unmodified default settings
7435 if ((save_type == SAVE_CONF_NEVER) ||
7436 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7440 num_bytes += putFile16BitBE(file, element);
7442 num_bytes += putFile8Bit(file, conf_type);
7443 num_bytes += putFile16BitBE(file, string_length);
7445 for (i = 0; i < string_length; i++)
7446 num_bytes += putFile8Bit(file, string[i]);
7448 else if (data_type == TYPE_ELEMENT_LIST)
7450 int *element_array = (int *)(entry->value);
7451 int num_elements = *(int *)(entry->num_entities);
7454 // check if any settings have been modified before saving them
7455 for (i = 0; i < num_elements; i++)
7456 if (element_array[i] != default_value)
7459 // do not save if explicitly told or if unmodified default settings
7460 if ((save_type == SAVE_CONF_NEVER) ||
7461 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7465 num_bytes += putFile16BitBE(file, element);
7467 num_bytes += putFile8Bit(file, conf_type);
7468 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7470 for (i = 0; i < num_elements; i++)
7471 num_bytes += putFile16BitBE(file, element_array[i]);
7473 else if (data_type == TYPE_CONTENT_LIST)
7475 struct Content *content = (struct Content *)(entry->value);
7476 int num_contents = *(int *)(entry->num_entities);
7479 // check if any settings have been modified before saving them
7480 for (i = 0; i < num_contents; i++)
7481 for (y = 0; y < 3; y++)
7482 for (x = 0; x < 3; x++)
7483 if (content[i].e[x][y] != default_value)
7486 // do not save if explicitly told or if unmodified default settings
7487 if ((save_type == SAVE_CONF_NEVER) ||
7488 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7492 num_bytes += putFile16BitBE(file, element);
7494 num_bytes += putFile8Bit(file, conf_type);
7495 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7497 for (i = 0; i < num_contents; i++)
7498 for (y = 0; y < 3; y++)
7499 for (x = 0; x < 3; x++)
7500 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7506 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7511 li = *level; // copy level data into temporary buffer
7513 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7514 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7519 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7524 li = *level; // copy level data into temporary buffer
7526 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7527 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7532 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7534 int envelope_nr = element - EL_ENVELOPE_1;
7538 chunk_size += putFile16BitBE(file, element);
7540 // copy envelope data into temporary buffer
7541 xx_envelope = level->envelope[envelope_nr];
7543 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7544 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7549 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7551 struct ElementInfo *ei = &element_info[element];
7555 chunk_size += putFile16BitBE(file, element);
7557 xx_ei = *ei; // copy element data into temporary buffer
7559 // set default description string for this specific element
7560 strcpy(xx_default_description, getDefaultElementDescription(ei));
7562 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7563 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7565 for (i = 0; i < ei->num_change_pages; i++)
7567 struct ElementChangeInfo *change = &ei->change_page[i];
7569 xx_current_change_page = i;
7571 xx_change = *change; // copy change data into temporary buffer
7574 setEventBitsFromEventFlags(change);
7576 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7577 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7584 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7586 struct ElementInfo *ei = &element_info[element];
7587 struct ElementGroupInfo *group = ei->group;
7591 chunk_size += putFile16BitBE(file, element);
7593 xx_ei = *ei; // copy element data into temporary buffer
7594 xx_group = *group; // copy group data into temporary buffer
7596 // set default description string for this specific element
7597 strcpy(xx_default_description, getDefaultElementDescription(ei));
7599 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7600 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7605 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7607 struct ElementInfo *ei = &element_info[element];
7611 chunk_size += putFile16BitBE(file, element);
7613 xx_ei = *ei; // copy element data into temporary buffer
7615 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7616 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7621 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7622 boolean save_as_template)
7628 if (!(file = fopen(filename, MODE_WRITE)))
7630 Warn("cannot save level file '%s'", filename);
7635 level->file_version = FILE_VERSION_ACTUAL;
7636 level->game_version = GAME_VERSION_ACTUAL;
7638 level->creation_date = getCurrentDate();
7640 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7641 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7643 chunk_size = SaveLevel_VERS(NULL, level);
7644 putFileChunkBE(file, "VERS", chunk_size);
7645 SaveLevel_VERS(file, level);
7647 chunk_size = SaveLevel_DATE(NULL, level);
7648 putFileChunkBE(file, "DATE", chunk_size);
7649 SaveLevel_DATE(file, level);
7651 chunk_size = SaveLevel_NAME(NULL, level);
7652 putFileChunkBE(file, "NAME", chunk_size);
7653 SaveLevel_NAME(file, level);
7655 chunk_size = SaveLevel_AUTH(NULL, level);
7656 putFileChunkBE(file, "AUTH", chunk_size);
7657 SaveLevel_AUTH(file, level);
7659 chunk_size = SaveLevel_INFO(NULL, level);
7660 putFileChunkBE(file, "INFO", chunk_size);
7661 SaveLevel_INFO(file, level);
7663 chunk_size = SaveLevel_BODY(NULL, level);
7664 putFileChunkBE(file, "BODY", chunk_size);
7665 SaveLevel_BODY(file, level);
7667 chunk_size = SaveLevel_ELEM(NULL, level);
7668 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
7670 putFileChunkBE(file, "ELEM", chunk_size);
7671 SaveLevel_ELEM(file, level);
7674 for (i = 0; i < NUM_ENVELOPES; i++)
7676 int element = EL_ENVELOPE_1 + i;
7678 chunk_size = SaveLevel_NOTE(NULL, level, element);
7679 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
7681 putFileChunkBE(file, "NOTE", chunk_size);
7682 SaveLevel_NOTE(file, level, element);
7686 // if not using template level, check for non-default custom/group elements
7687 if (!level->use_custom_template || save_as_template)
7689 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7691 int element = EL_CUSTOM_START + i;
7693 chunk_size = SaveLevel_CUSX(NULL, level, element);
7694 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
7696 putFileChunkBE(file, "CUSX", chunk_size);
7697 SaveLevel_CUSX(file, level, element);
7701 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7703 int element = EL_GROUP_START + i;
7705 chunk_size = SaveLevel_GRPX(NULL, level, element);
7706 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
7708 putFileChunkBE(file, "GRPX", chunk_size);
7709 SaveLevel_GRPX(file, level, element);
7713 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
7715 int element = GET_EMPTY_ELEMENT(i);
7717 chunk_size = SaveLevel_EMPX(NULL, level, element);
7718 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
7720 putFileChunkBE(file, "EMPX", chunk_size);
7721 SaveLevel_EMPX(file, level, element);
7728 SetFilePermissions(filename, PERMS_PRIVATE);
7731 void SaveLevel(int nr)
7733 char *filename = getDefaultLevelFilename(nr);
7735 SaveLevelFromFilename(&level, filename, FALSE);
7738 void SaveLevelTemplate(void)
7740 char *filename = getLocalLevelTemplateFilename();
7742 SaveLevelFromFilename(&level, filename, TRUE);
7745 boolean SaveLevelChecked(int nr)
7747 char *filename = getDefaultLevelFilename(nr);
7748 boolean new_level = !fileExists(filename);
7749 boolean level_saved = FALSE;
7751 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7756 Request("Level saved!", REQ_CONFIRM);
7764 void DumpLevel(struct LevelInfo *level)
7766 if (level->no_level_file || level->no_valid_file)
7768 Warn("cannot dump -- no valid level file found");
7774 Print("Level xxx (file version %08d, game version %08d)\n",
7775 level->file_version, level->game_version);
7778 Print("Level author: '%s'\n", level->author);
7779 Print("Level title: '%s'\n", level->name);
7781 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7783 Print("Level time: %d seconds\n", level->time);
7784 Print("Gems needed: %d\n", level->gems_needed);
7786 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7787 Print("Time for wheel: %d seconds\n", level->time_wheel);
7788 Print("Time for light: %d seconds\n", level->time_light);
7789 Print("Time for timegate: %d seconds\n", level->time_timegate);
7791 Print("Amoeba speed: %d\n", level->amoeba_speed);
7794 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
7795 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
7796 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7797 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7798 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7799 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
7805 for (i = 0; i < NUM_ENVELOPES; i++)
7807 char *text = level->envelope[i].text;
7808 int text_len = strlen(text);
7809 boolean has_text = FALSE;
7811 for (j = 0; j < text_len; j++)
7812 if (text[j] != ' ' && text[j] != '\n')
7818 Print("Envelope %d:\n'%s'\n", i + 1, text);
7826 void DumpLevels(void)
7828 static LevelDirTree *dumplevel_leveldir = NULL;
7830 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
7831 global.dumplevel_leveldir);
7833 if (dumplevel_leveldir == NULL)
7834 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
7836 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
7837 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
7838 Fail("no such level number: %d", global.dumplevel_level_nr);
7840 leveldir_current = dumplevel_leveldir;
7842 LoadLevel(global.dumplevel_level_nr);
7849 // ============================================================================
7850 // tape file functions
7851 // ============================================================================
7853 static void setTapeInfoToDefaults(void)
7857 // always start with reliable default values (empty tape)
7860 // default values (also for pre-1.2 tapes) with only the first player
7861 tape.player_participates[0] = TRUE;
7862 for (i = 1; i < MAX_PLAYERS; i++)
7863 tape.player_participates[i] = FALSE;
7865 // at least one (default: the first) player participates in every tape
7866 tape.num_participating_players = 1;
7868 tape.property_bits = TAPE_PROPERTY_NONE;
7870 tape.level_nr = level_nr;
7872 tape.changed = FALSE;
7874 tape.recording = FALSE;
7875 tape.playing = FALSE;
7876 tape.pausing = FALSE;
7878 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
7879 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
7881 tape.no_info_chunk = TRUE;
7882 tape.no_valid_file = FALSE;
7885 static int getTapePosSize(struct TapeInfo *tape)
7887 int tape_pos_size = 0;
7889 if (tape->use_key_actions)
7890 tape_pos_size += tape->num_participating_players;
7892 if (tape->use_mouse_actions)
7893 tape_pos_size += 3; // x and y position and mouse button mask
7895 tape_pos_size += 1; // tape action delay value
7897 return tape_pos_size;
7900 static void setTapeActionFlags(struct TapeInfo *tape, int value)
7902 tape->use_key_actions = FALSE;
7903 tape->use_mouse_actions = FALSE;
7905 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
7906 tape->use_key_actions = TRUE;
7908 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
7909 tape->use_mouse_actions = TRUE;
7912 static int getTapeActionValue(struct TapeInfo *tape)
7914 return (tape->use_key_actions &&
7915 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
7916 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
7917 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
7918 TAPE_ACTIONS_DEFAULT);
7921 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7923 tape->file_version = getFileVersion(file);
7924 tape->game_version = getFileVersion(file);
7929 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7933 tape->random_seed = getFile32BitBE(file);
7934 tape->date = getFile32BitBE(file);
7935 tape->length = getFile32BitBE(file);
7937 // read header fields that are new since version 1.2
7938 if (tape->file_version >= FILE_VERSION_1_2)
7940 byte store_participating_players = getFile8Bit(file);
7943 // since version 1.2, tapes store which players participate in the tape
7944 tape->num_participating_players = 0;
7945 for (i = 0; i < MAX_PLAYERS; i++)
7947 tape->player_participates[i] = FALSE;
7949 if (store_participating_players & (1 << i))
7951 tape->player_participates[i] = TRUE;
7952 tape->num_participating_players++;
7956 setTapeActionFlags(tape, getFile8Bit(file));
7958 tape->property_bits = getFile8Bit(file);
7960 ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7962 engine_version = getFileVersion(file);
7963 if (engine_version > 0)
7964 tape->engine_version = engine_version;
7966 tape->engine_version = tape->game_version;
7972 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
7974 tape->scr_fieldx = getFile8Bit(file);
7975 tape->scr_fieldy = getFile8Bit(file);
7980 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7982 char *level_identifier = NULL;
7983 int level_identifier_size;
7986 tape->no_info_chunk = FALSE;
7988 level_identifier_size = getFile16BitBE(file);
7990 level_identifier = checked_malloc(level_identifier_size);
7992 for (i = 0; i < level_identifier_size; i++)
7993 level_identifier[i] = getFile8Bit(file);
7995 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
7996 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
7998 checked_free(level_identifier);
8000 tape->level_nr = getFile16BitBE(file);
8002 chunk_size = 2 + level_identifier_size + 2;
8007 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8010 int tape_pos_size = getTapePosSize(tape);
8011 int chunk_size_expected = tape_pos_size * tape->length;
8013 if (chunk_size_expected != chunk_size)
8015 ReadUnusedBytesFromFile(file, chunk_size);
8016 return chunk_size_expected;
8019 for (i = 0; i < tape->length; i++)
8021 if (i >= MAX_TAPE_LEN)
8023 Warn("tape truncated -- size exceeds maximum tape size %d",
8026 // tape too large; read and ignore remaining tape data from this chunk
8027 for (;i < tape->length; i++)
8028 ReadUnusedBytesFromFile(file, tape_pos_size);
8033 if (tape->use_key_actions)
8035 for (j = 0; j < MAX_PLAYERS; j++)
8037 tape->pos[i].action[j] = MV_NONE;
8039 if (tape->player_participates[j])
8040 tape->pos[i].action[j] = getFile8Bit(file);
8044 if (tape->use_mouse_actions)
8046 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8047 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8048 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8051 tape->pos[i].delay = getFile8Bit(file);
8053 if (tape->file_version == FILE_VERSION_1_0)
8055 // eliminate possible diagonal moves in old tapes
8056 // this is only for backward compatibility
8058 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8059 byte action = tape->pos[i].action[0];
8060 int k, num_moves = 0;
8062 for (k = 0; k<4; k++)
8064 if (action & joy_dir[k])
8066 tape->pos[i + num_moves].action[0] = joy_dir[k];
8068 tape->pos[i + num_moves].delay = 0;
8077 tape->length += num_moves;
8080 else if (tape->file_version < FILE_VERSION_2_0)
8082 // convert pre-2.0 tapes to new tape format
8084 if (tape->pos[i].delay > 1)
8087 tape->pos[i + 1] = tape->pos[i];
8088 tape->pos[i + 1].delay = 1;
8091 for (j = 0; j < MAX_PLAYERS; j++)
8092 tape->pos[i].action[j] = MV_NONE;
8093 tape->pos[i].delay--;
8100 if (checkEndOfFile(file))
8104 if (i != tape->length)
8105 chunk_size = tape_pos_size * i;
8110 static void LoadTape_SokobanSolution(char *filename)
8113 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8115 if (!(file = openFile(filename, MODE_READ)))
8117 tape.no_valid_file = TRUE;
8122 while (!checkEndOfFile(file))
8124 unsigned char c = getByteFromFile(file);
8126 if (checkEndOfFile(file))
8133 tape.pos[tape.length].action[0] = MV_UP;
8134 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8140 tape.pos[tape.length].action[0] = MV_DOWN;
8141 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8147 tape.pos[tape.length].action[0] = MV_LEFT;
8148 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8154 tape.pos[tape.length].action[0] = MV_RIGHT;
8155 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8163 // ignore white-space characters
8167 tape.no_valid_file = TRUE;
8169 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8177 if (tape.no_valid_file)
8180 tape.length_frames = GetTapeLengthFrames();
8181 tape.length_seconds = GetTapeLengthSeconds();
8184 void LoadTapeFromFilename(char *filename)
8186 char cookie[MAX_LINE_LEN];
8187 char chunk_name[CHUNK_ID_LEN + 1];
8191 // always start with reliable default values
8192 setTapeInfoToDefaults();
8194 if (strSuffix(filename, ".sln"))
8196 LoadTape_SokobanSolution(filename);
8201 if (!(file = openFile(filename, MODE_READ)))
8203 tape.no_valid_file = TRUE;
8208 getFileChunkBE(file, chunk_name, NULL);
8209 if (strEqual(chunk_name, "RND1"))
8211 getFile32BitBE(file); // not used
8213 getFileChunkBE(file, chunk_name, NULL);
8214 if (!strEqual(chunk_name, "TAPE"))
8216 tape.no_valid_file = TRUE;
8218 Warn("unknown format of tape file '%s'", filename);
8225 else // check for pre-2.0 file format with cookie string
8227 strcpy(cookie, chunk_name);
8228 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8230 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8231 cookie[strlen(cookie) - 1] = '\0';
8233 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8235 tape.no_valid_file = TRUE;
8237 Warn("unknown format of tape file '%s'", filename);
8244 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8246 tape.no_valid_file = TRUE;
8248 Warn("unsupported version of tape file '%s'", filename);
8255 // pre-2.0 tape files have no game version, so use file version here
8256 tape.game_version = tape.file_version;
8259 if (tape.file_version < FILE_VERSION_1_2)
8261 // tape files from versions before 1.2.0 without chunk structure
8262 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8263 LoadTape_BODY(file, 2 * tape.length, &tape);
8271 int (*loader)(File *, int, struct TapeInfo *);
8275 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8276 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8277 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8278 { "INFO", -1, LoadTape_INFO },
8279 { "BODY", -1, LoadTape_BODY },
8283 while (getFileChunkBE(file, chunk_name, &chunk_size))
8287 while (chunk_info[i].name != NULL &&
8288 !strEqual(chunk_name, chunk_info[i].name))
8291 if (chunk_info[i].name == NULL)
8293 Warn("unknown chunk '%s' in tape file '%s'",
8294 chunk_name, filename);
8296 ReadUnusedBytesFromFile(file, chunk_size);
8298 else if (chunk_info[i].size != -1 &&
8299 chunk_info[i].size != chunk_size)
8301 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8302 chunk_size, chunk_name, filename);
8304 ReadUnusedBytesFromFile(file, chunk_size);
8308 // call function to load this tape chunk
8309 int chunk_size_expected =
8310 (chunk_info[i].loader)(file, chunk_size, &tape);
8312 // the size of some chunks cannot be checked before reading other
8313 // chunks first (like "HEAD" and "BODY") that contain some header
8314 // information, so check them here
8315 if (chunk_size_expected != chunk_size)
8317 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8318 chunk_size, chunk_name, filename);
8326 tape.length_frames = GetTapeLengthFrames();
8327 tape.length_seconds = GetTapeLengthSeconds();
8330 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8332 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8334 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8335 tape.engine_version);
8339 void LoadTape(int nr)
8341 char *filename = getTapeFilename(nr);
8343 LoadTapeFromFilename(filename);
8346 void LoadSolutionTape(int nr)
8348 char *filename = getSolutionTapeFilename(nr);
8350 LoadTapeFromFilename(filename);
8352 if (TAPE_IS_EMPTY(tape) &&
8353 level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8354 level.native_sp_level->demo.is_available)
8355 CopyNativeTape_SP_to_RND(&level);
8358 void LoadScoreTape(char *score_tape_basename, int nr)
8360 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8362 LoadTapeFromFilename(filename);
8365 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8367 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8369 LoadTapeFromFilename(filename);
8372 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8374 // chunk required for team mode tapes with non-default screen size
8375 return (tape->num_participating_players > 1 &&
8376 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8377 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8380 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8382 putFileVersion(file, tape->file_version);
8383 putFileVersion(file, tape->game_version);
8386 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8389 byte store_participating_players = 0;
8391 // set bits for participating players for compact storage
8392 for (i = 0; i < MAX_PLAYERS; i++)
8393 if (tape->player_participates[i])
8394 store_participating_players |= (1 << i);
8396 putFile32BitBE(file, tape->random_seed);
8397 putFile32BitBE(file, tape->date);
8398 putFile32BitBE(file, tape->length);
8400 putFile8Bit(file, store_participating_players);
8402 putFile8Bit(file, getTapeActionValue(tape));
8404 putFile8Bit(file, tape->property_bits);
8406 // unused bytes not at the end here for 4-byte alignment of engine_version
8407 WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
8409 putFileVersion(file, tape->engine_version);
8412 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8414 putFile8Bit(file, tape->scr_fieldx);
8415 putFile8Bit(file, tape->scr_fieldy);
8418 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8420 int level_identifier_size = strlen(tape->level_identifier) + 1;
8423 putFile16BitBE(file, level_identifier_size);
8425 for (i = 0; i < level_identifier_size; i++)
8426 putFile8Bit(file, tape->level_identifier[i]);
8428 putFile16BitBE(file, tape->level_nr);
8431 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8435 for (i = 0; i < tape->length; i++)
8437 if (tape->use_key_actions)
8439 for (j = 0; j < MAX_PLAYERS; j++)
8440 if (tape->player_participates[j])
8441 putFile8Bit(file, tape->pos[i].action[j]);
8444 if (tape->use_mouse_actions)
8446 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8447 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8448 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8451 putFile8Bit(file, tape->pos[i].delay);
8455 void SaveTapeToFilename(char *filename)
8459 int info_chunk_size;
8460 int body_chunk_size;
8462 if (!(file = fopen(filename, MODE_WRITE)))
8464 Warn("cannot save level recording file '%s'", filename);
8469 tape_pos_size = getTapePosSize(&tape);
8471 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8472 body_chunk_size = tape_pos_size * tape.length;
8474 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8475 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8477 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8478 SaveTape_VERS(file, &tape);
8480 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8481 SaveTape_HEAD(file, &tape);
8483 if (checkSaveTape_SCRN(&tape))
8485 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8486 SaveTape_SCRN(file, &tape);
8489 putFileChunkBE(file, "INFO", info_chunk_size);
8490 SaveTape_INFO(file, &tape);
8492 putFileChunkBE(file, "BODY", body_chunk_size);
8493 SaveTape_BODY(file, &tape);
8497 SetFilePermissions(filename, PERMS_PRIVATE);
8500 static void SaveTapeExt(char *filename)
8504 tape.file_version = FILE_VERSION_ACTUAL;
8505 tape.game_version = GAME_VERSION_ACTUAL;
8507 tape.num_participating_players = 0;
8509 // count number of participating players
8510 for (i = 0; i < MAX_PLAYERS; i++)
8511 if (tape.player_participates[i])
8512 tape.num_participating_players++;
8514 SaveTapeToFilename(filename);
8516 tape.changed = FALSE;
8519 void SaveTape(int nr)
8521 char *filename = getTapeFilename(nr);
8523 InitTapeDirectory(leveldir_current->subdir);
8525 SaveTapeExt(filename);
8528 void SaveScoreTape(int nr)
8530 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8532 // used instead of "leveldir_current->subdir" (for network games)
8533 InitScoreTapeDirectory(levelset.identifier, nr);
8535 SaveTapeExt(filename);
8538 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8539 unsigned int req_state_added)
8541 char *filename = getTapeFilename(nr);
8542 boolean new_tape = !fileExists(filename);
8543 boolean tape_saved = FALSE;
8545 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8550 Request(msg_saved, REQ_CONFIRM | req_state_added);
8558 boolean SaveTapeChecked(int nr)
8560 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8563 boolean SaveTapeChecked_LevelSolved(int nr)
8565 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8566 "Level solved! Tape saved!", REQ_STAY_OPEN);
8569 void DumpTape(struct TapeInfo *tape)
8571 int tape_frame_counter;
8574 if (tape->no_valid_file)
8576 Warn("cannot dump -- no valid tape file found");
8583 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8584 tape->level_nr, tape->file_version, tape->game_version);
8585 Print(" (effective engine version %08d)\n",
8586 tape->engine_version);
8587 Print("Level series identifier: '%s'\n", tape->level_identifier);
8589 Print("Special tape properties: ");
8590 if (tape->property_bits == TAPE_PROPERTY_NONE)
8592 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8593 Print("[em_random_bug]");
8594 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8595 Print("[game_speed]");
8596 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8598 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8599 Print("[single_step]");
8600 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8601 Print("[snapshot]");
8602 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8603 Print("[replayed]");
8604 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8605 Print("[tas_keys]");
8606 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8607 Print("[small_graphics]");
8610 int year2 = tape->date / 10000;
8611 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8612 int month_index_raw = (tape->date / 100) % 100;
8613 int month_index = month_index_raw % 12; // prevent invalid index
8614 int month = month_index + 1;
8615 int day = tape->date % 100;
8617 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8621 tape_frame_counter = 0;
8623 for (i = 0; i < tape->length; i++)
8625 if (i >= MAX_TAPE_LEN)
8630 for (j = 0; j < MAX_PLAYERS; j++)
8632 if (tape->player_participates[j])
8634 int action = tape->pos[i].action[j];
8636 Print("%d:%02x ", j, action);
8637 Print("[%c%c%c%c|%c%c] - ",
8638 (action & JOY_LEFT ? '<' : ' '),
8639 (action & JOY_RIGHT ? '>' : ' '),
8640 (action & JOY_UP ? '^' : ' '),
8641 (action & JOY_DOWN ? 'v' : ' '),
8642 (action & JOY_BUTTON_1 ? '1' : ' '),
8643 (action & JOY_BUTTON_2 ? '2' : ' '));
8647 Print("(%03d) ", tape->pos[i].delay);
8648 Print("[%05d]\n", tape_frame_counter);
8650 tape_frame_counter += tape->pos[i].delay;
8656 void DumpTapes(void)
8658 static LevelDirTree *dumptape_leveldir = NULL;
8660 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8661 global.dumptape_leveldir);
8663 if (dumptape_leveldir == NULL)
8664 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
8666 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
8667 global.dumptape_level_nr > dumptape_leveldir->last_level)
8668 Fail("no such level number: %d", global.dumptape_level_nr);
8670 leveldir_current = dumptape_leveldir;
8672 if (options.mytapes)
8673 LoadTape(global.dumptape_level_nr);
8675 LoadSolutionTape(global.dumptape_level_nr);
8683 // ============================================================================
8684 // score file functions
8685 // ============================================================================
8687 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
8691 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8693 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
8694 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
8695 scores->entry[i].score = 0;
8696 scores->entry[i].time = 0;
8698 scores->entry[i].id = -1;
8699 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
8700 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
8701 strcpy(scores->entry[i].version, UNKNOWN_NAME);
8702 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
8703 strcpy(scores->entry[i].country_code, "??");
8706 scores->num_entries = 0;
8707 scores->last_added = -1;
8708 scores->last_added_local = -1;
8710 scores->updated = FALSE;
8711 scores->uploaded = FALSE;
8712 scores->tape_downloaded = FALSE;
8713 scores->force_last_added = FALSE;
8715 // The following values are intentionally not reset here:
8719 // - continue_playing
8720 // - continue_on_return
8723 static void setScoreInfoToDefaults(void)
8725 setScoreInfoToDefaultsExt(&scores);
8728 static void setServerScoreInfoToDefaults(void)
8730 setScoreInfoToDefaultsExt(&server_scores);
8733 static void LoadScore_OLD(int nr)
8736 char *filename = getScoreFilename(nr);
8737 char cookie[MAX_LINE_LEN];
8738 char line[MAX_LINE_LEN];
8742 if (!(file = fopen(filename, MODE_READ)))
8745 // check file identifier
8746 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8748 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8749 cookie[strlen(cookie) - 1] = '\0';
8751 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8753 Warn("unknown format of score file '%s'", filename);
8760 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8762 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
8763 Warn("fscanf() failed; %s", strerror(errno));
8765 if (fgets(line, MAX_LINE_LEN, file) == NULL)
8768 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8769 line[strlen(line) - 1] = '\0';
8771 for (line_ptr = line; *line_ptr; line_ptr++)
8773 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8775 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
8776 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8785 static void ConvertScore_OLD(void)
8787 // only convert score to time for levels that rate playing time over score
8788 if (!level.rate_time_over_score)
8791 // convert old score to playing time for score-less levels (like Supaplex)
8792 int time_final_max = 999;
8795 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8797 int score = scores.entry[i].score;
8799 if (score > 0 && score < time_final_max)
8800 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
8804 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
8806 scores->file_version = getFileVersion(file);
8807 scores->game_version = getFileVersion(file);
8812 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
8814 char *level_identifier = NULL;
8815 int level_identifier_size;
8818 level_identifier_size = getFile16BitBE(file);
8820 level_identifier = checked_malloc(level_identifier_size);
8822 for (i = 0; i < level_identifier_size; i++)
8823 level_identifier[i] = getFile8Bit(file);
8825 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
8826 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
8828 checked_free(level_identifier);
8830 scores->level_nr = getFile16BitBE(file);
8831 scores->num_entries = getFile16BitBE(file);
8833 chunk_size = 2 + level_identifier_size + 2 + 2;
8838 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
8842 for (i = 0; i < scores->num_entries; i++)
8844 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
8845 scores->entry[i].name[j] = getFile8Bit(file);
8847 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
8850 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
8855 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
8859 for (i = 0; i < scores->num_entries; i++)
8860 scores->entry[i].score = getFile16BitBE(file);
8862 chunk_size = scores->num_entries * 2;
8867 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
8871 for (i = 0; i < scores->num_entries; i++)
8872 scores->entry[i].score = getFile32BitBE(file);
8874 chunk_size = scores->num_entries * 4;
8879 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
8883 for (i = 0; i < scores->num_entries; i++)
8884 scores->entry[i].time = getFile32BitBE(file);
8886 chunk_size = scores->num_entries * 4;
8891 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
8895 for (i = 0; i < scores->num_entries; i++)
8897 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
8898 scores->entry[i].tape_basename[j] = getFile8Bit(file);
8900 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
8903 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
8908 void LoadScore(int nr)
8910 char *filename = getScoreFilename(nr);
8911 char cookie[MAX_LINE_LEN];
8912 char chunk_name[CHUNK_ID_LEN + 1];
8914 boolean old_score_file_format = FALSE;
8917 // always start with reliable default values
8918 setScoreInfoToDefaults();
8920 if (!(file = openFile(filename, MODE_READ)))
8923 getFileChunkBE(file, chunk_name, NULL);
8924 if (strEqual(chunk_name, "RND1"))
8926 getFile32BitBE(file); // not used
8928 getFileChunkBE(file, chunk_name, NULL);
8929 if (!strEqual(chunk_name, "SCOR"))
8931 Warn("unknown format of score file '%s'", filename);
8938 else // check for old file format with cookie string
8940 strcpy(cookie, chunk_name);
8941 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8943 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8944 cookie[strlen(cookie) - 1] = '\0';
8946 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
8948 Warn("unknown format of score file '%s'", filename);
8955 old_score_file_format = TRUE;
8958 if (old_score_file_format)
8960 // score files from versions before 4.2.4.0 without chunk structure
8963 // convert score to time, if possible (mainly for Supaplex levels)
8972 int (*loader)(File *, int, struct ScoreInfo *);
8976 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
8977 { "INFO", -1, LoadScore_INFO },
8978 { "NAME", -1, LoadScore_NAME },
8979 { "SCOR", -1, LoadScore_SCOR },
8980 { "SC4R", -1, LoadScore_SC4R },
8981 { "TIME", -1, LoadScore_TIME },
8982 { "TAPE", -1, LoadScore_TAPE },
8987 while (getFileChunkBE(file, chunk_name, &chunk_size))
8991 while (chunk_info[i].name != NULL &&
8992 !strEqual(chunk_name, chunk_info[i].name))
8995 if (chunk_info[i].name == NULL)
8997 Warn("unknown chunk '%s' in score file '%s'",
8998 chunk_name, filename);
9000 ReadUnusedBytesFromFile(file, chunk_size);
9002 else if (chunk_info[i].size != -1 &&
9003 chunk_info[i].size != chunk_size)
9005 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9006 chunk_size, chunk_name, filename);
9008 ReadUnusedBytesFromFile(file, chunk_size);
9012 // call function to load this score chunk
9013 int chunk_size_expected =
9014 (chunk_info[i].loader)(file, chunk_size, &scores);
9016 // the size of some chunks cannot be checked before reading other
9017 // chunks first (like "HEAD" and "BODY") that contain some header
9018 // information, so check them here
9019 if (chunk_size_expected != chunk_size)
9021 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9022 chunk_size, chunk_name, filename);
9031 #if ENABLE_HISTORIC_CHUNKS
9032 void SaveScore_OLD(int nr)
9035 char *filename = getScoreFilename(nr);
9038 // used instead of "leveldir_current->subdir" (for network games)
9039 InitScoreDirectory(levelset.identifier);
9041 if (!(file = fopen(filename, MODE_WRITE)))
9043 Warn("cannot save score for level %d", nr);
9048 fprintf(file, "%s\n\n", SCORE_COOKIE);
9050 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9051 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9055 SetFilePermissions(filename, PERMS_PRIVATE);
9059 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9061 putFileVersion(file, scores->file_version);
9062 putFileVersion(file, scores->game_version);
9065 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9067 int level_identifier_size = strlen(scores->level_identifier) + 1;
9070 putFile16BitBE(file, level_identifier_size);
9072 for (i = 0; i < level_identifier_size; i++)
9073 putFile8Bit(file, scores->level_identifier[i]);
9075 putFile16BitBE(file, scores->level_nr);
9076 putFile16BitBE(file, scores->num_entries);
9079 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9083 for (i = 0; i < scores->num_entries; i++)
9085 int name_size = strlen(scores->entry[i].name);
9087 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9088 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9092 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9096 for (i = 0; i < scores->num_entries; i++)
9097 putFile16BitBE(file, scores->entry[i].score);
9100 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9104 for (i = 0; i < scores->num_entries; i++)
9105 putFile32BitBE(file, scores->entry[i].score);
9108 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9112 for (i = 0; i < scores->num_entries; i++)
9113 putFile32BitBE(file, scores->entry[i].time);
9116 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9120 for (i = 0; i < scores->num_entries; i++)
9122 int size = strlen(scores->entry[i].tape_basename);
9124 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9125 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9129 static void SaveScoreToFilename(char *filename)
9132 int info_chunk_size;
9133 int name_chunk_size;
9134 int scor_chunk_size;
9135 int sc4r_chunk_size;
9136 int time_chunk_size;
9137 int tape_chunk_size;
9138 boolean has_large_score_values;
9141 if (!(file = fopen(filename, MODE_WRITE)))
9143 Warn("cannot save score file '%s'", filename);
9148 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9149 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9150 scor_chunk_size = scores.num_entries * 2;
9151 sc4r_chunk_size = scores.num_entries * 4;
9152 time_chunk_size = scores.num_entries * 4;
9153 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9155 has_large_score_values = FALSE;
9156 for (i = 0; i < scores.num_entries; i++)
9157 if (scores.entry[i].score > 0xffff)
9158 has_large_score_values = TRUE;
9160 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9161 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9163 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9164 SaveScore_VERS(file, &scores);
9166 putFileChunkBE(file, "INFO", info_chunk_size);
9167 SaveScore_INFO(file, &scores);
9169 putFileChunkBE(file, "NAME", name_chunk_size);
9170 SaveScore_NAME(file, &scores);
9172 if (has_large_score_values)
9174 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9175 SaveScore_SC4R(file, &scores);
9179 putFileChunkBE(file, "SCOR", scor_chunk_size);
9180 SaveScore_SCOR(file, &scores);
9183 putFileChunkBE(file, "TIME", time_chunk_size);
9184 SaveScore_TIME(file, &scores);
9186 putFileChunkBE(file, "TAPE", tape_chunk_size);
9187 SaveScore_TAPE(file, &scores);
9191 SetFilePermissions(filename, PERMS_PRIVATE);
9194 void SaveScore(int nr)
9196 char *filename = getScoreFilename(nr);
9199 // used instead of "leveldir_current->subdir" (for network games)
9200 InitScoreDirectory(levelset.identifier);
9202 scores.file_version = FILE_VERSION_ACTUAL;
9203 scores.game_version = GAME_VERSION_ACTUAL;
9205 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9206 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9207 scores.level_nr = level_nr;
9209 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9210 if (scores.entry[i].score == 0 &&
9211 scores.entry[i].time == 0 &&
9212 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9215 scores.num_entries = i;
9217 if (scores.num_entries == 0)
9220 SaveScoreToFilename(filename);
9223 static void LoadServerScoreFromCache(int nr)
9225 struct ScoreEntry score_entry;
9234 { &score_entry.score, FALSE, 0 },
9235 { &score_entry.time, FALSE, 0 },
9236 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9237 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9238 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9239 { &score_entry.id, FALSE, 0 },
9240 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9241 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9242 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9243 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9247 char *filename = getScoreCacheFilename(nr);
9248 SetupFileHash *score_hash = loadSetupFileHash(filename);
9251 server_scores.num_entries = 0;
9253 if (score_hash == NULL)
9256 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9258 score_entry = server_scores.entry[i];
9260 for (j = 0; score_mapping[j].value != NULL; j++)
9264 sprintf(token, "%02d.%d", i, j);
9266 char *value = getHashEntry(score_hash, token);
9271 if (score_mapping[j].is_string)
9273 char *score_value = (char *)score_mapping[j].value;
9274 int value_size = score_mapping[j].string_size;
9276 strncpy(score_value, value, value_size);
9277 score_value[value_size] = '\0';
9281 int *score_value = (int *)score_mapping[j].value;
9283 *score_value = atoi(value);
9286 server_scores.num_entries = i + 1;
9289 server_scores.entry[i] = score_entry;
9292 freeSetupFileHash(score_hash);
9295 void LoadServerScore(int nr, boolean download_score)
9297 if (!setup.use_api_server)
9300 // always start with reliable default values
9301 setServerScoreInfoToDefaults();
9303 // 1st step: load server scores from cache file (which may not exist)
9304 // (this should prevent reading it while the thread is writing to it)
9305 LoadServerScoreFromCache(nr);
9307 if (download_score && runtime.use_api_server)
9309 // 2nd step: download server scores from score server to cache file
9310 // (as thread, as it might time out if the server is not reachable)
9311 ApiGetScoreAsThread(nr);
9315 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9317 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9319 // if score tape not uploaded, ask for uploading missing tapes later
9320 if (!setup.has_remaining_tapes)
9321 setup.ask_for_remaining_tapes = TRUE;
9323 setup.provide_uploading_tapes = TRUE;
9324 setup.has_remaining_tapes = TRUE;
9326 SaveSetup_ServerSetup();
9329 void SaveServerScore(int nr, boolean tape_saved)
9331 if (!runtime.use_api_server)
9333 PrepareScoreTapesForUpload(leveldir_current->subdir);
9338 ApiAddScoreAsThread(nr, tape_saved, NULL);
9341 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9342 char *score_tape_filename)
9344 if (!runtime.use_api_server)
9347 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9350 void LoadLocalAndServerScore(int nr, boolean download_score)
9352 int last_added_local = scores.last_added_local;
9353 boolean force_last_added = scores.force_last_added;
9355 // needed if only showing server scores
9356 setScoreInfoToDefaults();
9358 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9361 // restore last added local score entry (before merging server scores)
9362 scores.last_added = scores.last_added_local = last_added_local;
9364 if (setup.use_api_server &&
9365 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9367 // load server scores from cache file and trigger update from server
9368 LoadServerScore(nr, download_score);
9370 // merge local scores with scores from server
9374 if (force_last_added)
9375 scores.force_last_added = force_last_added;
9379 // ============================================================================
9380 // setup file functions
9381 // ============================================================================
9383 #define TOKEN_STR_PLAYER_PREFIX "player_"
9386 static struct TokenInfo global_setup_tokens[] =
9390 &setup.player_name, "player_name"
9394 &setup.multiple_users, "multiple_users"
9398 &setup.sound, "sound"
9402 &setup.sound_loops, "repeating_sound_loops"
9406 &setup.sound_music, "background_music"
9410 &setup.sound_simple, "simple_sound_effects"
9414 &setup.toons, "toons"
9418 &setup.scroll_delay, "scroll_delay"
9422 &setup.forced_scroll_delay, "forced_scroll_delay"
9426 &setup.scroll_delay_value, "scroll_delay_value"
9430 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9434 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9438 &setup.fade_screens, "fade_screens"
9442 &setup.autorecord, "automatic_tape_recording"
9446 &setup.auto_pause_on_start, "auto_pause_on_start"
9450 &setup.show_titlescreen, "show_titlescreen"
9454 &setup.quick_doors, "quick_doors"
9458 &setup.team_mode, "team_mode"
9462 &setup.handicap, "handicap"
9466 &setup.skip_levels, "skip_levels"
9470 &setup.increment_levels, "increment_levels"
9474 &setup.auto_play_next_level, "auto_play_next_level"
9478 &setup.count_score_after_game, "count_score_after_game"
9482 &setup.show_scores_after_game, "show_scores_after_game"
9486 &setup.time_limit, "time_limit"
9490 &setup.fullscreen, "fullscreen"
9494 &setup.window_scaling_percent, "window_scaling_percent"
9498 &setup.window_scaling_quality, "window_scaling_quality"
9502 &setup.screen_rendering_mode, "screen_rendering_mode"
9506 &setup.vsync_mode, "vsync_mode"
9510 &setup.ask_on_escape, "ask_on_escape"
9514 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9518 &setup.ask_on_game_over, "ask_on_game_over"
9522 &setup.ask_on_quit_game, "ask_on_quit_game"
9526 &setup.ask_on_quit_program, "ask_on_quit_program"
9530 &setup.quick_switch, "quick_player_switch"
9534 &setup.input_on_focus, "input_on_focus"
9538 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9542 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9546 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9550 &setup.game_speed_extended, "game_speed_extended"
9554 &setup.game_frame_delay, "game_frame_delay"
9558 &setup.sp_show_border_elements, "sp_show_border_elements"
9562 &setup.small_game_graphics, "small_game_graphics"
9566 &setup.show_load_save_buttons, "show_load_save_buttons"
9570 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9574 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9578 &setup.graphics_set, "graphics_set"
9582 &setup.sounds_set, "sounds_set"
9586 &setup.music_set, "music_set"
9590 &setup.override_level_graphics, "override_level_graphics"
9594 &setup.override_level_sounds, "override_level_sounds"
9598 &setup.override_level_music, "override_level_music"
9602 &setup.volume_simple, "volume_simple"
9606 &setup.volume_loops, "volume_loops"
9610 &setup.volume_music, "volume_music"
9614 &setup.network_mode, "network_mode"
9618 &setup.network_player_nr, "network_player"
9622 &setup.network_server_hostname, "network_server_hostname"
9626 &setup.touch.control_type, "touch.control_type"
9630 &setup.touch.move_distance, "touch.move_distance"
9634 &setup.touch.drop_distance, "touch.drop_distance"
9638 &setup.touch.transparency, "touch.transparency"
9642 &setup.touch.draw_outlined, "touch.draw_outlined"
9646 &setup.touch.draw_pressed, "touch.draw_pressed"
9650 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
9654 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
9658 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
9662 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
9666 &setup.touch.overlay_buttons, "touch.overlay_buttons"
9670 static struct TokenInfo auto_setup_tokens[] =
9674 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
9678 static struct TokenInfo server_setup_tokens[] =
9682 &setup.player_uuid, "player_uuid"
9686 &setup.player_version, "player_version"
9690 &setup.use_api_server, TEST_PREFIX "use_api_server"
9694 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
9698 &setup.api_server_password, TEST_PREFIX "api_server_password"
9702 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
9706 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
9710 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
9714 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
9718 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
9722 static struct TokenInfo editor_setup_tokens[] =
9726 &setup.editor.el_classic, "editor.el_classic"
9730 &setup.editor.el_custom, "editor.el_custom"
9734 &setup.editor.el_user_defined, "editor.el_user_defined"
9738 &setup.editor.el_dynamic, "editor.el_dynamic"
9742 &setup.editor.el_headlines, "editor.el_headlines"
9746 &setup.editor.show_element_token, "editor.show_element_token"
9750 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
9754 static struct TokenInfo editor_cascade_setup_tokens[] =
9758 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
9762 &setup.editor_cascade.el_em, "editor.cascade.el_em"
9766 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
9770 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
9774 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
9778 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
9782 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
9786 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
9790 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
9794 &setup.editor_cascade.el_df, "editor.cascade.el_df"
9798 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
9802 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
9806 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
9810 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
9814 &setup.editor_cascade.el_es, "editor.cascade.el_es"
9818 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
9822 &setup.editor_cascade.el_user, "editor.cascade.el_user"
9826 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
9830 static struct TokenInfo shortcut_setup_tokens[] =
9834 &setup.shortcut.save_game, "shortcut.save_game"
9838 &setup.shortcut.load_game, "shortcut.load_game"
9842 &setup.shortcut.restart_game, "shortcut.restart_game"
9846 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
9850 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
9854 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
9858 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
9862 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
9866 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
9870 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
9874 &setup.shortcut.tape_eject, "shortcut.tape_eject"
9878 &setup.shortcut.tape_extra, "shortcut.tape_extra"
9882 &setup.shortcut.tape_stop, "shortcut.tape_stop"
9886 &setup.shortcut.tape_pause, "shortcut.tape_pause"
9890 &setup.shortcut.tape_record, "shortcut.tape_record"
9894 &setup.shortcut.tape_play, "shortcut.tape_play"
9898 &setup.shortcut.sound_simple, "shortcut.sound_simple"
9902 &setup.shortcut.sound_loops, "shortcut.sound_loops"
9906 &setup.shortcut.sound_music, "shortcut.sound_music"
9910 &setup.shortcut.snap_left, "shortcut.snap_left"
9914 &setup.shortcut.snap_right, "shortcut.snap_right"
9918 &setup.shortcut.snap_up, "shortcut.snap_up"
9922 &setup.shortcut.snap_down, "shortcut.snap_down"
9926 static struct SetupInputInfo setup_input;
9927 static struct TokenInfo player_setup_tokens[] =
9931 &setup_input.use_joystick, ".use_joystick"
9935 &setup_input.joy.device_name, ".joy.device_name"
9939 &setup_input.joy.xleft, ".joy.xleft"
9943 &setup_input.joy.xmiddle, ".joy.xmiddle"
9947 &setup_input.joy.xright, ".joy.xright"
9951 &setup_input.joy.yupper, ".joy.yupper"
9955 &setup_input.joy.ymiddle, ".joy.ymiddle"
9959 &setup_input.joy.ylower, ".joy.ylower"
9963 &setup_input.joy.snap, ".joy.snap_field"
9967 &setup_input.joy.drop, ".joy.place_bomb"
9971 &setup_input.key.left, ".key.move_left"
9975 &setup_input.key.right, ".key.move_right"
9979 &setup_input.key.up, ".key.move_up"
9983 &setup_input.key.down, ".key.move_down"
9987 &setup_input.key.snap, ".key.snap_field"
9991 &setup_input.key.drop, ".key.place_bomb"
9995 static struct TokenInfo system_setup_tokens[] =
9999 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10003 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10007 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10011 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10015 static struct TokenInfo internal_setup_tokens[] =
10019 &setup.internal.program_title, "program_title"
10023 &setup.internal.program_version, "program_version"
10027 &setup.internal.program_author, "program_author"
10031 &setup.internal.program_email, "program_email"
10035 &setup.internal.program_website, "program_website"
10039 &setup.internal.program_copyright, "program_copyright"
10043 &setup.internal.program_company, "program_company"
10047 &setup.internal.program_icon_file, "program_icon_file"
10051 &setup.internal.default_graphics_set, "default_graphics_set"
10055 &setup.internal.default_sounds_set, "default_sounds_set"
10059 &setup.internal.default_music_set, "default_music_set"
10063 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10067 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10071 &setup.internal.fallback_music_file, "fallback_music_file"
10075 &setup.internal.default_level_series, "default_level_series"
10079 &setup.internal.default_window_width, "default_window_width"
10083 &setup.internal.default_window_height, "default_window_height"
10087 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10091 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10095 &setup.internal.create_user_levelset, "create_user_levelset"
10099 &setup.internal.menu_game, "menu_game"
10103 &setup.internal.menu_editor, "menu_editor"
10107 &setup.internal.menu_graphics, "menu_graphics"
10111 &setup.internal.menu_sound, "menu_sound"
10115 &setup.internal.menu_artwork, "menu_artwork"
10119 &setup.internal.menu_input, "menu_input"
10123 &setup.internal.menu_touch, "menu_touch"
10127 &setup.internal.menu_shortcuts, "menu_shortcuts"
10131 &setup.internal.menu_exit, "menu_exit"
10135 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10139 &setup.internal.info_title, "info_title"
10143 &setup.internal.info_elements, "info_elements"
10147 &setup.internal.info_music, "info_music"
10151 &setup.internal.info_credits, "info_credits"
10155 &setup.internal.info_program, "info_program"
10159 &setup.internal.info_version, "info_version"
10163 &setup.internal.info_levelset, "info_levelset"
10167 &setup.internal.info_exit, "info_exit"
10171 static struct TokenInfo debug_setup_tokens[] =
10175 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10179 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10183 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10187 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10191 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10195 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10199 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10203 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10207 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10211 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10215 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10219 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10223 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10227 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10231 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10235 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10239 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10243 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10247 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10251 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10255 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10258 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10262 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10266 &setup.debug.xsn_mode, "debug.xsn_mode"
10270 &setup.debug.xsn_percent, "debug.xsn_percent"
10274 static struct TokenInfo options_setup_tokens[] =
10278 &setup.options.verbose, "options.verbose"
10282 static void setSetupInfoToDefaults(struct SetupInfo *si)
10286 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10288 si->multiple_users = TRUE;
10291 si->sound_loops = TRUE;
10292 si->sound_music = TRUE;
10293 si->sound_simple = TRUE;
10295 si->scroll_delay = TRUE;
10296 si->forced_scroll_delay = FALSE;
10297 si->scroll_delay_value = STD_SCROLL_DELAY;
10298 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10299 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10300 si->fade_screens = TRUE;
10301 si->autorecord = TRUE;
10302 si->auto_pause_on_start = FALSE;
10303 si->show_titlescreen = TRUE;
10304 si->quick_doors = FALSE;
10305 si->team_mode = FALSE;
10306 si->handicap = TRUE;
10307 si->skip_levels = TRUE;
10308 si->increment_levels = TRUE;
10309 si->auto_play_next_level = TRUE;
10310 si->count_score_after_game = TRUE;
10311 si->show_scores_after_game = TRUE;
10312 si->time_limit = TRUE;
10313 si->fullscreen = FALSE;
10314 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10315 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10316 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10317 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10318 si->ask_on_escape = TRUE;
10319 si->ask_on_escape_editor = TRUE;
10320 si->ask_on_game_over = TRUE;
10321 si->ask_on_quit_game = TRUE;
10322 si->ask_on_quit_program = TRUE;
10323 si->quick_switch = FALSE;
10324 si->input_on_focus = FALSE;
10325 si->prefer_aga_graphics = TRUE;
10326 si->prefer_lowpass_sounds = FALSE;
10327 si->prefer_extra_panel_items = TRUE;
10328 si->game_speed_extended = FALSE;
10329 si->game_frame_delay = GAME_FRAME_DELAY;
10330 si->sp_show_border_elements = FALSE;
10331 si->small_game_graphics = FALSE;
10332 si->show_load_save_buttons = FALSE;
10333 si->show_undo_redo_buttons = FALSE;
10334 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10336 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10337 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10338 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10340 si->override_level_graphics = FALSE;
10341 si->override_level_sounds = FALSE;
10342 si->override_level_music = FALSE;
10344 si->volume_simple = 100; // percent
10345 si->volume_loops = 100; // percent
10346 si->volume_music = 100; // percent
10348 si->network_mode = FALSE;
10349 si->network_player_nr = 0; // first player
10350 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10352 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10353 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10354 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10355 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10356 si->touch.draw_outlined = TRUE;
10357 si->touch.draw_pressed = TRUE;
10359 for (i = 0; i < 2; i++)
10361 char *default_grid_button[6][2] =
10367 { "111222", " vv " },
10368 { "111222", " vv " }
10370 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10371 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10372 int min_xsize = MIN(6, grid_xsize);
10373 int min_ysize = MIN(6, grid_ysize);
10374 int startx = grid_xsize - min_xsize;
10375 int starty = grid_ysize - min_ysize;
10378 // virtual buttons grid can only be set to defaults if video is initialized
10379 // (this will be repeated if virtual buttons are not loaded from setup file)
10380 if (video.initialized)
10382 si->touch.grid_xsize[i] = grid_xsize;
10383 si->touch.grid_ysize[i] = grid_ysize;
10387 si->touch.grid_xsize[i] = -1;
10388 si->touch.grid_ysize[i] = -1;
10391 for (x = 0; x < MAX_GRID_XSIZE; x++)
10392 for (y = 0; y < MAX_GRID_YSIZE; y++)
10393 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10395 for (x = 0; x < min_xsize; x++)
10396 for (y = 0; y < min_ysize; y++)
10397 si->touch.grid_button[i][x][starty + y] =
10398 default_grid_button[y][0][x];
10400 for (x = 0; x < min_xsize; x++)
10401 for (y = 0; y < min_ysize; y++)
10402 si->touch.grid_button[i][startx + x][starty + y] =
10403 default_grid_button[y][1][x];
10406 si->touch.grid_initialized = video.initialized;
10408 si->touch.overlay_buttons = FALSE;
10410 si->editor.el_boulderdash = TRUE;
10411 si->editor.el_emerald_mine = TRUE;
10412 si->editor.el_emerald_mine_club = TRUE;
10413 si->editor.el_more = TRUE;
10414 si->editor.el_sokoban = TRUE;
10415 si->editor.el_supaplex = TRUE;
10416 si->editor.el_diamond_caves = TRUE;
10417 si->editor.el_dx_boulderdash = TRUE;
10419 si->editor.el_mirror_magic = TRUE;
10420 si->editor.el_deflektor = TRUE;
10422 si->editor.el_chars = TRUE;
10423 si->editor.el_steel_chars = TRUE;
10425 si->editor.el_classic = TRUE;
10426 si->editor.el_custom = TRUE;
10428 si->editor.el_user_defined = FALSE;
10429 si->editor.el_dynamic = TRUE;
10431 si->editor.el_headlines = TRUE;
10433 si->editor.show_element_token = FALSE;
10435 si->editor.show_read_only_warning = TRUE;
10437 si->editor.use_template_for_new_levels = TRUE;
10439 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10440 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10441 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10442 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10443 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10445 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10446 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10447 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10448 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10449 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10451 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10452 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10453 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10454 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10455 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10456 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10458 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10459 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10460 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10462 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10463 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10464 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10465 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10467 for (i = 0; i < MAX_PLAYERS; i++)
10469 si->input[i].use_joystick = FALSE;
10470 si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
10471 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10472 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10473 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10474 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10475 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10476 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10477 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10478 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10479 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10480 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10481 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10482 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10483 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10484 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10487 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10488 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10489 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10490 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10492 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10493 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10494 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10495 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10496 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10497 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10498 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10500 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10502 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10503 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10504 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10506 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10507 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10508 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10510 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10511 si->internal.choose_from_top_leveldir = FALSE;
10512 si->internal.show_scaling_in_title = TRUE;
10513 si->internal.create_user_levelset = TRUE;
10515 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10516 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10518 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10519 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10520 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10521 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10522 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10523 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10524 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10525 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10526 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10527 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10529 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10530 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10531 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10532 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10533 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10534 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10535 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10536 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10537 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10538 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10540 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10541 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10543 si->debug.show_frames_per_second = FALSE;
10545 si->debug.xsn_mode = AUTO;
10546 si->debug.xsn_percent = 0;
10548 si->options.verbose = FALSE;
10550 #if defined(PLATFORM_ANDROID)
10551 si->fullscreen = TRUE;
10552 si->touch.overlay_buttons = TRUE;
10555 setHideSetupEntry(&setup.debug.xsn_mode);
10558 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10560 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10563 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10565 si->player_uuid = NULL; // (will be set later)
10566 si->player_version = 1; // (will be set later)
10568 si->use_api_server = TRUE;
10569 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10570 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10571 si->ask_for_uploading_tapes = TRUE;
10572 si->ask_for_remaining_tapes = FALSE;
10573 si->provide_uploading_tapes = TRUE;
10574 si->ask_for_using_api_server = TRUE;
10575 si->has_remaining_tapes = FALSE;
10578 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10580 si->editor_cascade.el_bd = TRUE;
10581 si->editor_cascade.el_em = TRUE;
10582 si->editor_cascade.el_emc = TRUE;
10583 si->editor_cascade.el_rnd = TRUE;
10584 si->editor_cascade.el_sb = TRUE;
10585 si->editor_cascade.el_sp = TRUE;
10586 si->editor_cascade.el_dc = TRUE;
10587 si->editor_cascade.el_dx = TRUE;
10589 si->editor_cascade.el_mm = TRUE;
10590 si->editor_cascade.el_df = TRUE;
10592 si->editor_cascade.el_chars = FALSE;
10593 si->editor_cascade.el_steel_chars = FALSE;
10594 si->editor_cascade.el_ce = FALSE;
10595 si->editor_cascade.el_ge = FALSE;
10596 si->editor_cascade.el_es = FALSE;
10597 si->editor_cascade.el_ref = FALSE;
10598 si->editor_cascade.el_user = FALSE;
10599 si->editor_cascade.el_dynamic = FALSE;
10602 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
10604 static char *getHideSetupToken(void *setup_value)
10606 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
10608 if (setup_value != NULL)
10609 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
10611 return hide_setup_token;
10614 void setHideSetupEntry(void *setup_value)
10616 char *hide_setup_token = getHideSetupToken(setup_value);
10618 if (hide_setup_hash == NULL)
10619 hide_setup_hash = newSetupFileHash();
10621 if (setup_value != NULL)
10622 setHashEntry(hide_setup_hash, hide_setup_token, "");
10625 void removeHideSetupEntry(void *setup_value)
10627 char *hide_setup_token = getHideSetupToken(setup_value);
10629 if (setup_value != NULL)
10630 removeHashEntry(hide_setup_hash, hide_setup_token);
10633 boolean hideSetupEntry(void *setup_value)
10635 char *hide_setup_token = getHideSetupToken(setup_value);
10637 return (setup_value != NULL &&
10638 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
10641 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
10642 struct TokenInfo *token_info,
10643 int token_nr, char *token_text)
10645 char *token_hide_text = getStringCat2(token_text, ".hide");
10646 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
10648 // set the value of this setup option in the setup option structure
10649 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
10651 // check if this setup option should be hidden in the setup menu
10652 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
10653 setHideSetupEntry(token_info[token_nr].value);
10655 free(token_hide_text);
10658 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
10659 struct TokenInfo *token_info,
10662 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
10663 token_info[token_nr].text);
10666 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
10670 if (!setup_file_hash)
10673 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
10674 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
10676 setup.touch.grid_initialized = TRUE;
10677 for (i = 0; i < 2; i++)
10679 int grid_xsize = setup.touch.grid_xsize[i];
10680 int grid_ysize = setup.touch.grid_ysize[i];
10683 // if virtual buttons are not loaded from setup file, repeat initializing
10684 // virtual buttons grid with default values later when video is initialized
10685 if (grid_xsize == -1 ||
10688 setup.touch.grid_initialized = FALSE;
10693 for (y = 0; y < grid_ysize; y++)
10695 char token_string[MAX_LINE_LEN];
10697 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
10699 char *value_string = getHashEntry(setup_file_hash, token_string);
10701 if (value_string == NULL)
10704 for (x = 0; x < grid_xsize; x++)
10706 char c = value_string[x];
10708 setup.touch.grid_button[i][x][y] =
10709 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
10714 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
10715 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
10717 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
10718 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
10720 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
10724 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
10726 setup_input = setup.input[pnr];
10727 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
10729 char full_token[100];
10731 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
10732 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
10735 setup.input[pnr] = setup_input;
10738 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
10739 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
10741 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
10742 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
10744 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
10745 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
10747 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
10748 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
10750 setHideRelatedSetupEntries();
10753 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
10757 if (!setup_file_hash)
10760 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
10761 setSetupInfo(auto_setup_tokens, i,
10762 getHashEntry(setup_file_hash,
10763 auto_setup_tokens[i].text));
10766 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
10770 if (!setup_file_hash)
10773 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
10774 setSetupInfo(server_setup_tokens, i,
10775 getHashEntry(setup_file_hash,
10776 server_setup_tokens[i].text));
10779 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
10783 if (!setup_file_hash)
10786 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
10787 setSetupInfo(editor_cascade_setup_tokens, i,
10788 getHashEntry(setup_file_hash,
10789 editor_cascade_setup_tokens[i].text));
10792 void LoadUserNames(void)
10794 int last_user_nr = user.nr;
10797 if (global.user_names != NULL)
10799 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10800 checked_free(global.user_names[i]);
10802 checked_free(global.user_names);
10805 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
10807 for (i = 0; i < MAX_PLAYER_NAMES; i++)
10811 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
10813 if (setup_file_hash)
10815 char *player_name = getHashEntry(setup_file_hash, "player_name");
10817 global.user_names[i] = getFixedUserName(player_name);
10819 freeSetupFileHash(setup_file_hash);
10822 if (global.user_names[i] == NULL)
10823 global.user_names[i] = getStringCopy(getDefaultUserName(i));
10826 user.nr = last_user_nr;
10829 void LoadSetupFromFilename(char *filename)
10831 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
10833 if (setup_file_hash)
10835 decodeSetupFileHash_Default(setup_file_hash);
10837 freeSetupFileHash(setup_file_hash);
10841 Debug("setup", "using default setup values");
10845 static void LoadSetup_SpecialPostProcessing(void)
10847 char *player_name_new;
10849 // needed to work around problems with fixed length strings
10850 player_name_new = getFixedUserName(setup.player_name);
10851 free(setup.player_name);
10852 setup.player_name = player_name_new;
10854 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
10855 if (setup.scroll_delay == FALSE)
10857 setup.scroll_delay_value = MIN_SCROLL_DELAY;
10858 setup.scroll_delay = TRUE; // now always "on"
10861 // make sure that scroll delay value stays inside valid range
10862 setup.scroll_delay_value =
10863 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
10866 void LoadSetup_Default(void)
10870 // always start with reliable default values
10871 setSetupInfoToDefaults(&setup);
10873 // try to load setup values from default setup file
10874 filename = getDefaultSetupFilename();
10876 if (fileExists(filename))
10877 LoadSetupFromFilename(filename);
10879 // try to load setup values from platform setup file
10880 filename = getPlatformSetupFilename();
10882 if (fileExists(filename))
10883 LoadSetupFromFilename(filename);
10885 // try to load setup values from user setup file
10886 filename = getSetupFilename();
10888 LoadSetupFromFilename(filename);
10890 LoadSetup_SpecialPostProcessing();
10893 void LoadSetup_AutoSetup(void)
10895 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
10896 SetupFileHash *setup_file_hash = NULL;
10898 // always start with reliable default values
10899 setSetupInfoToDefaults_AutoSetup(&setup);
10901 setup_file_hash = loadSetupFileHash(filename);
10903 if (setup_file_hash)
10905 decodeSetupFileHash_AutoSetup(setup_file_hash);
10907 freeSetupFileHash(setup_file_hash);
10913 void LoadSetup_ServerSetup(void)
10915 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
10916 SetupFileHash *setup_file_hash = NULL;
10918 // always start with reliable default values
10919 setSetupInfoToDefaults_ServerSetup(&setup);
10921 setup_file_hash = loadSetupFileHash(filename);
10923 if (setup_file_hash)
10925 decodeSetupFileHash_ServerSetup(setup_file_hash);
10927 freeSetupFileHash(setup_file_hash);
10932 if (setup.player_uuid == NULL)
10934 // player UUID does not yet exist in setup file
10935 setup.player_uuid = getStringCopy(getUUID());
10936 setup.player_version = 2;
10938 SaveSetup_ServerSetup();
10942 void LoadSetup_EditorCascade(void)
10944 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
10945 SetupFileHash *setup_file_hash = NULL;
10947 // always start with reliable default values
10948 setSetupInfoToDefaults_EditorCascade(&setup);
10950 setup_file_hash = loadSetupFileHash(filename);
10952 if (setup_file_hash)
10954 decodeSetupFileHash_EditorCascade(setup_file_hash);
10956 freeSetupFileHash(setup_file_hash);
10962 void LoadSetup(void)
10964 LoadSetup_Default();
10965 LoadSetup_AutoSetup();
10966 LoadSetup_ServerSetup();
10967 LoadSetup_EditorCascade();
10970 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
10971 char *mapping_line)
10973 char mapping_guid[MAX_LINE_LEN];
10974 char *mapping_start, *mapping_end;
10976 // get GUID from game controller mapping line: copy complete line
10977 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
10978 mapping_guid[MAX_LINE_LEN - 1] = '\0';
10980 // get GUID from game controller mapping line: cut after GUID part
10981 mapping_start = strchr(mapping_guid, ',');
10982 if (mapping_start != NULL)
10983 *mapping_start = '\0';
10985 // cut newline from game controller mapping line
10986 mapping_end = strchr(mapping_line, '\n');
10987 if (mapping_end != NULL)
10988 *mapping_end = '\0';
10990 // add mapping entry to game controller mappings hash
10991 setHashEntry(mappings_hash, mapping_guid, mapping_line);
10994 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
10999 if (!(file = fopen(filename, MODE_READ)))
11001 Warn("cannot read game controller mappings file '%s'", filename);
11006 while (!feof(file))
11008 char line[MAX_LINE_LEN];
11010 if (!fgets(line, MAX_LINE_LEN, file))
11013 addGameControllerMappingToHash(mappings_hash, line);
11019 void SaveSetup_Default(void)
11021 char *filename = getSetupFilename();
11025 InitUserDataDirectory();
11027 if (!(file = fopen(filename, MODE_WRITE)))
11029 Warn("cannot write setup file '%s'", filename);
11034 fprintFileHeader(file, SETUP_FILENAME);
11036 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11038 // just to make things nicer :)
11039 if (global_setup_tokens[i].value == &setup.multiple_users ||
11040 global_setup_tokens[i].value == &setup.sound ||
11041 global_setup_tokens[i].value == &setup.graphics_set ||
11042 global_setup_tokens[i].value == &setup.volume_simple ||
11043 global_setup_tokens[i].value == &setup.network_mode ||
11044 global_setup_tokens[i].value == &setup.touch.control_type ||
11045 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11046 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11047 fprintf(file, "\n");
11049 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11052 for (i = 0; i < 2; i++)
11054 int grid_xsize = setup.touch.grid_xsize[i];
11055 int grid_ysize = setup.touch.grid_ysize[i];
11058 fprintf(file, "\n");
11060 for (y = 0; y < grid_ysize; y++)
11062 char token_string[MAX_LINE_LEN];
11063 char value_string[MAX_LINE_LEN];
11065 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11067 for (x = 0; x < grid_xsize; x++)
11069 char c = setup.touch.grid_button[i][x][y];
11071 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11074 value_string[grid_xsize] = '\0';
11076 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11080 fprintf(file, "\n");
11081 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11082 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11084 fprintf(file, "\n");
11085 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11086 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11088 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11092 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11093 fprintf(file, "\n");
11095 setup_input = setup.input[pnr];
11096 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11097 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11100 fprintf(file, "\n");
11101 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11102 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11104 // (internal setup values not saved to user setup file)
11106 fprintf(file, "\n");
11107 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11108 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11109 setup.debug.xsn_mode != AUTO)
11110 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11112 fprintf(file, "\n");
11113 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11114 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11118 SetFilePermissions(filename, PERMS_PRIVATE);
11121 void SaveSetup_AutoSetup(void)
11123 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11127 InitUserDataDirectory();
11129 if (!(file = fopen(filename, MODE_WRITE)))
11131 Warn("cannot write auto setup file '%s'", filename);
11138 fprintFileHeader(file, AUTOSETUP_FILENAME);
11140 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11141 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11145 SetFilePermissions(filename, PERMS_PRIVATE);
11150 void SaveSetup_ServerSetup(void)
11152 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11156 InitUserDataDirectory();
11158 if (!(file = fopen(filename, MODE_WRITE)))
11160 Warn("cannot write server setup file '%s'", filename);
11167 fprintFileHeader(file, SERVERSETUP_FILENAME);
11169 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11171 // just to make things nicer :)
11172 if (server_setup_tokens[i].value == &setup.use_api_server)
11173 fprintf(file, "\n");
11175 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11180 SetFilePermissions(filename, PERMS_PRIVATE);
11185 void SaveSetup_EditorCascade(void)
11187 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11191 InitUserDataDirectory();
11193 if (!(file = fopen(filename, MODE_WRITE)))
11195 Warn("cannot write editor cascade state file '%s'", filename);
11202 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11204 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11205 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11209 SetFilePermissions(filename, PERMS_PRIVATE);
11214 void SaveSetup(void)
11216 SaveSetup_Default();
11217 SaveSetup_AutoSetup();
11218 SaveSetup_ServerSetup();
11219 SaveSetup_EditorCascade();
11222 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11227 if (!(file = fopen(filename, MODE_WRITE)))
11229 Warn("cannot write game controller mappings file '%s'", filename);
11234 BEGIN_HASH_ITERATION(mappings_hash, itr)
11236 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11238 END_HASH_ITERATION(mappings_hash, itr)
11243 void SaveSetup_AddGameControllerMapping(char *mapping)
11245 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11246 SetupFileHash *mappings_hash = newSetupFileHash();
11248 InitUserDataDirectory();
11250 // load existing personal game controller mappings
11251 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11253 // add new mapping to personal game controller mappings
11254 addGameControllerMappingToHash(mappings_hash, mapping);
11256 // save updated personal game controller mappings
11257 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11259 freeSetupFileHash(mappings_hash);
11263 void LoadCustomElementDescriptions(void)
11265 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11266 SetupFileHash *setup_file_hash;
11269 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11271 if (element_info[i].custom_description != NULL)
11273 free(element_info[i].custom_description);
11274 element_info[i].custom_description = NULL;
11278 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11281 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11283 char *token = getStringCat2(element_info[i].token_name, ".name");
11284 char *value = getHashEntry(setup_file_hash, token);
11287 element_info[i].custom_description = getStringCopy(value);
11292 freeSetupFileHash(setup_file_hash);
11295 static int getElementFromToken(char *token)
11297 char *value = getHashEntry(element_token_hash, token);
11300 return atoi(value);
11302 Warn("unknown element token '%s'", token);
11304 return EL_UNDEFINED;
11307 void FreeGlobalAnimEventInfo(void)
11309 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11311 if (gaei->event_list == NULL)
11316 for (i = 0; i < gaei->num_event_lists; i++)
11318 checked_free(gaei->event_list[i]->event_value);
11319 checked_free(gaei->event_list[i]);
11322 checked_free(gaei->event_list);
11324 gaei->event_list = NULL;
11325 gaei->num_event_lists = 0;
11328 static int AddGlobalAnimEventList(void)
11330 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11331 int list_pos = gaei->num_event_lists++;
11333 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11334 sizeof(struct GlobalAnimEventListInfo *));
11336 gaei->event_list[list_pos] =
11337 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11339 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11341 gaeli->event_value = NULL;
11342 gaeli->num_event_values = 0;
11347 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11349 // do not add empty global animation events
11350 if (event_value == ANIM_EVENT_NONE)
11353 // if list position is undefined, create new list
11354 if (list_pos == ANIM_EVENT_UNDEFINED)
11355 list_pos = AddGlobalAnimEventList();
11357 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11358 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11359 int value_pos = gaeli->num_event_values++;
11361 gaeli->event_value = checked_realloc(gaeli->event_value,
11362 gaeli->num_event_values * sizeof(int *));
11364 gaeli->event_value[value_pos] = event_value;
11369 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11371 if (list_pos == ANIM_EVENT_UNDEFINED)
11372 return ANIM_EVENT_NONE;
11374 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11375 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11377 return gaeli->event_value[value_pos];
11380 int GetGlobalAnimEventValueCount(int list_pos)
11382 if (list_pos == ANIM_EVENT_UNDEFINED)
11385 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11386 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11388 return gaeli->num_event_values;
11391 // This function checks if a string <s> of the format "string1, string2, ..."
11392 // exactly contains a string <s_contained>.
11394 static boolean string_has_parameter(char *s, char *s_contained)
11398 if (s == NULL || s_contained == NULL)
11401 if (strlen(s_contained) > strlen(s))
11404 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11406 char next_char = s[strlen(s_contained)];
11408 // check if next character is delimiter or whitespace
11409 return (next_char == ',' || next_char == '\0' ||
11410 next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
11413 // check if string contains another parameter string after a comma
11414 substring = strchr(s, ',');
11415 if (substring == NULL) // string does not contain a comma
11418 // advance string pointer to next character after the comma
11421 // skip potential whitespaces after the comma
11422 while (*substring == ' ' || *substring == '\t')
11425 return string_has_parameter(substring, s_contained);
11428 static int get_anim_parameter_value(char *s)
11430 int event_value[] =
11438 char *pattern_1[] =
11446 char *pattern_2 = ".part_";
11447 char *matching_char = NULL;
11449 int pattern_1_len = 0;
11450 int result = ANIM_EVENT_NONE;
11453 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11455 matching_char = strstr(s_ptr, pattern_1[i]);
11456 pattern_1_len = strlen(pattern_1[i]);
11457 result = event_value[i];
11459 if (matching_char != NULL)
11463 if (matching_char == NULL)
11464 return ANIM_EVENT_NONE;
11466 s_ptr = matching_char + pattern_1_len;
11468 // check for main animation number ("anim_X" or "anim_XX")
11469 if (*s_ptr >= '0' && *s_ptr <= '9')
11471 int gic_anim_nr = (*s_ptr++ - '0');
11473 if (*s_ptr >= '0' && *s_ptr <= '9')
11474 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11476 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11477 return ANIM_EVENT_NONE;
11479 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11483 // invalid main animation number specified
11485 return ANIM_EVENT_NONE;
11488 // check for animation part number ("part_X" or "part_XX") (optional)
11489 if (strPrefix(s_ptr, pattern_2))
11491 s_ptr += strlen(pattern_2);
11493 if (*s_ptr >= '0' && *s_ptr <= '9')
11495 int gic_part_nr = (*s_ptr++ - '0');
11497 if (*s_ptr >= '0' && *s_ptr <= '9')
11498 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
11500 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
11501 return ANIM_EVENT_NONE;
11503 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
11507 // invalid animation part number specified
11509 return ANIM_EVENT_NONE;
11513 // discard result if next character is neither delimiter nor whitespace
11514 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11515 *s_ptr == ' ' || *s_ptr == '\t'))
11516 return ANIM_EVENT_NONE;
11521 static int get_anim_parameter_values(char *s)
11523 int list_pos = ANIM_EVENT_UNDEFINED;
11524 int event_value = ANIM_EVENT_DEFAULT;
11526 if (string_has_parameter(s, "any"))
11527 event_value |= ANIM_EVENT_ANY;
11529 if (string_has_parameter(s, "click:self") ||
11530 string_has_parameter(s, "click") ||
11531 string_has_parameter(s, "self"))
11532 event_value |= ANIM_EVENT_SELF;
11534 if (string_has_parameter(s, "unclick:any"))
11535 event_value |= ANIM_EVENT_UNCLICK_ANY;
11537 // if animation event found, add it to global animation event list
11538 if (event_value != ANIM_EVENT_NONE)
11539 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11543 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
11544 event_value = get_anim_parameter_value(s);
11546 // if animation event found, add it to global animation event list
11547 if (event_value != ANIM_EVENT_NONE)
11548 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
11550 // continue with next part of the string, starting with next comma
11551 s = strchr(s + 1, ',');
11557 static int get_anim_action_parameter_value(char *token)
11559 // check most common default case first to massively speed things up
11560 if (strEqual(token, ARG_UNDEFINED))
11561 return ANIM_EVENT_ACTION_NONE;
11563 int result = getImageIDFromToken(token);
11567 char *gfx_token = getStringCat2("gfx.", token);
11569 result = getImageIDFromToken(gfx_token);
11571 checked_free(gfx_token);
11576 Key key = getKeyFromX11KeyName(token);
11578 if (key != KSYM_UNDEFINED)
11579 result = -(int)key;
11586 result = get_hash_from_key(token); // unsigned int => int
11587 result = ABS(result); // may be negative now
11588 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
11590 setHashEntry(anim_url_hash, int2str(result, 0), token);
11595 result = ANIM_EVENT_ACTION_NONE;
11600 int get_parameter_value(char *value_raw, char *suffix, int type)
11602 char *value = getStringToLower(value_raw);
11603 int result = 0; // probably a save default value
11605 if (strEqual(suffix, ".direction"))
11607 result = (strEqual(value, "left") ? MV_LEFT :
11608 strEqual(value, "right") ? MV_RIGHT :
11609 strEqual(value, "up") ? MV_UP :
11610 strEqual(value, "down") ? MV_DOWN : MV_NONE);
11612 else if (strEqual(suffix, ".position"))
11614 result = (strEqual(value, "left") ? POS_LEFT :
11615 strEqual(value, "right") ? POS_RIGHT :
11616 strEqual(value, "top") ? POS_TOP :
11617 strEqual(value, "upper") ? POS_UPPER :
11618 strEqual(value, "middle") ? POS_MIDDLE :
11619 strEqual(value, "lower") ? POS_LOWER :
11620 strEqual(value, "bottom") ? POS_BOTTOM :
11621 strEqual(value, "any") ? POS_ANY :
11622 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
11624 else if (strEqual(suffix, ".align"))
11626 result = (strEqual(value, "left") ? ALIGN_LEFT :
11627 strEqual(value, "right") ? ALIGN_RIGHT :
11628 strEqual(value, "center") ? ALIGN_CENTER :
11629 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
11631 else if (strEqual(suffix, ".valign"))
11633 result = (strEqual(value, "top") ? VALIGN_TOP :
11634 strEqual(value, "bottom") ? VALIGN_BOTTOM :
11635 strEqual(value, "middle") ? VALIGN_MIDDLE :
11636 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
11638 else if (strEqual(suffix, ".anim_mode"))
11640 result = (string_has_parameter(value, "none") ? ANIM_NONE :
11641 string_has_parameter(value, "loop") ? ANIM_LOOP :
11642 string_has_parameter(value, "linear") ? ANIM_LINEAR :
11643 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
11644 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
11645 string_has_parameter(value, "random") ? ANIM_RANDOM :
11646 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
11647 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
11648 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
11649 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
11650 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
11651 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
11652 string_has_parameter(value, "centered") ? ANIM_CENTERED :
11653 string_has_parameter(value, "all") ? ANIM_ALL :
11654 string_has_parameter(value, "tiled") ? ANIM_TILED :
11657 if (string_has_parameter(value, "once"))
11658 result |= ANIM_ONCE;
11660 if (string_has_parameter(value, "reverse"))
11661 result |= ANIM_REVERSE;
11663 if (string_has_parameter(value, "opaque_player"))
11664 result |= ANIM_OPAQUE_PLAYER;
11666 if (string_has_parameter(value, "static_panel"))
11667 result |= ANIM_STATIC_PANEL;
11669 else if (strEqual(suffix, ".init_event") ||
11670 strEqual(suffix, ".anim_event"))
11672 result = get_anim_parameter_values(value);
11674 else if (strEqual(suffix, ".init_delay_action") ||
11675 strEqual(suffix, ".anim_delay_action") ||
11676 strEqual(suffix, ".post_delay_action") ||
11677 strEqual(suffix, ".init_event_action") ||
11678 strEqual(suffix, ".anim_event_action"))
11680 result = get_anim_action_parameter_value(value_raw);
11682 else if (strEqual(suffix, ".class"))
11684 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11685 get_hash_from_key(value));
11687 else if (strEqual(suffix, ".style"))
11689 result = STYLE_DEFAULT;
11691 if (string_has_parameter(value, "accurate_borders"))
11692 result |= STYLE_ACCURATE_BORDERS;
11694 if (string_has_parameter(value, "inner_corners"))
11695 result |= STYLE_INNER_CORNERS;
11697 if (string_has_parameter(value, "reverse"))
11698 result |= STYLE_REVERSE;
11700 if (string_has_parameter(value, "leftmost_position"))
11701 result |= STYLE_LEFTMOST_POSITION;
11703 if (string_has_parameter(value, "block_clicks"))
11704 result |= STYLE_BLOCK;
11706 if (string_has_parameter(value, "passthrough_clicks"))
11707 result |= STYLE_PASSTHROUGH;
11709 if (string_has_parameter(value, "multiple_actions"))
11710 result |= STYLE_MULTIPLE_ACTIONS;
11712 else if (strEqual(suffix, ".fade_mode"))
11714 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
11715 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
11716 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
11717 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
11718 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
11719 FADE_MODE_DEFAULT);
11721 else if (strEqual(suffix, ".auto_delay_unit"))
11723 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
11724 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
11725 AUTO_DELAY_UNIT_DEFAULT);
11727 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
11729 result = gfx.get_font_from_token_function(value);
11731 else // generic parameter of type integer or boolean
11733 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
11734 type == TYPE_INTEGER ? get_integer_from_string(value) :
11735 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
11736 ARG_UNDEFINED_VALUE);
11744 static int get_token_parameter_value(char *token, char *value_raw)
11748 if (token == NULL || value_raw == NULL)
11749 return ARG_UNDEFINED_VALUE;
11751 suffix = strrchr(token, '.');
11752 if (suffix == NULL)
11755 if (strEqual(suffix, ".element"))
11756 return getElementFromToken(value_raw);
11758 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
11759 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
11762 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
11763 boolean ignore_defaults)
11767 for (i = 0; image_config_vars[i].token != NULL; i++)
11769 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
11771 // (ignore definitions set to "[DEFAULT]" which are already initialized)
11772 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
11776 *image_config_vars[i].value =
11777 get_token_parameter_value(image_config_vars[i].token, value);
11781 void InitMenuDesignSettings_Static(void)
11783 // always start with reliable default values from static default config
11784 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
11787 static void InitMenuDesignSettings_SpecialPreProcessing(void)
11791 // the following initializes hierarchical values from static configuration
11793 // special case: initialize "ARG_DEFAULT" values in static default config
11794 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
11795 titlescreen_initial_first_default.fade_mode =
11796 title_initial_first_default.fade_mode;
11797 titlescreen_initial_first_default.fade_delay =
11798 title_initial_first_default.fade_delay;
11799 titlescreen_initial_first_default.post_delay =
11800 title_initial_first_default.post_delay;
11801 titlescreen_initial_first_default.auto_delay =
11802 title_initial_first_default.auto_delay;
11803 titlescreen_initial_first_default.auto_delay_unit =
11804 title_initial_first_default.auto_delay_unit;
11805 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
11806 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
11807 titlescreen_first_default.post_delay = title_first_default.post_delay;
11808 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
11809 titlescreen_first_default.auto_delay_unit =
11810 title_first_default.auto_delay_unit;
11811 titlemessage_initial_first_default.fade_mode =
11812 title_initial_first_default.fade_mode;
11813 titlemessage_initial_first_default.fade_delay =
11814 title_initial_first_default.fade_delay;
11815 titlemessage_initial_first_default.post_delay =
11816 title_initial_first_default.post_delay;
11817 titlemessage_initial_first_default.auto_delay =
11818 title_initial_first_default.auto_delay;
11819 titlemessage_initial_first_default.auto_delay_unit =
11820 title_initial_first_default.auto_delay_unit;
11821 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
11822 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
11823 titlemessage_first_default.post_delay = title_first_default.post_delay;
11824 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
11825 titlemessage_first_default.auto_delay_unit =
11826 title_first_default.auto_delay_unit;
11828 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
11829 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
11830 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
11831 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
11832 titlescreen_initial_default.auto_delay_unit =
11833 title_initial_default.auto_delay_unit;
11834 titlescreen_default.fade_mode = title_default.fade_mode;
11835 titlescreen_default.fade_delay = title_default.fade_delay;
11836 titlescreen_default.post_delay = title_default.post_delay;
11837 titlescreen_default.auto_delay = title_default.auto_delay;
11838 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
11839 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
11840 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
11841 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
11842 titlemessage_initial_default.auto_delay_unit =
11843 title_initial_default.auto_delay_unit;
11844 titlemessage_default.fade_mode = title_default.fade_mode;
11845 titlemessage_default.fade_delay = title_default.fade_delay;
11846 titlemessage_default.post_delay = title_default.post_delay;
11847 titlemessage_default.auto_delay = title_default.auto_delay;
11848 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
11850 // special case: initialize "ARG_DEFAULT" values in static default config
11851 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
11852 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
11854 titlescreen_initial_first[i] = titlescreen_initial_first_default;
11855 titlescreen_first[i] = titlescreen_first_default;
11856 titlemessage_initial_first[i] = titlemessage_initial_first_default;
11857 titlemessage_first[i] = titlemessage_first_default;
11859 titlescreen_initial[i] = titlescreen_initial_default;
11860 titlescreen[i] = titlescreen_default;
11861 titlemessage_initial[i] = titlemessage_initial_default;
11862 titlemessage[i] = titlemessage_default;
11865 // special case: initialize "ARG_DEFAULT" values in static default config
11866 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
11867 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11869 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
11872 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
11873 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
11874 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
11877 // special case: initialize "ARG_DEFAULT" values in static default config
11878 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
11879 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11881 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
11882 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
11883 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
11885 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
11888 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
11892 static void InitMenuDesignSettings_SpecialPostProcessing(void)
11896 struct XY *dst, *src;
11898 game_buttons_xy[] =
11900 { &game.button.save, &game.button.stop },
11901 { &game.button.pause2, &game.button.pause },
11902 { &game.button.load, &game.button.play },
11903 { &game.button.undo, &game.button.stop },
11904 { &game.button.redo, &game.button.play },
11910 // special case: initialize later added SETUP list size from LEVELS value
11911 if (menu.list_size[GAME_MODE_SETUP] == -1)
11912 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
11914 // set default position for snapshot buttons to stop/pause/play buttons
11915 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
11916 if ((*game_buttons_xy[i].dst).x == -1 &&
11917 (*game_buttons_xy[i].dst).y == -1)
11918 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
11920 // --------------------------------------------------------------------------
11921 // dynamic viewports (including playfield margins, borders and alignments)
11922 // --------------------------------------------------------------------------
11924 // dynamic viewports currently only supported for landscape mode
11925 int display_width = MAX(video.display_width, video.display_height);
11926 int display_height = MIN(video.display_width, video.display_height);
11928 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
11930 struct RectWithBorder *vp_window = &viewport.window[i];
11931 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
11932 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
11933 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
11934 boolean dynamic_window_width = (vp_window->min_width != -1);
11935 boolean dynamic_window_height = (vp_window->min_height != -1);
11936 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
11937 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
11939 // adjust window size if min/max width/height is specified
11941 if (vp_window->min_width != -1)
11943 int window_width = display_width;
11945 // when using static window height, use aspect ratio of display
11946 if (vp_window->min_height == -1)
11947 window_width = vp_window->height * display_width / display_height;
11949 vp_window->width = MAX(vp_window->min_width, window_width);
11952 if (vp_window->min_height != -1)
11954 int window_height = display_height;
11956 // when using static window width, use aspect ratio of display
11957 if (vp_window->min_width == -1)
11958 window_height = vp_window->width * display_height / display_width;
11960 vp_window->height = MAX(vp_window->min_height, window_height);
11963 if (vp_window->max_width != -1)
11964 vp_window->width = MIN(vp_window->width, vp_window->max_width);
11966 if (vp_window->max_height != -1)
11967 vp_window->height = MIN(vp_window->height, vp_window->max_height);
11969 int playfield_width = vp_window->width;
11970 int playfield_height = vp_window->height;
11972 // adjust playfield size and position according to specified margins
11974 playfield_width -= vp_playfield->margin_left;
11975 playfield_width -= vp_playfield->margin_right;
11977 playfield_height -= vp_playfield->margin_top;
11978 playfield_height -= vp_playfield->margin_bottom;
11980 // adjust playfield size if min/max width/height is specified
11982 if (vp_playfield->min_width != -1)
11983 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
11985 if (vp_playfield->min_height != -1)
11986 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
11988 if (vp_playfield->max_width != -1)
11989 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
11991 if (vp_playfield->max_height != -1)
11992 vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
11994 // adjust playfield position according to specified alignment
11996 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
11997 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
11998 else if (vp_playfield->align == ALIGN_CENTER)
11999 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12000 else if (vp_playfield->align == ALIGN_RIGHT)
12001 vp_playfield->x += playfield_width - vp_playfield->width;
12003 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12004 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12005 else if (vp_playfield->valign == VALIGN_MIDDLE)
12006 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12007 else if (vp_playfield->valign == VALIGN_BOTTOM)
12008 vp_playfield->y += playfield_height - vp_playfield->height;
12010 vp_playfield->x += vp_playfield->margin_left;
12011 vp_playfield->y += vp_playfield->margin_top;
12013 // adjust individual playfield borders if only default border is specified
12015 if (vp_playfield->border_left == -1)
12016 vp_playfield->border_left = vp_playfield->border_size;
12017 if (vp_playfield->border_right == -1)
12018 vp_playfield->border_right = vp_playfield->border_size;
12019 if (vp_playfield->border_top == -1)
12020 vp_playfield->border_top = vp_playfield->border_size;
12021 if (vp_playfield->border_bottom == -1)
12022 vp_playfield->border_bottom = vp_playfield->border_size;
12024 // set dynamic playfield borders if borders are specified as undefined
12025 // (but only if window size was dynamic and playfield size was static)
12027 if (dynamic_window_width && !dynamic_playfield_width)
12029 if (vp_playfield->border_left == -1)
12031 vp_playfield->border_left = (vp_playfield->x -
12032 vp_playfield->margin_left);
12033 vp_playfield->x -= vp_playfield->border_left;
12034 vp_playfield->width += vp_playfield->border_left;
12037 if (vp_playfield->border_right == -1)
12039 vp_playfield->border_right = (vp_window->width -
12041 vp_playfield->width -
12042 vp_playfield->margin_right);
12043 vp_playfield->width += vp_playfield->border_right;
12047 if (dynamic_window_height && !dynamic_playfield_height)
12049 if (vp_playfield->border_top == -1)
12051 vp_playfield->border_top = (vp_playfield->y -
12052 vp_playfield->margin_top);
12053 vp_playfield->y -= vp_playfield->border_top;
12054 vp_playfield->height += vp_playfield->border_top;
12057 if (vp_playfield->border_bottom == -1)
12059 vp_playfield->border_bottom = (vp_window->height -
12061 vp_playfield->height -
12062 vp_playfield->margin_bottom);
12063 vp_playfield->height += vp_playfield->border_bottom;
12067 // adjust playfield size to be a multiple of a defined alignment tile size
12069 int align_size = vp_playfield->align_size;
12070 int playfield_xtiles = vp_playfield->width / align_size;
12071 int playfield_ytiles = vp_playfield->height / align_size;
12072 int playfield_width_corrected = playfield_xtiles * align_size;
12073 int playfield_height_corrected = playfield_ytiles * align_size;
12074 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12075 i == GFX_SPECIAL_ARG_EDITOR);
12077 if (is_playfield_mode &&
12078 dynamic_playfield_width &&
12079 vp_playfield->width != playfield_width_corrected)
12081 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12083 vp_playfield->width = playfield_width_corrected;
12085 if (vp_playfield->align == ALIGN_LEFT)
12087 vp_playfield->border_left += playfield_xdiff;
12089 else if (vp_playfield->align == ALIGN_RIGHT)
12091 vp_playfield->border_right += playfield_xdiff;
12093 else if (vp_playfield->align == ALIGN_CENTER)
12095 int border_left_diff = playfield_xdiff / 2;
12096 int border_right_diff = playfield_xdiff - border_left_diff;
12098 vp_playfield->border_left += border_left_diff;
12099 vp_playfield->border_right += border_right_diff;
12103 if (is_playfield_mode &&
12104 dynamic_playfield_height &&
12105 vp_playfield->height != playfield_height_corrected)
12107 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12109 vp_playfield->height = playfield_height_corrected;
12111 if (vp_playfield->valign == VALIGN_TOP)
12113 vp_playfield->border_top += playfield_ydiff;
12115 else if (vp_playfield->align == VALIGN_BOTTOM)
12117 vp_playfield->border_right += playfield_ydiff;
12119 else if (vp_playfield->align == VALIGN_MIDDLE)
12121 int border_top_diff = playfield_ydiff / 2;
12122 int border_bottom_diff = playfield_ydiff - border_top_diff;
12124 vp_playfield->border_top += border_top_diff;
12125 vp_playfield->border_bottom += border_bottom_diff;
12129 // adjust door positions according to specified alignment
12131 for (j = 0; j < 2; j++)
12133 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12135 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12136 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12137 else if (vp_door->align == ALIGN_CENTER)
12138 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12139 else if (vp_door->align == ALIGN_RIGHT)
12140 vp_door->x += vp_window->width - vp_door->width;
12142 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12143 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12144 else if (vp_door->valign == VALIGN_MIDDLE)
12145 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12146 else if (vp_door->valign == VALIGN_BOTTOM)
12147 vp_door->y += vp_window->height - vp_door->height;
12152 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12156 struct XYTileSize *dst, *src;
12159 editor_buttons_xy[] =
12162 &editor.button.element_left, &editor.palette.element_left,
12163 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12166 &editor.button.element_middle, &editor.palette.element_middle,
12167 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12170 &editor.button.element_right, &editor.palette.element_right,
12171 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12178 // set default position for element buttons to element graphics
12179 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12181 if ((*editor_buttons_xy[i].dst).x == -1 &&
12182 (*editor_buttons_xy[i].dst).y == -1)
12184 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12186 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12188 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12192 // adjust editor palette rows and columns if specified to be dynamic
12194 if (editor.palette.cols == -1)
12196 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12197 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12198 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12200 editor.palette.cols = (vp_width - sc_width) / bt_width;
12202 if (editor.palette.x == -1)
12204 int palette_width = editor.palette.cols * bt_width + sc_width;
12206 editor.palette.x = (vp_width - palette_width) / 2;
12210 if (editor.palette.rows == -1)
12212 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12213 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12214 int tx_height = getFontHeight(FONT_TEXT_2);
12216 editor.palette.rows = (vp_height - tx_height) / bt_height;
12218 if (editor.palette.y == -1)
12220 int palette_height = editor.palette.rows * bt_height + tx_height;
12222 editor.palette.y = (vp_height - palette_height) / 2;
12227 static void LoadMenuDesignSettingsFromFilename(char *filename)
12229 static struct TitleFadingInfo tfi;
12230 static struct TitleMessageInfo tmi;
12231 static struct TokenInfo title_tokens[] =
12233 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12234 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12235 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12236 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12237 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12241 static struct TokenInfo titlemessage_tokens[] =
12243 { TYPE_INTEGER, &tmi.x, ".x" },
12244 { TYPE_INTEGER, &tmi.y, ".y" },
12245 { TYPE_INTEGER, &tmi.width, ".width" },
12246 { TYPE_INTEGER, &tmi.height, ".height" },
12247 { TYPE_INTEGER, &tmi.chars, ".chars" },
12248 { TYPE_INTEGER, &tmi.lines, ".lines" },
12249 { TYPE_INTEGER, &tmi.align, ".align" },
12250 { TYPE_INTEGER, &tmi.valign, ".valign" },
12251 { TYPE_INTEGER, &tmi.font, ".font" },
12252 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12253 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12254 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12255 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12256 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12257 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12258 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12259 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12260 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12266 struct TitleFadingInfo *info;
12271 // initialize first titles from "enter screen" definitions, if defined
12272 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12273 { &title_first_default, "menu.enter_screen.TITLE" },
12275 // initialize title screens from "next screen" definitions, if defined
12276 { &title_initial_default, "menu.next_screen.TITLE" },
12277 { &title_default, "menu.next_screen.TITLE" },
12283 struct TitleMessageInfo *array;
12286 titlemessage_arrays[] =
12288 // initialize first titles from "enter screen" definitions, if defined
12289 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12290 { titlescreen_first, "menu.enter_screen.TITLE" },
12291 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12292 { titlemessage_first, "menu.enter_screen.TITLE" },
12294 // initialize titles from "next screen" definitions, if defined
12295 { titlescreen_initial, "menu.next_screen.TITLE" },
12296 { titlescreen, "menu.next_screen.TITLE" },
12297 { titlemessage_initial, "menu.next_screen.TITLE" },
12298 { titlemessage, "menu.next_screen.TITLE" },
12300 // overwrite titles with title definitions, if defined
12301 { titlescreen_initial_first, "[title_initial]" },
12302 { titlescreen_first, "[title]" },
12303 { titlemessage_initial_first, "[title_initial]" },
12304 { titlemessage_first, "[title]" },
12306 { titlescreen_initial, "[title_initial]" },
12307 { titlescreen, "[title]" },
12308 { titlemessage_initial, "[title_initial]" },
12309 { titlemessage, "[title]" },
12311 // overwrite titles with title screen/message definitions, if defined
12312 { titlescreen_initial_first, "[titlescreen_initial]" },
12313 { titlescreen_first, "[titlescreen]" },
12314 { titlemessage_initial_first, "[titlemessage_initial]" },
12315 { titlemessage_first, "[titlemessage]" },
12317 { titlescreen_initial, "[titlescreen_initial]" },
12318 { titlescreen, "[titlescreen]" },
12319 { titlemessage_initial, "[titlemessage_initial]" },
12320 { titlemessage, "[titlemessage]" },
12324 SetupFileHash *setup_file_hash;
12327 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12330 // the following initializes hierarchical values from dynamic configuration
12332 // special case: initialize with default values that may be overwritten
12333 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12334 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12336 struct TokenIntPtrInfo menu_config[] =
12338 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12339 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12340 { "menu.list_size", &menu.list_size[i] }
12343 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12345 char *token = menu_config[j].token;
12346 char *value = getHashEntry(setup_file_hash, token);
12349 *menu_config[j].value = get_integer_from_string(value);
12353 // special case: initialize with default values that may be overwritten
12354 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12355 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12357 struct TokenIntPtrInfo menu_config[] =
12359 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12360 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12361 { "menu.list_size.INFO", &menu.list_size_info[i] }
12364 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12366 char *token = menu_config[j].token;
12367 char *value = getHashEntry(setup_file_hash, token);
12370 *menu_config[j].value = get_integer_from_string(value);
12374 // special case: initialize with default values that may be overwritten
12375 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12376 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12378 struct TokenIntPtrInfo menu_config[] =
12380 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12381 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12384 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12386 char *token = menu_config[j].token;
12387 char *value = getHashEntry(setup_file_hash, token);
12390 *menu_config[j].value = get_integer_from_string(value);
12394 // special case: initialize with default values that may be overwritten
12395 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12396 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12398 struct TokenIntPtrInfo menu_config[] =
12400 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12401 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12402 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12403 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12404 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12405 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12406 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12407 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12408 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12411 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12413 char *token = menu_config[j].token;
12414 char *value = getHashEntry(setup_file_hash, token);
12417 *menu_config[j].value = get_integer_from_string(value);
12421 // special case: initialize with default values that may be overwritten
12422 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12423 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12425 struct TokenIntPtrInfo menu_config[] =
12427 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12428 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12429 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12430 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12431 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12432 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12433 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12434 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12435 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12438 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12440 char *token = menu_config[j].token;
12441 char *value = getHashEntry(setup_file_hash, token);
12444 *menu_config[j].value = get_token_parameter_value(token, value);
12448 // special case: initialize with default values that may be overwritten
12449 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12450 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12454 char *token_prefix;
12455 struct RectWithBorder *struct_ptr;
12459 { "viewport.window", &viewport.window[i] },
12460 { "viewport.playfield", &viewport.playfield[i] },
12461 { "viewport.door_1", &viewport.door_1[i] },
12462 { "viewport.door_2", &viewport.door_2[i] }
12465 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
12467 struct TokenIntPtrInfo vp_config[] =
12469 { ".x", &vp_struct[j].struct_ptr->x },
12470 { ".y", &vp_struct[j].struct_ptr->y },
12471 { ".width", &vp_struct[j].struct_ptr->width },
12472 { ".height", &vp_struct[j].struct_ptr->height },
12473 { ".min_width", &vp_struct[j].struct_ptr->min_width },
12474 { ".min_height", &vp_struct[j].struct_ptr->min_height },
12475 { ".max_width", &vp_struct[j].struct_ptr->max_width },
12476 { ".max_height", &vp_struct[j].struct_ptr->max_height },
12477 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
12478 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
12479 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
12480 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
12481 { ".border_left", &vp_struct[j].struct_ptr->border_left },
12482 { ".border_right", &vp_struct[j].struct_ptr->border_right },
12483 { ".border_top", &vp_struct[j].struct_ptr->border_top },
12484 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
12485 { ".border_size", &vp_struct[j].struct_ptr->border_size },
12486 { ".align_size", &vp_struct[j].struct_ptr->align_size },
12487 { ".align", &vp_struct[j].struct_ptr->align },
12488 { ".valign", &vp_struct[j].struct_ptr->valign }
12491 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
12493 char *token = getStringCat2(vp_struct[j].token_prefix,
12494 vp_config[k].token);
12495 char *value = getHashEntry(setup_file_hash, token);
12498 *vp_config[k].value = get_token_parameter_value(token, value);
12505 // special case: initialize with default values that may be overwritten
12506 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
12507 for (i = 0; title_info[i].info != NULL; i++)
12509 struct TitleFadingInfo *info = title_info[i].info;
12510 char *base_token = title_info[i].text;
12512 for (j = 0; title_tokens[j].type != -1; j++)
12514 char *token = getStringCat2(base_token, title_tokens[j].text);
12515 char *value = getHashEntry(setup_file_hash, token);
12519 int parameter_value = get_token_parameter_value(token, value);
12523 *(int *)title_tokens[j].value = (int)parameter_value;
12532 // special case: initialize with default values that may be overwritten
12533 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12534 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
12536 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
12537 char *base_token = titlemessage_arrays[i].text;
12539 for (j = 0; titlemessage_tokens[j].type != -1; j++)
12541 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
12542 char *value = getHashEntry(setup_file_hash, token);
12546 int parameter_value = get_token_parameter_value(token, value);
12548 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
12552 if (titlemessage_tokens[j].type == TYPE_INTEGER)
12553 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
12555 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
12565 // special case: check if network and preview player positions are redefined,
12566 // to compare this later against the main menu level preview being redefined
12567 struct TokenIntPtrInfo menu_config_players[] =
12569 { "main.network_players.x", &menu.main.network_players.redefined },
12570 { "main.network_players.y", &menu.main.network_players.redefined },
12571 { "main.preview_players.x", &menu.main.preview_players.redefined },
12572 { "main.preview_players.y", &menu.main.preview_players.redefined },
12573 { "preview.x", &preview.redefined },
12574 { "preview.y", &preview.redefined }
12577 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12578 *menu_config_players[i].value = FALSE;
12580 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12581 if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
12582 *menu_config_players[i].value = TRUE;
12584 // read (and overwrite with) values that may be specified in config file
12585 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
12587 freeSetupFileHash(setup_file_hash);
12590 void LoadMenuDesignSettings(void)
12592 char *filename_base = UNDEFINED_FILENAME, *filename_local;
12594 InitMenuDesignSettings_Static();
12595 InitMenuDesignSettings_SpecialPreProcessing();
12597 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
12599 // first look for special settings configured in level series config
12600 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
12602 if (fileExists(filename_base))
12603 LoadMenuDesignSettingsFromFilename(filename_base);
12606 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12608 if (filename_local != NULL && !strEqual(filename_base, filename_local))
12609 LoadMenuDesignSettingsFromFilename(filename_local);
12611 InitMenuDesignSettings_SpecialPostProcessing();
12614 void LoadMenuDesignSettings_AfterGraphics(void)
12616 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
12619 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
12621 char *filename = getEditorSetupFilename();
12622 SetupFileList *setup_file_list, *list;
12623 SetupFileHash *element_hash;
12624 int num_unknown_tokens = 0;
12627 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
12630 element_hash = newSetupFileHash();
12632 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12633 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
12635 // determined size may be larger than needed (due to unknown elements)
12637 for (list = setup_file_list; list != NULL; list = list->next)
12640 // add space for up to 3 more elements for padding that may be needed
12641 *num_elements += 3;
12643 // free memory for old list of elements, if needed
12644 checked_free(*elements);
12646 // allocate memory for new list of elements
12647 *elements = checked_malloc(*num_elements * sizeof(int));
12650 for (list = setup_file_list; list != NULL; list = list->next)
12652 char *value = getHashEntry(element_hash, list->token);
12654 if (value == NULL) // try to find obsolete token mapping
12656 char *mapped_token = get_mapped_token(list->token);
12658 if (mapped_token != NULL)
12660 value = getHashEntry(element_hash, mapped_token);
12662 free(mapped_token);
12668 (*elements)[(*num_elements)++] = atoi(value);
12672 if (num_unknown_tokens == 0)
12675 Warn("unknown token(s) found in config file:");
12676 Warn("- config file: '%s'", filename);
12678 num_unknown_tokens++;
12681 Warn("- token: '%s'", list->token);
12685 if (num_unknown_tokens > 0)
12688 while (*num_elements % 4) // pad with empty elements, if needed
12689 (*elements)[(*num_elements)++] = EL_EMPTY;
12691 freeSetupFileList(setup_file_list);
12692 freeSetupFileHash(element_hash);
12695 for (i = 0; i < *num_elements; i++)
12696 Debug("editor", "element '%s' [%d]\n",
12697 element_info[(*elements)[i]].token_name, (*elements)[i]);
12701 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
12704 SetupFileHash *setup_file_hash = NULL;
12705 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
12706 char *filename_music, *filename_prefix, *filename_info;
12712 token_to_value_ptr[] =
12714 { "title_header", &tmp_music_file_info.title_header },
12715 { "artist_header", &tmp_music_file_info.artist_header },
12716 { "album_header", &tmp_music_file_info.album_header },
12717 { "year_header", &tmp_music_file_info.year_header },
12719 { "title", &tmp_music_file_info.title },
12720 { "artist", &tmp_music_file_info.artist },
12721 { "album", &tmp_music_file_info.album },
12722 { "year", &tmp_music_file_info.year },
12728 filename_music = (is_sound ? getCustomSoundFilename(basename) :
12729 getCustomMusicFilename(basename));
12731 if (filename_music == NULL)
12734 // ---------- try to replace file extension ----------
12736 filename_prefix = getStringCopy(filename_music);
12737 if (strrchr(filename_prefix, '.') != NULL)
12738 *strrchr(filename_prefix, '.') = '\0';
12739 filename_info = getStringCat2(filename_prefix, ".txt");
12741 if (fileExists(filename_info))
12742 setup_file_hash = loadSetupFileHash(filename_info);
12744 free(filename_prefix);
12745 free(filename_info);
12747 if (setup_file_hash == NULL)
12749 // ---------- try to add file extension ----------
12751 filename_prefix = getStringCopy(filename_music);
12752 filename_info = getStringCat2(filename_prefix, ".txt");
12754 if (fileExists(filename_info))
12755 setup_file_hash = loadSetupFileHash(filename_info);
12757 free(filename_prefix);
12758 free(filename_info);
12761 if (setup_file_hash == NULL)
12764 // ---------- music file info found ----------
12766 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
12768 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
12770 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
12772 *token_to_value_ptr[i].value_ptr =
12773 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
12776 tmp_music_file_info.basename = getStringCopy(basename);
12777 tmp_music_file_info.music = music;
12778 tmp_music_file_info.is_sound = is_sound;
12780 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
12781 *new_music_file_info = tmp_music_file_info;
12783 return new_music_file_info;
12786 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
12788 return get_music_file_info_ext(basename, music, FALSE);
12791 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
12793 return get_music_file_info_ext(basename, sound, TRUE);
12796 static boolean music_info_listed_ext(struct MusicFileInfo *list,
12797 char *basename, boolean is_sound)
12799 for (; list != NULL; list = list->next)
12800 if (list->is_sound == is_sound && strEqual(list->basename, basename))
12806 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
12808 return music_info_listed_ext(list, basename, FALSE);
12811 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
12813 return music_info_listed_ext(list, basename, TRUE);
12816 void LoadMusicInfo(void)
12818 char *music_directory = getCustomMusicDirectory();
12819 int num_music = getMusicListSize();
12820 int num_music_noconf = 0;
12821 int num_sounds = getSoundListSize();
12823 DirectoryEntry *dir_entry;
12824 struct FileInfo *music, *sound;
12825 struct MusicFileInfo *next, **new;
12828 while (music_file_info != NULL)
12830 next = music_file_info->next;
12832 checked_free(music_file_info->basename);
12834 checked_free(music_file_info->title_header);
12835 checked_free(music_file_info->artist_header);
12836 checked_free(music_file_info->album_header);
12837 checked_free(music_file_info->year_header);
12839 checked_free(music_file_info->title);
12840 checked_free(music_file_info->artist);
12841 checked_free(music_file_info->album);
12842 checked_free(music_file_info->year);
12844 free(music_file_info);
12846 music_file_info = next;
12849 new = &music_file_info;
12851 for (i = 0; i < num_music; i++)
12853 music = getMusicListEntry(i);
12855 if (music->filename == NULL)
12858 if (strEqual(music->filename, UNDEFINED_FILENAME))
12861 // a configured file may be not recognized as music
12862 if (!FileIsMusic(music->filename))
12865 if (!music_info_listed(music_file_info, music->filename))
12867 *new = get_music_file_info(music->filename, i);
12870 new = &(*new)->next;
12874 if ((dir = openDirectory(music_directory)) == NULL)
12876 Warn("cannot read music directory '%s'", music_directory);
12881 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
12883 char *basename = dir_entry->basename;
12884 boolean music_already_used = FALSE;
12887 // skip all music files that are configured in music config file
12888 for (i = 0; i < num_music; i++)
12890 music = getMusicListEntry(i);
12892 if (music->filename == NULL)
12895 if (strEqual(basename, music->filename))
12897 music_already_used = TRUE;
12902 if (music_already_used)
12905 if (!FileIsMusic(dir_entry->filename))
12908 if (!music_info_listed(music_file_info, basename))
12910 *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
12913 new = &(*new)->next;
12916 num_music_noconf++;
12919 closeDirectory(dir);
12921 for (i = 0; i < num_sounds; i++)
12923 sound = getSoundListEntry(i);
12925 if (sound->filename == NULL)
12928 if (strEqual(sound->filename, UNDEFINED_FILENAME))
12931 // a configured file may be not recognized as sound
12932 if (!FileIsSound(sound->filename))
12935 if (!sound_info_listed(music_file_info, sound->filename))
12937 *new = get_sound_file_info(sound->filename, i);
12939 new = &(*new)->next;
12943 // add pointers to previous list nodes
12945 struct MusicFileInfo *node = music_file_info;
12947 while (node != NULL)
12950 node->next->prev = node;
12956 static void add_helpanim_entry(int element, int action, int direction,
12957 int delay, int *num_list_entries)
12959 struct HelpAnimInfo *new_list_entry;
12960 (*num_list_entries)++;
12963 checked_realloc(helpanim_info,
12964 *num_list_entries * sizeof(struct HelpAnimInfo));
12965 new_list_entry = &helpanim_info[*num_list_entries - 1];
12967 new_list_entry->element = element;
12968 new_list_entry->action = action;
12969 new_list_entry->direction = direction;
12970 new_list_entry->delay = delay;
12973 static void print_unknown_token(char *filename, char *token, int token_nr)
12978 Warn("unknown token(s) found in config file:");
12979 Warn("- config file: '%s'", filename);
12982 Warn("- token: '%s'", token);
12985 static void print_unknown_token_end(int token_nr)
12991 void LoadHelpAnimInfo(void)
12993 char *filename = getHelpAnimFilename();
12994 SetupFileList *setup_file_list = NULL, *list;
12995 SetupFileHash *element_hash, *action_hash, *direction_hash;
12996 int num_list_entries = 0;
12997 int num_unknown_tokens = 0;
13000 if (fileExists(filename))
13001 setup_file_list = loadSetupFileList(filename);
13003 if (setup_file_list == NULL)
13005 // use reliable default values from static configuration
13006 SetupFileList *insert_ptr;
13008 insert_ptr = setup_file_list =
13009 newSetupFileList(helpanim_config[0].token,
13010 helpanim_config[0].value);
13012 for (i = 1; helpanim_config[i].token; i++)
13013 insert_ptr = addListEntry(insert_ptr,
13014 helpanim_config[i].token,
13015 helpanim_config[i].value);
13018 element_hash = newSetupFileHash();
13019 action_hash = newSetupFileHash();
13020 direction_hash = newSetupFileHash();
13022 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13023 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13025 for (i = 0; i < NUM_ACTIONS; i++)
13026 setHashEntry(action_hash, element_action_info[i].suffix,
13027 i_to_a(element_action_info[i].value));
13029 // do not store direction index (bit) here, but direction value!
13030 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13031 setHashEntry(direction_hash, element_direction_info[i].suffix,
13032 i_to_a(1 << element_direction_info[i].value));
13034 for (list = setup_file_list; list != NULL; list = list->next)
13036 char *element_token, *action_token, *direction_token;
13037 char *element_value, *action_value, *direction_value;
13038 int delay = atoi(list->value);
13040 if (strEqual(list->token, "end"))
13042 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13047 /* first try to break element into element/action/direction parts;
13048 if this does not work, also accept combined "element[.act][.dir]"
13049 elements (like "dynamite.active"), which are unique elements */
13051 if (strchr(list->token, '.') == NULL) // token contains no '.'
13053 element_value = getHashEntry(element_hash, list->token);
13054 if (element_value != NULL) // element found
13055 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13056 &num_list_entries);
13059 // no further suffixes found -- this is not an element
13060 print_unknown_token(filename, list->token, num_unknown_tokens++);
13066 // token has format "<prefix>.<something>"
13068 action_token = strchr(list->token, '.'); // suffix may be action ...
13069 direction_token = action_token; // ... or direction
13071 element_token = getStringCopy(list->token);
13072 *strchr(element_token, '.') = '\0';
13074 element_value = getHashEntry(element_hash, element_token);
13076 if (element_value == NULL) // this is no element
13078 element_value = getHashEntry(element_hash, list->token);
13079 if (element_value != NULL) // combined element found
13080 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13081 &num_list_entries);
13083 print_unknown_token(filename, list->token, num_unknown_tokens++);
13085 free(element_token);
13090 action_value = getHashEntry(action_hash, action_token);
13092 if (action_value != NULL) // action found
13094 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13095 &num_list_entries);
13097 free(element_token);
13102 direction_value = getHashEntry(direction_hash, direction_token);
13104 if (direction_value != NULL) // direction found
13106 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13107 &num_list_entries);
13109 free(element_token);
13114 if (strchr(action_token + 1, '.') == NULL)
13116 // no further suffixes found -- this is not an action nor direction
13118 element_value = getHashEntry(element_hash, list->token);
13119 if (element_value != NULL) // combined element found
13120 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13121 &num_list_entries);
13123 print_unknown_token(filename, list->token, num_unknown_tokens++);
13125 free(element_token);
13130 // token has format "<prefix>.<suffix>.<something>"
13132 direction_token = strchr(action_token + 1, '.');
13134 action_token = getStringCopy(action_token);
13135 *strchr(action_token + 1, '.') = '\0';
13137 action_value = getHashEntry(action_hash, action_token);
13139 if (action_value == NULL) // this is no action
13141 element_value = getHashEntry(element_hash, list->token);
13142 if (element_value != NULL) // combined element found
13143 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13144 &num_list_entries);
13146 print_unknown_token(filename, list->token, num_unknown_tokens++);
13148 free(element_token);
13149 free(action_token);
13154 direction_value = getHashEntry(direction_hash, direction_token);
13156 if (direction_value != NULL) // direction found
13158 add_helpanim_entry(atoi(element_value), atoi(action_value),
13159 atoi(direction_value), delay, &num_list_entries);
13161 free(element_token);
13162 free(action_token);
13167 // this is no direction
13169 element_value = getHashEntry(element_hash, list->token);
13170 if (element_value != NULL) // combined element found
13171 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13172 &num_list_entries);
13174 print_unknown_token(filename, list->token, num_unknown_tokens++);
13176 free(element_token);
13177 free(action_token);
13180 print_unknown_token_end(num_unknown_tokens);
13182 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13183 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13185 freeSetupFileList(setup_file_list);
13186 freeSetupFileHash(element_hash);
13187 freeSetupFileHash(action_hash);
13188 freeSetupFileHash(direction_hash);
13191 for (i = 0; i < num_list_entries; i++)
13192 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13193 EL_NAME(helpanim_info[i].element),
13194 helpanim_info[i].element,
13195 helpanim_info[i].action,
13196 helpanim_info[i].direction,
13197 helpanim_info[i].delay);
13201 void LoadHelpTextInfo(void)
13203 char *filename = getHelpTextFilename();
13206 if (helptext_info != NULL)
13208 freeSetupFileHash(helptext_info);
13209 helptext_info = NULL;
13212 if (fileExists(filename))
13213 helptext_info = loadSetupFileHash(filename);
13215 if (helptext_info == NULL)
13217 // use reliable default values from static configuration
13218 helptext_info = newSetupFileHash();
13220 for (i = 0; helptext_config[i].token; i++)
13221 setHashEntry(helptext_info,
13222 helptext_config[i].token,
13223 helptext_config[i].value);
13227 BEGIN_HASH_ITERATION(helptext_info, itr)
13229 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13230 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13232 END_HASH_ITERATION(hash, itr)
13237 // ----------------------------------------------------------------------------
13239 // ----------------------------------------------------------------------------
13241 #define MAX_NUM_CONVERT_LEVELS 1000
13243 void ConvertLevels(void)
13245 static LevelDirTree *convert_leveldir = NULL;
13246 static int convert_level_nr = -1;
13247 static int num_levels_handled = 0;
13248 static int num_levels_converted = 0;
13249 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13252 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13253 global.convert_leveldir);
13255 if (convert_leveldir == NULL)
13256 Fail("no such level identifier: '%s'", global.convert_leveldir);
13258 leveldir_current = convert_leveldir;
13260 if (global.convert_level_nr != -1)
13262 convert_leveldir->first_level = global.convert_level_nr;
13263 convert_leveldir->last_level = global.convert_level_nr;
13266 convert_level_nr = convert_leveldir->first_level;
13268 PrintLine("=", 79);
13269 Print("Converting levels\n");
13270 PrintLine("-", 79);
13271 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13272 Print("Level series name: '%s'\n", convert_leveldir->name);
13273 Print("Level series author: '%s'\n", convert_leveldir->author);
13274 Print("Number of levels: %d\n", convert_leveldir->levels);
13275 PrintLine("=", 79);
13278 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13279 levels_failed[i] = FALSE;
13281 while (convert_level_nr <= convert_leveldir->last_level)
13283 char *level_filename;
13286 level_nr = convert_level_nr++;
13288 Print("Level %03d: ", level_nr);
13290 LoadLevel(level_nr);
13291 if (level.no_level_file || level.no_valid_file)
13293 Print("(no level)\n");
13297 Print("converting level ... ");
13300 // special case: conversion of some EMC levels as requested by ACME
13301 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13304 level_filename = getDefaultLevelFilename(level_nr);
13305 new_level = !fileExists(level_filename);
13309 SaveLevel(level_nr);
13311 num_levels_converted++;
13313 Print("converted.\n");
13317 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13318 levels_failed[level_nr] = TRUE;
13320 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13323 num_levels_handled++;
13327 PrintLine("=", 79);
13328 Print("Number of levels handled: %d\n", num_levels_handled);
13329 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13330 (num_levels_handled ?
13331 num_levels_converted * 100 / num_levels_handled : 0));
13332 PrintLine("-", 79);
13333 Print("Summary (for automatic parsing by scripts):\n");
13334 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13335 convert_leveldir->identifier, num_levels_converted,
13336 num_levels_handled,
13337 (num_levels_handled ?
13338 num_levels_converted * 100 / num_levels_handled : 0));
13340 if (num_levels_handled != num_levels_converted)
13342 Print(", FAILED:");
13343 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13344 if (levels_failed[i])
13349 PrintLine("=", 79);
13351 CloseAllAndExit(0);
13355 // ----------------------------------------------------------------------------
13356 // create and save images for use in level sketches (raw BMP format)
13357 // ----------------------------------------------------------------------------
13359 void CreateLevelSketchImages(void)
13365 InitElementPropertiesGfxElement();
13367 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13368 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13370 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13372 int element = getMappedElement(i);
13373 char basename1[16];
13374 char basename2[16];
13378 sprintf(basename1, "%04d.bmp", i);
13379 sprintf(basename2, "%04ds.bmp", i);
13381 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13382 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13384 DrawSizedElement(0, 0, element, TILESIZE);
13385 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13387 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13388 Fail("cannot save level sketch image file '%s'", filename1);
13390 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13391 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13393 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13394 Fail("cannot save level sketch image file '%s'", filename2);
13399 // create corresponding SQL statements (for normal and small images)
13402 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13403 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13406 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13407 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13409 // optional: create content for forum level sketch demonstration post
13411 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
13414 FreeBitmap(bitmap1);
13415 FreeBitmap(bitmap2);
13418 fprintf(stderr, "\n");
13420 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
13422 CloseAllAndExit(0);
13426 // ----------------------------------------------------------------------------
13427 // create and save images for element collecting animations (raw BMP format)
13428 // ----------------------------------------------------------------------------
13430 static boolean createCollectImage(int element)
13432 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
13435 void CreateCollectElementImages(void)
13439 int anim_frames = num_steps - 1;
13440 int tile_size = TILESIZE;
13441 int anim_width = tile_size * anim_frames;
13442 int anim_height = tile_size;
13443 int num_collect_images = 0;
13444 int pos_collect_images = 0;
13446 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13447 if (createCollectImage(i))
13448 num_collect_images++;
13450 Info("Creating %d element collecting animation images ...",
13451 num_collect_images);
13453 int dst_width = anim_width * 2;
13454 int dst_height = anim_height * num_collect_images / 2;
13455 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
13456 char *basename = "RocksCollect.bmp";
13457 char *filename = getPath2(global.create_collect_images_dir, basename);
13459 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13461 if (!createCollectImage(i))
13464 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
13465 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
13466 int graphic = el2img(i);
13467 char *token_name = element_info[i].token_name;
13468 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
13469 Bitmap *src_bitmap;
13472 Info("- creating collecting image for '%s' ...", token_name);
13474 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
13476 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
13477 tile_size, tile_size, 0, 0);
13479 tmp_bitmap->surface_masked = tmp_bitmap->surface;
13481 for (j = 0; j < anim_frames; j++)
13483 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
13484 int frame_size = frame_size_final * num_steps;
13485 int offset = (tile_size - frame_size_final) / 2;
13486 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
13488 while (frame_size > frame_size_final)
13492 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
13494 FreeBitmap(frame_bitmap);
13496 frame_bitmap = half_bitmap;
13499 BlitBitmap(frame_bitmap, dst_bitmap, 0, 0,
13500 frame_size_final, frame_size_final,
13501 dst_x + j * tile_size + offset, dst_y + offset);
13503 FreeBitmap(frame_bitmap);
13506 tmp_bitmap->surface_masked = NULL;
13508 FreeBitmap(tmp_bitmap);
13510 pos_collect_images++;
13513 if (SDL_SaveBMP(dst_bitmap->surface, filename) != 0)
13514 Fail("cannot save element collecting image file '%s'", filename);
13516 FreeBitmap(dst_bitmap);
13520 CloseAllAndExit(0);
13524 // ----------------------------------------------------------------------------
13525 // create and save images for custom and group elements (raw BMP format)
13526 // ----------------------------------------------------------------------------
13528 void CreateCustomElementImages(char *directory)
13530 char *src_basename = "RocksCE-template.ilbm";
13531 char *dst_basename = "RocksCE.bmp";
13532 char *src_filename = getPath2(directory, src_basename);
13533 char *dst_filename = getPath2(directory, dst_basename);
13534 Bitmap *src_bitmap;
13536 int yoffset_ce = 0;
13537 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
13540 InitVideoDefaults();
13542 ReCreateBitmap(&backbuffer, video.width, video.height);
13544 src_bitmap = LoadImage(src_filename);
13546 bitmap = CreateBitmap(TILEX * 16 * 2,
13547 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
13550 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13557 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13558 TILEX * x, TILEY * y + yoffset_ce);
13560 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13562 TILEX * x + TILEX * 16,
13563 TILEY * y + yoffset_ce);
13565 for (j = 2; j >= 0; j--)
13569 BlitBitmap(src_bitmap, bitmap,
13570 TILEX + c * 7, 0, 6, 10,
13571 TILEX * x + 6 + j * 7,
13572 TILEY * y + 11 + yoffset_ce);
13574 BlitBitmap(src_bitmap, bitmap,
13575 TILEX + c * 8, TILEY, 6, 10,
13576 TILEX * 16 + TILEX * x + 6 + j * 8,
13577 TILEY * y + 10 + yoffset_ce);
13583 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13590 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
13591 TILEX * x, TILEY * y + yoffset_ge);
13593 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
13595 TILEX * x + TILEX * 16,
13596 TILEY * y + yoffset_ge);
13598 for (j = 1; j >= 0; j--)
13602 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
13603 TILEX * x + 6 + j * 10,
13604 TILEY * y + 11 + yoffset_ge);
13606 BlitBitmap(src_bitmap, bitmap,
13607 TILEX + c * 8, TILEY + 12, 6, 10,
13608 TILEX * 16 + TILEX * x + 10 + j * 8,
13609 TILEY * y + 10 + yoffset_ge);
13615 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
13616 Fail("cannot save CE graphics file '%s'", dst_filename);
13618 FreeBitmap(bitmap);
13620 CloseAllAndExit(0);