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_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_wraparound_objects, FALSE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_scan_first_and_last_row, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_short_explosions, TRUE
315 TYPE_BOOLEAN, CONF_VALUE_8_BIT(24),
316 &li.bd_gravity_affects_all, TRUE
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
328 // (these values are the same for each player)
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
332 &li.block_last_field, FALSE // default case for EM levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
337 &li.sp_block_last_field, TRUE // default case for SP levels
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
342 &li.instant_relocation, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
347 &li.can_pass_to_walkable, FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
352 &li.block_snap_field, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
357 &li.continuous_snapping, TRUE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
362 &li.shifted_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
367 &li.lazy_relocation, FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
372 &li.finish_dig_collect, TRUE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
377 &li.keep_walkable_ce, FALSE
380 // (these values are different for each player)
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[0], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[0], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[0], EL_PLAYER_1
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[0], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[0], EL_PLAYER_1
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[0], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[0], EL_PLAYER_1
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[0], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[0], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[1], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[1], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[1], EL_PLAYER_2
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[1], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[1], EL_PLAYER_2
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[1], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[1], EL_PLAYER_2
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[1], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[1], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[2], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[2], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[2], EL_PLAYER_3
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[2], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[2], EL_PLAYER_3
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[2], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[2], EL_PLAYER_3
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[2], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[2], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
555 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
560 &li.initial_player_gravity[3], FALSE
564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
565 &li.use_start_element[3], FALSE
569 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
570 &li.start_element[3], EL_PLAYER_4
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
575 &li.use_artwork_element[3], FALSE
579 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
580 &li.artwork_element[3], EL_PLAYER_4
584 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
585 &li.use_explosion_element[3], FALSE
589 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
590 &li.explosion_element[3], EL_PLAYER_4
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
595 &li.use_initial_inventory[3], FALSE
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
600 &li.initial_inventory_size[3], 1
604 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
605 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
609 // (these values are only valid for BD style levels)
612 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
613 &li.bd_diagonal_movements, FALSE
617 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
618 &li.bd_topmost_player_active, TRUE
622 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
623 &li.bd_pushing_prob, 25
627 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
628 &li.bd_pushing_prob_with_sweet, 100
632 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
633 &li.bd_push_mega_rock_with_sweet, FALSE
638 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
639 &li.score[SC_DIAMOND_EXTRA], 20
642 // (the following values are related to various game elements)
646 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
647 &li.score[SC_EMERALD], 10
652 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
653 &li.score[SC_DIAMOND], 10
658 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
659 &li.score[SC_BUG], 10
664 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
665 &li.score[SC_SPACESHIP], 10
670 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
671 &li.score[SC_PACMAN], 10
676 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
677 &li.score[SC_NUT], 10
682 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
683 &li.score[SC_DYNAMITE], 10
688 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
689 &li.score[SC_KEY], 10
694 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
695 &li.score[SC_PEARL], 10
700 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
701 &li.score[SC_CRYSTAL], 10
706 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
707 &li.amoeba_content, EL_DIAMOND
711 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
716 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
717 &li.grow_into_diggable, TRUE
722 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
723 &li.yamyam_content, EL_ROCK, NULL,
724 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
728 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
729 &li.score[SC_YAMYAM], 10
734 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
735 &li.score[SC_ROBOT], 10
739 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
745 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
751 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
752 &li.time_magic_wall, 10
757 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
758 &li.game_of_life[0], 2
762 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
763 &li.game_of_life[1], 3
767 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
768 &li.game_of_life[2], 3
772 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
773 &li.game_of_life[3], 3
777 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
778 &li.use_life_bugs, FALSE
783 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
788 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
793 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
798 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
803 EL_TIMEGATE_SWITCH, -1,
804 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
805 &li.time_timegate, 10
809 EL_LIGHT_SWITCH_ACTIVE, -1,
810 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
815 EL_SHIELD_NORMAL, -1,
816 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
817 &li.shield_normal_time, 10
820 EL_SHIELD_NORMAL, -1,
821 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
822 &li.score[SC_SHIELD], 10
826 EL_SHIELD_DEADLY, -1,
827 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
828 &li.shield_deadly_time, 10
831 EL_SHIELD_DEADLY, -1,
832 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
833 &li.score[SC_SHIELD], 10
838 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
843 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
844 &li.extra_time_score, 10
848 EL_TIME_ORB_FULL, -1,
849 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
850 &li.time_orb_time, 10
853 EL_TIME_ORB_FULL, -1,
854 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
855 &li.use_time_orb_bug, FALSE
860 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
861 &li.use_spring_bug, FALSE
866 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
867 &li.android_move_time, 10
871 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
872 &li.android_clone_time, 10
875 EL_EMC_ANDROID, SAVE_CONF_NEVER,
876 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
877 &li.android_clone_element[0], EL_EMPTY, NULL,
878 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
882 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
883 &li.android_clone_element[0], EL_EMPTY, NULL,
884 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
889 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
894 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
899 EL_EMC_MAGNIFIER, -1,
900 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
901 &li.magnify_score, 10
904 EL_EMC_MAGNIFIER, -1,
905 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
910 EL_EMC_MAGIC_BALL, -1,
911 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 EL_EMC_MAGIC_BALL, -1,
916 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
917 &li.ball_random, FALSE
920 EL_EMC_MAGIC_BALL, -1,
921 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
922 &li.ball_active_initial, FALSE
925 EL_EMC_MAGIC_BALL, -1,
926 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
927 &li.ball_content, EL_EMPTY, NULL,
928 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
932 EL_SOKOBAN_FIELD_EMPTY, -1,
933 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
934 &li.sb_fields_needed, TRUE
938 EL_SOKOBAN_OBJECT, -1,
939 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
940 &li.sb_objects_needed, TRUE
945 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
946 &li.mm_laser_red, FALSE
950 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
951 &li.mm_laser_green, FALSE
955 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
956 &li.mm_laser_blue, TRUE
961 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
962 &li.df_laser_red, TRUE
966 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
967 &li.df_laser_green, TRUE
971 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
972 &li.df_laser_blue, FALSE
976 EL_MM_FUSE_ACTIVE, -1,
977 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
982 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
988 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
993 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
994 &li.mm_ball_choice_mode, ANIM_RANDOM
998 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
999 &li.mm_ball_content, EL_EMPTY, NULL,
1000 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1003 EL_MM_GRAY_BALL, -1,
1004 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1005 &li.rotate_mm_ball_content, TRUE
1008 EL_MM_GRAY_BALL, -1,
1009 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1010 &li.explode_mm_ball, FALSE
1014 EL_MM_STEEL_BLOCK, -1,
1015 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1016 &li.mm_time_block, 75
1019 EL_MM_LIGHTBALL, -1,
1020 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1021 &li.score[SC_ELEM_BONUS], 10
1031 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1035 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1036 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1040 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1041 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1046 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1047 &xx_envelope.autowrap, FALSE
1051 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1052 &xx_envelope.centered, FALSE
1057 TYPE_STRING, CONF_VALUE_BYTES(1),
1058 &xx_envelope.text, -1, NULL,
1059 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1060 &xx_default_string_empty[0]
1070 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1074 TYPE_STRING, CONF_VALUE_BYTES(1),
1075 &xx_ei.description[0], -1,
1076 &yy_ei.description[0],
1077 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1078 &xx_default_description[0]
1083 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1084 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1085 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1087 #if ENABLE_RESERVED_CODE
1088 // (reserved for later use)
1091 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1092 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1093 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1099 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1100 &xx_ei.use_gfx_element, FALSE,
1101 &yy_ei.use_gfx_element
1105 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1106 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1107 &yy_ei.gfx_element_initial
1112 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1113 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1114 &yy_ei.access_direction
1119 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1120 &xx_ei.collect_score_initial, 10,
1121 &yy_ei.collect_score_initial
1125 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1126 &xx_ei.collect_count_initial, 1,
1127 &yy_ei.collect_count_initial
1132 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1133 &xx_ei.ce_value_fixed_initial, 0,
1134 &yy_ei.ce_value_fixed_initial
1138 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1139 &xx_ei.ce_value_random_initial, 0,
1140 &yy_ei.ce_value_random_initial
1144 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1145 &xx_ei.use_last_ce_value, FALSE,
1146 &yy_ei.use_last_ce_value
1151 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1152 &xx_ei.push_delay_fixed, 8,
1153 &yy_ei.push_delay_fixed
1157 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1158 &xx_ei.push_delay_random, 8,
1159 &yy_ei.push_delay_random
1163 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1164 &xx_ei.drop_delay_fixed, 0,
1165 &yy_ei.drop_delay_fixed
1169 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1170 &xx_ei.drop_delay_random, 0,
1171 &yy_ei.drop_delay_random
1175 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1176 &xx_ei.move_delay_fixed, 0,
1177 &yy_ei.move_delay_fixed
1181 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1182 &xx_ei.move_delay_random, 0,
1183 &yy_ei.move_delay_random
1187 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1188 &xx_ei.step_delay_fixed, 0,
1189 &yy_ei.step_delay_fixed
1193 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1194 &xx_ei.step_delay_random, 0,
1195 &yy_ei.step_delay_random
1200 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1201 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1206 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1207 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1208 &yy_ei.move_direction_initial
1212 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1213 &xx_ei.move_stepsize, TILEX / 8,
1214 &yy_ei.move_stepsize
1219 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1220 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1221 &yy_ei.move_enter_element
1225 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1226 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1227 &yy_ei.move_leave_element
1231 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1232 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1233 &yy_ei.move_leave_type
1238 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1239 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1240 &yy_ei.slippery_type
1245 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1246 &xx_ei.explosion_type, EXPLODES_3X3,
1247 &yy_ei.explosion_type
1251 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1252 &xx_ei.explosion_delay, 16,
1253 &yy_ei.explosion_delay
1257 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1258 &xx_ei.ignition_delay, 8,
1259 &yy_ei.ignition_delay
1264 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1265 &xx_ei.content, EL_EMPTY_SPACE,
1267 &xx_num_contents, 1, 1
1270 // ---------- "num_change_pages" must be the last entry ---------------------
1273 -1, SAVE_CONF_ALWAYS,
1274 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1275 &xx_ei.num_change_pages, 1,
1276 &yy_ei.num_change_pages
1287 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1289 // ---------- "current_change_page" must be the first entry -----------------
1292 -1, SAVE_CONF_ALWAYS,
1293 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1294 &xx_current_change_page, -1
1297 // ---------- (the remaining entries can be in any order) -------------------
1301 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1302 &xx_change.can_change, FALSE
1307 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1308 &xx_event_bits[0], 0
1312 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1313 &xx_event_bits[1], 0
1318 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1319 &xx_change.trigger_player, CH_PLAYER_ANY
1323 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1324 &xx_change.trigger_side, CH_SIDE_ANY
1328 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1329 &xx_change.trigger_page, CH_PAGE_ANY
1334 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1335 &xx_change.target_element, EL_EMPTY_SPACE
1340 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1341 &xx_change.delay_fixed, 0
1345 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1346 &xx_change.delay_random, 0
1350 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1351 &xx_change.delay_frames, FRAMES_PER_SECOND
1356 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1357 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1363 &xx_change.explode, FALSE
1367 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1368 &xx_change.use_target_content, FALSE
1372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1373 &xx_change.only_if_complete, FALSE
1377 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1378 &xx_change.use_random_replace, FALSE
1382 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1383 &xx_change.random_percentage, 100
1387 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1388 &xx_change.replace_when, CP_WHEN_EMPTY
1393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1394 &xx_change.has_action, FALSE
1398 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1399 &xx_change.action_type, CA_NO_ACTION
1403 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1404 &xx_change.action_mode, CA_MODE_UNDEFINED
1408 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1409 &xx_change.action_arg, CA_ARG_UNDEFINED
1414 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1415 &xx_change.action_element, EL_EMPTY_SPACE
1420 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1421 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1422 &xx_num_contents, 1, 1
1432 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1436 TYPE_STRING, CONF_VALUE_BYTES(1),
1437 &xx_ei.description[0], -1, NULL,
1438 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1439 &xx_default_description[0]
1444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1445 &xx_ei.use_gfx_element, FALSE
1449 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1450 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1455 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1456 &xx_group.choice_mode, ANIM_RANDOM
1461 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1462 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1463 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1473 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1477 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1478 &xx_ei.use_gfx_element, FALSE
1482 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1483 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1493 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1497 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1498 &li.block_snap_field, TRUE
1502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1503 &li.continuous_snapping, TRUE
1507 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1508 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1513 &li.use_start_element[0], FALSE
1517 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1518 &li.start_element[0], EL_PLAYER_1
1522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1523 &li.use_artwork_element[0], FALSE
1527 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1528 &li.artwork_element[0], EL_PLAYER_1
1532 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1533 &li.use_explosion_element[0], FALSE
1537 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1538 &li.explosion_element[0], EL_PLAYER_1
1553 filetype_id_list[] =
1555 { LEVEL_FILE_TYPE_RND, "RND" },
1556 { LEVEL_FILE_TYPE_BD, "BD" },
1557 { LEVEL_FILE_TYPE_EM, "EM" },
1558 { LEVEL_FILE_TYPE_SP, "SP" },
1559 { LEVEL_FILE_TYPE_DX, "DX" },
1560 { LEVEL_FILE_TYPE_SB, "SB" },
1561 { LEVEL_FILE_TYPE_DC, "DC" },
1562 { LEVEL_FILE_TYPE_MM, "MM" },
1563 { LEVEL_FILE_TYPE_MM, "DF" },
1568 // ============================================================================
1569 // level file functions
1570 // ============================================================================
1572 static boolean check_special_flags(char *flag)
1574 if (strEqual(options.special_flags, flag) ||
1575 strEqual(leveldir_current->special_flags, flag))
1581 static struct DateInfo getCurrentDate(void)
1583 time_t epoch_seconds = time(NULL);
1584 struct tm *now = localtime(&epoch_seconds);
1585 struct DateInfo date;
1587 date.year = now->tm_year + 1900;
1588 date.month = now->tm_mon + 1;
1589 date.day = now->tm_mday;
1591 date.src = DATE_SRC_CLOCK;
1596 static void resetEventFlags(struct ElementChangeInfo *change)
1600 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1601 change->has_event[i] = FALSE;
1604 static void resetEventBits(void)
1608 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1609 xx_event_bits[i] = 0;
1612 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1616 /* important: only change event flag if corresponding event bit is set
1617 (this is because all xx_event_bits[] values are loaded separately,
1618 and all xx_event_bits[] values are set back to zero before loading
1619 another value xx_event_bits[x] (each value representing 32 flags)) */
1621 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1622 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1623 change->has_event[i] = TRUE;
1626 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1630 /* in contrast to the above function setEventFlagsFromEventBits(), it
1631 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1632 depending on the corresponding change->has_event[i] values here, as
1633 all xx_event_bits[] values are reset in resetEventBits() before */
1635 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1636 if (change->has_event[i])
1637 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1640 static char *getDefaultElementDescription(struct ElementInfo *ei)
1642 static char description[MAX_ELEMENT_NAME_LEN + 1];
1643 char *default_description = (ei->custom_description != NULL ?
1644 ei->custom_description :
1645 ei->editor_description);
1648 // always start with reliable default values
1649 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1650 description[i] = '\0';
1652 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1653 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1655 return &description[0];
1658 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1660 char *default_description = getDefaultElementDescription(ei);
1663 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1664 ei->description[i] = default_description[i];
1667 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1671 for (i = 0; conf[i].data_type != -1; i++)
1673 int default_value = conf[i].default_value;
1674 int data_type = conf[i].data_type;
1675 int conf_type = conf[i].conf_type;
1676 int byte_mask = conf_type & CONF_MASK_BYTES;
1678 if (byte_mask == CONF_MASK_MULTI_BYTES)
1680 int default_num_entities = conf[i].default_num_entities;
1681 int max_num_entities = conf[i].max_num_entities;
1683 *(int *)(conf[i].num_entities) = default_num_entities;
1685 if (data_type == TYPE_STRING)
1687 char *default_string = conf[i].default_string;
1688 char *string = (char *)(conf[i].value);
1690 strncpy(string, default_string, max_num_entities);
1692 else if (data_type == TYPE_ELEMENT_LIST)
1694 int *element_array = (int *)(conf[i].value);
1697 for (j = 0; j < max_num_entities; j++)
1698 element_array[j] = default_value;
1700 else if (data_type == TYPE_CONTENT_LIST)
1702 struct Content *content = (struct Content *)(conf[i].value);
1705 for (c = 0; c < max_num_entities; c++)
1706 for (y = 0; y < 3; y++)
1707 for (x = 0; x < 3; x++)
1708 content[c].e[x][y] = default_value;
1711 else // constant size configuration data (1, 2 or 4 bytes)
1713 if (data_type == TYPE_BOOLEAN)
1714 *(boolean *)(conf[i].value) = default_value;
1716 *(int *) (conf[i].value) = default_value;
1721 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1725 for (i = 0; conf[i].data_type != -1; i++)
1727 int data_type = conf[i].data_type;
1728 int conf_type = conf[i].conf_type;
1729 int byte_mask = conf_type & CONF_MASK_BYTES;
1731 if (byte_mask == CONF_MASK_MULTI_BYTES)
1733 int max_num_entities = conf[i].max_num_entities;
1735 if (data_type == TYPE_STRING)
1737 char *string = (char *)(conf[i].value);
1738 char *string_copy = (char *)(conf[i].value_copy);
1740 strncpy(string_copy, string, max_num_entities);
1742 else if (data_type == TYPE_ELEMENT_LIST)
1744 int *element_array = (int *)(conf[i].value);
1745 int *element_array_copy = (int *)(conf[i].value_copy);
1748 for (j = 0; j < max_num_entities; j++)
1749 element_array_copy[j] = element_array[j];
1751 else if (data_type == TYPE_CONTENT_LIST)
1753 struct Content *content = (struct Content *)(conf[i].value);
1754 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1757 for (c = 0; c < max_num_entities; c++)
1758 for (y = 0; y < 3; y++)
1759 for (x = 0; x < 3; x++)
1760 content_copy[c].e[x][y] = content[c].e[x][y];
1763 else // constant size configuration data (1, 2 or 4 bytes)
1765 if (data_type == TYPE_BOOLEAN)
1766 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1768 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1773 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1777 xx_ei = *ei_from; // copy element data into temporary buffer
1778 yy_ei = *ei_to; // copy element data into temporary buffer
1780 copyConfigFromConfigList(chunk_config_CUSX_base);
1785 // ---------- reinitialize and copy change pages ----------
1787 ei_to->num_change_pages = ei_from->num_change_pages;
1788 ei_to->current_change_page = ei_from->current_change_page;
1790 setElementChangePages(ei_to, ei_to->num_change_pages);
1792 for (i = 0; i < ei_to->num_change_pages; i++)
1793 ei_to->change_page[i] = ei_from->change_page[i];
1795 // ---------- copy group element info ----------
1796 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1797 *ei_to->group = *ei_from->group;
1799 // mark this custom element as modified
1800 ei_to->modified_settings = TRUE;
1803 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1805 int change_page_size = sizeof(struct ElementChangeInfo);
1807 ei->num_change_pages = MAX(1, change_pages);
1810 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1812 if (ei->current_change_page >= ei->num_change_pages)
1813 ei->current_change_page = ei->num_change_pages - 1;
1815 ei->change = &ei->change_page[ei->current_change_page];
1818 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1820 xx_change = *change; // copy change data into temporary buffer
1822 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1824 *change = xx_change;
1826 resetEventFlags(change);
1828 change->direct_action = 0;
1829 change->other_action = 0;
1831 change->pre_change_function = NULL;
1832 change->change_function = NULL;
1833 change->post_change_function = NULL;
1836 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1840 li = *level; // copy level data into temporary buffer
1841 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1842 *level = li; // copy temporary buffer back to level data
1844 setLevelInfoToDefaults_BD();
1845 setLevelInfoToDefaults_EM();
1846 setLevelInfoToDefaults_SP();
1847 setLevelInfoToDefaults_MM();
1849 level->native_bd_level = &native_bd_level;
1850 level->native_em_level = &native_em_level;
1851 level->native_sp_level = &native_sp_level;
1852 level->native_mm_level = &native_mm_level;
1854 level->file_version = FILE_VERSION_ACTUAL;
1855 level->game_version = GAME_VERSION_ACTUAL;
1857 level->creation_date = getCurrentDate();
1859 level->encoding_16bit_field = TRUE;
1860 level->encoding_16bit_yamyam = TRUE;
1861 level->encoding_16bit_amoeba = TRUE;
1863 // clear level name and level author string buffers
1864 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1865 level->name[i] = '\0';
1866 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1867 level->author[i] = '\0';
1869 // set level name and level author to default values
1870 strcpy(level->name, NAMELESS_LEVEL_NAME);
1871 strcpy(level->author, ANONYMOUS_NAME);
1873 // set level playfield to playable default level with player and exit
1874 for (x = 0; x < MAX_LEV_FIELDX; x++)
1875 for (y = 0; y < MAX_LEV_FIELDY; y++)
1876 level->field[x][y] = EL_SAND;
1878 level->field[0][0] = EL_PLAYER_1;
1879 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1881 BorderElement = EL_STEELWALL;
1883 // detect custom elements when loading them
1884 level->file_has_custom_elements = FALSE;
1886 // set all bug compatibility flags to "false" => do not emulate this bug
1887 level->use_action_after_change_bug = FALSE;
1889 if (leveldir_current)
1891 // try to determine better author name than 'anonymous'
1892 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1894 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1895 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1899 switch (LEVELCLASS(leveldir_current))
1901 case LEVELCLASS_TUTORIAL:
1902 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1905 case LEVELCLASS_CONTRIB:
1906 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1907 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1910 case LEVELCLASS_PRIVATE:
1911 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1912 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1916 // keep default value
1923 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1925 static boolean clipboard_elements_initialized = FALSE;
1928 InitElementPropertiesStatic();
1930 li = *level; // copy level data into temporary buffer
1931 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1932 *level = li; // copy temporary buffer back to level data
1934 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1937 struct ElementInfo *ei = &element_info[element];
1939 if (element == EL_MM_GRAY_BALL)
1941 struct LevelInfo_MM *level_mm = level->native_mm_level;
1944 for (j = 0; j < level->num_mm_ball_contents; j++)
1945 level->mm_ball_content[j] =
1946 map_element_MM_to_RND(level_mm->ball_content[j]);
1949 // never initialize clipboard elements after the very first time
1950 // (to be able to use clipboard elements between several levels)
1951 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1954 if (IS_ENVELOPE(element))
1956 int envelope_nr = element - EL_ENVELOPE_1;
1958 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1960 level->envelope[envelope_nr] = xx_envelope;
1963 if (IS_CUSTOM_ELEMENT(element) ||
1964 IS_GROUP_ELEMENT(element) ||
1965 IS_INTERNAL_ELEMENT(element))
1967 xx_ei = *ei; // copy element data into temporary buffer
1969 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1974 setElementChangePages(ei, 1);
1975 setElementChangeInfoToDefaults(ei->change);
1977 if (IS_CUSTOM_ELEMENT(element) ||
1978 IS_GROUP_ELEMENT(element))
1980 setElementDescriptionToDefault(ei);
1982 ei->modified_settings = FALSE;
1985 if (IS_CUSTOM_ELEMENT(element) ||
1986 IS_INTERNAL_ELEMENT(element))
1988 // internal values used in level editor
1990 ei->access_type = 0;
1991 ei->access_layer = 0;
1992 ei->access_protected = 0;
1993 ei->walk_to_action = 0;
1994 ei->smash_targets = 0;
1997 ei->can_explode_by_fire = FALSE;
1998 ei->can_explode_smashed = FALSE;
1999 ei->can_explode_impact = FALSE;
2001 ei->current_change_page = 0;
2004 if (IS_GROUP_ELEMENT(element) ||
2005 IS_INTERNAL_ELEMENT(element))
2007 struct ElementGroupInfo *group;
2009 // initialize memory for list of elements in group
2010 if (ei->group == NULL)
2011 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2015 xx_group = *group; // copy group data into temporary buffer
2017 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2022 if (IS_EMPTY_ELEMENT(element) ||
2023 IS_INTERNAL_ELEMENT(element))
2025 xx_ei = *ei; // copy element data into temporary buffer
2027 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2033 clipboard_elements_initialized = TRUE;
2036 static void setLevelInfoToDefaults(struct LevelInfo *level,
2037 boolean level_info_only,
2038 boolean reset_file_status)
2040 setLevelInfoToDefaults_Level(level);
2042 if (!level_info_only)
2043 setLevelInfoToDefaults_Elements(level);
2045 if (reset_file_status)
2047 level->no_valid_file = FALSE;
2048 level->no_level_file = FALSE;
2051 level->changed = FALSE;
2054 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2056 level_file_info->nr = 0;
2057 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2058 level_file_info->packed = FALSE;
2060 setString(&level_file_info->basename, NULL);
2061 setString(&level_file_info->filename, NULL);
2064 int getMappedElement_SB(int, boolean);
2066 static void ActivateLevelTemplate(void)
2070 if (check_special_flags("load_xsb_to_ces"))
2072 // fill smaller playfields with padding "beyond border wall" elements
2073 if (level.fieldx < level_template.fieldx ||
2074 level.fieldy < level_template.fieldy)
2076 short field[level.fieldx][level.fieldy];
2077 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2078 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2079 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2080 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2082 // copy old playfield (which is smaller than the visible area)
2083 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2084 field[x][y] = level.field[x][y];
2086 // fill new, larger playfield with "beyond border wall" elements
2087 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2088 level.field[x][y] = getMappedElement_SB('_', TRUE);
2090 // copy the old playfield to the middle of the new playfield
2091 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2092 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2094 level.fieldx = new_fieldx;
2095 level.fieldy = new_fieldy;
2099 // Currently there is no special action needed to activate the template
2100 // data, because 'element_info' property settings overwrite the original
2101 // level data, while all other variables do not change.
2103 // Exception: 'from_level_template' elements in the original level playfield
2104 // are overwritten with the corresponding elements at the same position in
2105 // playfield from the level template.
2107 for (x = 0; x < level.fieldx; x++)
2108 for (y = 0; y < level.fieldy; y++)
2109 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2110 level.field[x][y] = level_template.field[x][y];
2112 if (check_special_flags("load_xsb_to_ces"))
2114 struct LevelInfo level_backup = level;
2116 // overwrite all individual level settings from template level settings
2117 level = level_template;
2119 // restore level file info
2120 level.file_info = level_backup.file_info;
2122 // restore playfield size
2123 level.fieldx = level_backup.fieldx;
2124 level.fieldy = level_backup.fieldy;
2126 // restore playfield content
2127 for (x = 0; x < level.fieldx; x++)
2128 for (y = 0; y < level.fieldy; y++)
2129 level.field[x][y] = level_backup.field[x][y];
2131 // restore name and author from individual level
2132 strcpy(level.name, level_backup.name);
2133 strcpy(level.author, level_backup.author);
2135 // restore flag "use_custom_template"
2136 level.use_custom_template = level_backup.use_custom_template;
2140 static boolean checkForPackageFromBasename_BD(char *basename)
2142 // check for native BD level file extensions
2143 if (!strSuffixLower(basename, ".bd") &&
2144 !strSuffixLower(basename, ".bdr") &&
2145 !strSuffixLower(basename, ".brc") &&
2146 !strSuffixLower(basename, ".gds"))
2149 // check for standard single-level BD files (like "001.bd")
2150 if (strSuffixLower(basename, ".bd") &&
2151 strlen(basename) == 6 &&
2152 basename[0] >= '0' && basename[0] <= '9' &&
2153 basename[1] >= '0' && basename[1] <= '9' &&
2154 basename[2] >= '0' && basename[2] <= '9')
2157 // this is a level package in native BD file format
2161 static char *getLevelFilenameFromBasename(char *basename)
2163 static char *filename = NULL;
2165 checked_free(filename);
2167 filename = getPath2(getCurrentLevelDir(), basename);
2172 static int getFileTypeFromBasename(char *basename)
2174 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2176 static char *filename = NULL;
2177 struct stat file_status;
2179 // ---------- try to determine file type from filename ----------
2181 // check for typical filename of a Supaplex level package file
2182 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2183 return LEVEL_FILE_TYPE_SP;
2185 // check for typical filename of a Diamond Caves II level package file
2186 if (strSuffixLower(basename, ".dc") ||
2187 strSuffixLower(basename, ".dc2"))
2188 return LEVEL_FILE_TYPE_DC;
2190 // check for typical filename of a Sokoban level package file
2191 if (strSuffixLower(basename, ".xsb") &&
2192 strchr(basename, '%') == NULL)
2193 return LEVEL_FILE_TYPE_SB;
2195 // check for typical filename of a Boulder Dash (GDash) level package file
2196 if (checkForPackageFromBasename_BD(basename))
2197 return LEVEL_FILE_TYPE_BD;
2199 // ---------- try to determine file type from filesize ----------
2201 checked_free(filename);
2202 filename = getPath2(getCurrentLevelDir(), basename);
2204 if (stat(filename, &file_status) == 0)
2206 // check for typical filesize of a Supaplex level package file
2207 if (file_status.st_size == 170496)
2208 return LEVEL_FILE_TYPE_SP;
2211 return LEVEL_FILE_TYPE_UNKNOWN;
2214 static int getFileTypeFromMagicBytes(char *filename, int type)
2218 if ((file = openFile(filename, MODE_READ)))
2220 char chunk_name[CHUNK_ID_LEN + 1];
2222 getFileChunkBE(file, chunk_name, NULL);
2224 if (strEqual(chunk_name, "MMII") ||
2225 strEqual(chunk_name, "MIRR"))
2226 type = LEVEL_FILE_TYPE_MM;
2234 static boolean checkForPackageFromBasename(char *basename)
2236 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2237 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2239 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2242 static char *getSingleLevelBasenameExt(int nr, char *extension)
2244 static char basename[MAX_FILENAME_LEN];
2247 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2249 sprintf(basename, "%03d.%s", nr, extension);
2254 static char *getSingleLevelBasename(int nr)
2256 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2259 static char *getPackedLevelBasename(int type)
2261 static char basename[MAX_FILENAME_LEN];
2262 char *directory = getCurrentLevelDir();
2264 DirectoryEntry *dir_entry;
2266 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2268 if ((dir = openDirectory(directory)) == NULL)
2270 Warn("cannot read current level directory '%s'", directory);
2275 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2277 char *entry_basename = dir_entry->basename;
2278 int entry_type = getFileTypeFromBasename(entry_basename);
2280 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2282 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2285 strcpy(basename, entry_basename);
2292 closeDirectory(dir);
2297 static char *getSingleLevelFilename(int nr)
2299 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2302 #if ENABLE_UNUSED_CODE
2303 static char *getPackedLevelFilename(int type)
2305 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2309 char *getDefaultLevelFilename(int nr)
2311 return getSingleLevelFilename(nr);
2314 #if ENABLE_UNUSED_CODE
2315 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2319 lfi->packed = FALSE;
2321 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2322 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2326 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2327 int type, char *format, ...)
2329 static char basename[MAX_FILENAME_LEN];
2332 va_start(ap, format);
2333 vsprintf(basename, format, ap);
2337 lfi->packed = FALSE;
2339 setString(&lfi->basename, basename);
2340 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2343 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2349 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2350 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2353 static int getFiletypeFromID(char *filetype_id)
2355 char *filetype_id_lower;
2356 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2359 if (filetype_id == NULL)
2360 return LEVEL_FILE_TYPE_UNKNOWN;
2362 filetype_id_lower = getStringToLower(filetype_id);
2364 for (i = 0; filetype_id_list[i].id != NULL; i++)
2366 char *id_lower = getStringToLower(filetype_id_list[i].id);
2368 if (strEqual(filetype_id_lower, id_lower))
2369 filetype = filetype_id_list[i].filetype;
2373 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2377 free(filetype_id_lower);
2382 char *getLocalLevelTemplateFilename(void)
2384 return getDefaultLevelFilename(-1);
2387 char *getGlobalLevelTemplateFilename(void)
2389 // global variable "leveldir_current" must be modified in the loop below
2390 LevelDirTree *leveldir_current_last = leveldir_current;
2391 char *filename = NULL;
2393 // check for template level in path from current to topmost tree node
2395 while (leveldir_current != NULL)
2397 filename = getDefaultLevelFilename(-1);
2399 if (fileExists(filename))
2402 leveldir_current = leveldir_current->node_parent;
2405 // restore global variable "leveldir_current" modified in above loop
2406 leveldir_current = leveldir_current_last;
2411 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2415 // special case: level number is negative => check for level template file
2418 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2419 getSingleLevelBasename(-1));
2421 // replace local level template filename with global template filename
2422 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2424 // no fallback if template file not existing
2428 // special case: check for file name/pattern specified in "levelinfo.conf"
2429 if (leveldir_current->level_filename != NULL)
2431 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2433 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2434 leveldir_current->level_filename, nr);
2436 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2438 if (fileExists(lfi->filename))
2441 else if (leveldir_current->level_filetype != NULL)
2443 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2445 // check for specified native level file with standard file name
2446 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2447 "%03d.%s", nr, LEVELFILE_EXTENSION);
2448 if (fileExists(lfi->filename))
2452 // check for native Rocks'n'Diamonds level file
2453 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2454 "%03d.%s", nr, LEVELFILE_EXTENSION);
2455 if (fileExists(lfi->filename))
2458 // check for native Boulder Dash level file
2459 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2460 if (fileExists(lfi->filename))
2463 // check for Emerald Mine level file (V1)
2464 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2465 'a' + (nr / 10) % 26, '0' + nr % 10);
2466 if (fileExists(lfi->filename))
2468 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2469 'A' + (nr / 10) % 26, '0' + nr % 10);
2470 if (fileExists(lfi->filename))
2473 // check for Emerald Mine level file (V2 to V5)
2474 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2475 if (fileExists(lfi->filename))
2478 // check for Emerald Mine level file (V6 / single mode)
2479 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2480 if (fileExists(lfi->filename))
2482 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2483 if (fileExists(lfi->filename))
2486 // check for Emerald Mine level file (V6 / teamwork mode)
2487 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2488 if (fileExists(lfi->filename))
2490 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2491 if (fileExists(lfi->filename))
2494 // check for various packed level file formats
2495 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2496 if (fileExists(lfi->filename))
2499 // no known level file found -- use default values (and fail later)
2500 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2501 "%03d.%s", nr, LEVELFILE_EXTENSION);
2504 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2506 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2507 lfi->type = getFileTypeFromBasename(lfi->basename);
2509 if (lfi->type == LEVEL_FILE_TYPE_RND)
2510 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2513 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2515 // always start with reliable default values
2516 setFileInfoToDefaults(level_file_info);
2518 level_file_info->nr = nr; // set requested level number
2520 determineLevelFileInfo_Filename(level_file_info);
2521 determineLevelFileInfo_Filetype(level_file_info);
2524 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2525 struct LevelFileInfo *lfi_to)
2527 lfi_to->nr = lfi_from->nr;
2528 lfi_to->type = lfi_from->type;
2529 lfi_to->packed = lfi_from->packed;
2531 setString(&lfi_to->basename, lfi_from->basename);
2532 setString(&lfi_to->filename, lfi_from->filename);
2535 // ----------------------------------------------------------------------------
2536 // functions for loading R'n'D level
2537 // ----------------------------------------------------------------------------
2539 int getMappedElement(int element)
2541 // remap some (historic, now obsolete) elements
2545 case EL_PLAYER_OBSOLETE:
2546 element = EL_PLAYER_1;
2549 case EL_KEY_OBSOLETE:
2553 case EL_EM_KEY_1_FILE_OBSOLETE:
2554 element = EL_EM_KEY_1;
2557 case EL_EM_KEY_2_FILE_OBSOLETE:
2558 element = EL_EM_KEY_2;
2561 case EL_EM_KEY_3_FILE_OBSOLETE:
2562 element = EL_EM_KEY_3;
2565 case EL_EM_KEY_4_FILE_OBSOLETE:
2566 element = EL_EM_KEY_4;
2569 case EL_ENVELOPE_OBSOLETE:
2570 element = EL_ENVELOPE_1;
2578 if (element >= NUM_FILE_ELEMENTS)
2580 Warn("invalid level element %d", element);
2582 element = EL_UNKNOWN;
2590 static int getMappedElementByVersion(int element, int game_version)
2592 // remap some elements due to certain game version
2594 if (game_version <= VERSION_IDENT(2,2,0,0))
2596 // map game font elements
2597 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2598 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2599 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2600 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2603 if (game_version < VERSION_IDENT(3,0,0,0))
2605 // map Supaplex gravity tube elements
2606 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2607 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2608 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2609 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2616 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2618 level->file_version = getFileVersion(file);
2619 level->game_version = getFileVersion(file);
2624 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2626 level->creation_date.year = getFile16BitBE(file);
2627 level->creation_date.month = getFile8Bit(file);
2628 level->creation_date.day = getFile8Bit(file);
2630 level->creation_date.src = DATE_SRC_LEVELFILE;
2635 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2637 int initial_player_stepsize;
2638 int initial_player_gravity;
2641 level->fieldx = getFile8Bit(file);
2642 level->fieldy = getFile8Bit(file);
2644 level->time = getFile16BitBE(file);
2645 level->gems_needed = getFile16BitBE(file);
2647 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2648 level->name[i] = getFile8Bit(file);
2649 level->name[MAX_LEVEL_NAME_LEN] = 0;
2651 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2652 level->score[i] = getFile8Bit(file);
2654 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2655 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2656 for (y = 0; y < 3; y++)
2657 for (x = 0; x < 3; x++)
2658 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2660 level->amoeba_speed = getFile8Bit(file);
2661 level->time_magic_wall = getFile8Bit(file);
2662 level->time_wheel = getFile8Bit(file);
2663 level->amoeba_content = getMappedElement(getFile8Bit(file));
2665 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2668 for (i = 0; i < MAX_PLAYERS; i++)
2669 level->initial_player_stepsize[i] = initial_player_stepsize;
2671 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2673 for (i = 0; i < MAX_PLAYERS; i++)
2674 level->initial_player_gravity[i] = initial_player_gravity;
2676 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2677 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2679 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2681 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2682 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2683 level->can_move_into_acid_bits = getFile32BitBE(file);
2684 level->dont_collide_with_bits = getFile8Bit(file);
2686 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2687 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2689 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2690 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2691 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2693 level->game_engine_type = getFile8Bit(file);
2695 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2700 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2704 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2705 level->name[i] = getFile8Bit(file);
2706 level->name[MAX_LEVEL_NAME_LEN] = 0;
2711 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2715 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2716 level->author[i] = getFile8Bit(file);
2717 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2722 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2725 int chunk_size_expected = level->fieldx * level->fieldy;
2727 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2728 stored with 16-bit encoding (and should be twice as big then).
2729 Even worse, playfield data was stored 16-bit when only yamyam content
2730 contained 16-bit elements and vice versa. */
2732 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2733 chunk_size_expected *= 2;
2735 if (chunk_size_expected != chunk_size)
2737 ReadUnusedBytesFromFile(file, chunk_size);
2738 return chunk_size_expected;
2741 for (y = 0; y < level->fieldy; y++)
2742 for (x = 0; x < level->fieldx; x++)
2743 level->field[x][y] =
2744 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2749 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2752 int header_size = 4;
2753 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2754 int chunk_size_expected = header_size + content_size;
2756 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2757 stored with 16-bit encoding (and should be twice as big then).
2758 Even worse, playfield data was stored 16-bit when only yamyam content
2759 contained 16-bit elements and vice versa. */
2761 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2762 chunk_size_expected += content_size;
2764 if (chunk_size_expected != chunk_size)
2766 ReadUnusedBytesFromFile(file, chunk_size);
2767 return chunk_size_expected;
2771 level->num_yamyam_contents = getFile8Bit(file);
2775 // correct invalid number of content fields -- should never happen
2776 if (level->num_yamyam_contents < 1 ||
2777 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2778 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2780 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2781 for (y = 0; y < 3; y++)
2782 for (x = 0; x < 3; x++)
2783 level->yamyam_content[i].e[x][y] =
2784 getMappedElement(level->encoding_16bit_field ?
2785 getFile16BitBE(file) : getFile8Bit(file));
2789 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2794 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2796 element = getMappedElement(getFile16BitBE(file));
2797 num_contents = getFile8Bit(file);
2799 getFile8Bit(file); // content x size (unused)
2800 getFile8Bit(file); // content y size (unused)
2802 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2804 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2805 for (y = 0; y < 3; y++)
2806 for (x = 0; x < 3; x++)
2807 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2809 // correct invalid number of content fields -- should never happen
2810 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2811 num_contents = STD_ELEMENT_CONTENTS;
2813 if (element == EL_YAMYAM)
2815 level->num_yamyam_contents = num_contents;
2817 for (i = 0; i < num_contents; i++)
2818 for (y = 0; y < 3; y++)
2819 for (x = 0; x < 3; x++)
2820 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2822 else if (element == EL_BD_AMOEBA)
2824 level->amoeba_content = content_array[0][0][0];
2828 Warn("cannot load content for element '%d'", element);
2834 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2840 int chunk_size_expected;
2842 element = getMappedElement(getFile16BitBE(file));
2843 if (!IS_ENVELOPE(element))
2844 element = EL_ENVELOPE_1;
2846 envelope_nr = element - EL_ENVELOPE_1;
2848 envelope_len = getFile16BitBE(file);
2850 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2851 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2853 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2855 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2856 if (chunk_size_expected != chunk_size)
2858 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2859 return chunk_size_expected;
2862 for (i = 0; i < envelope_len; i++)
2863 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2868 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2870 int num_changed_custom_elements = getFile16BitBE(file);
2871 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2874 if (chunk_size_expected != chunk_size)
2876 ReadUnusedBytesFromFile(file, chunk_size - 2);
2877 return chunk_size_expected;
2880 for (i = 0; i < num_changed_custom_elements; i++)
2882 int element = getMappedElement(getFile16BitBE(file));
2883 int properties = getFile32BitBE(file);
2885 if (IS_CUSTOM_ELEMENT(element))
2886 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2888 Warn("invalid custom element number %d", element);
2890 // older game versions that wrote level files with CUS1 chunks used
2891 // different default push delay values (not yet stored in level file)
2892 element_info[element].push_delay_fixed = 2;
2893 element_info[element].push_delay_random = 8;
2896 level->file_has_custom_elements = TRUE;
2901 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2903 int num_changed_custom_elements = getFile16BitBE(file);
2904 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2907 if (chunk_size_expected != chunk_size)
2909 ReadUnusedBytesFromFile(file, chunk_size - 2);
2910 return chunk_size_expected;
2913 for (i = 0; i < num_changed_custom_elements; i++)
2915 int element = getMappedElement(getFile16BitBE(file));
2916 int custom_target_element = getMappedElement(getFile16BitBE(file));
2918 if (IS_CUSTOM_ELEMENT(element))
2919 element_info[element].change->target_element = custom_target_element;
2921 Warn("invalid custom element number %d", element);
2924 level->file_has_custom_elements = TRUE;
2929 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2931 int num_changed_custom_elements = getFile16BitBE(file);
2932 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2935 if (chunk_size_expected != chunk_size)
2937 ReadUnusedBytesFromFile(file, chunk_size - 2);
2938 return chunk_size_expected;
2941 for (i = 0; i < num_changed_custom_elements; i++)
2943 int element = getMappedElement(getFile16BitBE(file));
2944 struct ElementInfo *ei = &element_info[element];
2945 unsigned int event_bits;
2947 if (!IS_CUSTOM_ELEMENT(element))
2949 Warn("invalid custom element number %d", element);
2951 element = EL_INTERNAL_DUMMY;
2954 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2955 ei->description[j] = getFile8Bit(file);
2956 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2958 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2960 // some free bytes for future properties and padding
2961 ReadUnusedBytesFromFile(file, 7);
2963 ei->use_gfx_element = getFile8Bit(file);
2964 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2966 ei->collect_score_initial = getFile8Bit(file);
2967 ei->collect_count_initial = getFile8Bit(file);
2969 ei->push_delay_fixed = getFile16BitBE(file);
2970 ei->push_delay_random = getFile16BitBE(file);
2971 ei->move_delay_fixed = getFile16BitBE(file);
2972 ei->move_delay_random = getFile16BitBE(file);
2974 ei->move_pattern = getFile16BitBE(file);
2975 ei->move_direction_initial = getFile8Bit(file);
2976 ei->move_stepsize = getFile8Bit(file);
2978 for (y = 0; y < 3; y++)
2979 for (x = 0; x < 3; x++)
2980 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2982 // bits 0 - 31 of "has_event[]"
2983 event_bits = getFile32BitBE(file);
2984 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2985 if (event_bits & (1u << j))
2986 ei->change->has_event[j] = TRUE;
2988 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2990 ei->change->delay_fixed = getFile16BitBE(file);
2991 ei->change->delay_random = getFile16BitBE(file);
2992 ei->change->delay_frames = getFile16BitBE(file);
2994 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2996 ei->change->explode = getFile8Bit(file);
2997 ei->change->use_target_content = getFile8Bit(file);
2998 ei->change->only_if_complete = getFile8Bit(file);
2999 ei->change->use_random_replace = getFile8Bit(file);
3001 ei->change->random_percentage = getFile8Bit(file);
3002 ei->change->replace_when = getFile8Bit(file);
3004 for (y = 0; y < 3; y++)
3005 for (x = 0; x < 3; x++)
3006 ei->change->target_content.e[x][y] =
3007 getMappedElement(getFile16BitBE(file));
3009 ei->slippery_type = getFile8Bit(file);
3011 // some free bytes for future properties and padding
3012 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3014 // mark that this custom element has been modified
3015 ei->modified_settings = TRUE;
3018 level->file_has_custom_elements = TRUE;
3023 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3025 struct ElementInfo *ei;
3026 int chunk_size_expected;
3030 // ---------- custom element base property values (96 bytes) ----------------
3032 element = getMappedElement(getFile16BitBE(file));
3034 if (!IS_CUSTOM_ELEMENT(element))
3036 Warn("invalid custom element number %d", element);
3038 ReadUnusedBytesFromFile(file, chunk_size - 2);
3043 ei = &element_info[element];
3045 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3046 ei->description[i] = getFile8Bit(file);
3047 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3049 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3051 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3053 ei->num_change_pages = getFile8Bit(file);
3055 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3056 if (chunk_size_expected != chunk_size)
3058 ReadUnusedBytesFromFile(file, chunk_size - 43);
3059 return chunk_size_expected;
3062 ei->ce_value_fixed_initial = getFile16BitBE(file);
3063 ei->ce_value_random_initial = getFile16BitBE(file);
3064 ei->use_last_ce_value = getFile8Bit(file);
3066 ei->use_gfx_element = getFile8Bit(file);
3067 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3069 ei->collect_score_initial = getFile8Bit(file);
3070 ei->collect_count_initial = getFile8Bit(file);
3072 ei->drop_delay_fixed = getFile8Bit(file);
3073 ei->push_delay_fixed = getFile8Bit(file);
3074 ei->drop_delay_random = getFile8Bit(file);
3075 ei->push_delay_random = getFile8Bit(file);
3076 ei->move_delay_fixed = getFile16BitBE(file);
3077 ei->move_delay_random = getFile16BitBE(file);
3079 // bits 0 - 15 of "move_pattern" ...
3080 ei->move_pattern = getFile16BitBE(file);
3081 ei->move_direction_initial = getFile8Bit(file);
3082 ei->move_stepsize = getFile8Bit(file);
3084 ei->slippery_type = getFile8Bit(file);
3086 for (y = 0; y < 3; y++)
3087 for (x = 0; x < 3; x++)
3088 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3090 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3091 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3092 ei->move_leave_type = getFile8Bit(file);
3094 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3095 ei->move_pattern |= (getFile16BitBE(file) << 16);
3097 ei->access_direction = getFile8Bit(file);
3099 ei->explosion_delay = getFile8Bit(file);
3100 ei->ignition_delay = getFile8Bit(file);
3101 ei->explosion_type = getFile8Bit(file);
3103 // some free bytes for future custom property values and padding
3104 ReadUnusedBytesFromFile(file, 1);
3106 // ---------- change page property values (48 bytes) ------------------------
3108 setElementChangePages(ei, ei->num_change_pages);
3110 for (i = 0; i < ei->num_change_pages; i++)
3112 struct ElementChangeInfo *change = &ei->change_page[i];
3113 unsigned int event_bits;
3115 // always start with reliable default values
3116 setElementChangeInfoToDefaults(change);
3118 // bits 0 - 31 of "has_event[]" ...
3119 event_bits = getFile32BitBE(file);
3120 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3121 if (event_bits & (1u << j))
3122 change->has_event[j] = TRUE;
3124 change->target_element = getMappedElement(getFile16BitBE(file));
3126 change->delay_fixed = getFile16BitBE(file);
3127 change->delay_random = getFile16BitBE(file);
3128 change->delay_frames = getFile16BitBE(file);
3130 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3132 change->explode = getFile8Bit(file);
3133 change->use_target_content = getFile8Bit(file);
3134 change->only_if_complete = getFile8Bit(file);
3135 change->use_random_replace = getFile8Bit(file);
3137 change->random_percentage = getFile8Bit(file);
3138 change->replace_when = getFile8Bit(file);
3140 for (y = 0; y < 3; y++)
3141 for (x = 0; x < 3; x++)
3142 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3144 change->can_change = getFile8Bit(file);
3146 change->trigger_side = getFile8Bit(file);
3148 change->trigger_player = getFile8Bit(file);
3149 change->trigger_page = getFile8Bit(file);
3151 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3152 CH_PAGE_ANY : (1 << change->trigger_page));
3154 change->has_action = getFile8Bit(file);
3155 change->action_type = getFile8Bit(file);
3156 change->action_mode = getFile8Bit(file);
3157 change->action_arg = getFile16BitBE(file);
3159 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3160 event_bits = getFile8Bit(file);
3161 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3162 if (event_bits & (1u << (j - 32)))
3163 change->has_event[j] = TRUE;
3166 // mark this custom element as modified
3167 ei->modified_settings = TRUE;
3169 level->file_has_custom_elements = TRUE;
3174 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3176 struct ElementInfo *ei;
3177 struct ElementGroupInfo *group;
3181 element = getMappedElement(getFile16BitBE(file));
3183 if (!IS_GROUP_ELEMENT(element))
3185 Warn("invalid group element number %d", element);
3187 ReadUnusedBytesFromFile(file, chunk_size - 2);
3192 ei = &element_info[element];
3194 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3195 ei->description[i] = getFile8Bit(file);
3196 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3198 group = element_info[element].group;
3200 group->num_elements = getFile8Bit(file);
3202 ei->use_gfx_element = getFile8Bit(file);
3203 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3205 group->choice_mode = getFile8Bit(file);
3207 // some free bytes for future values and padding
3208 ReadUnusedBytesFromFile(file, 3);
3210 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3211 group->element[i] = getMappedElement(getFile16BitBE(file));
3213 // mark this group element as modified
3214 element_info[element].modified_settings = TRUE;
3216 level->file_has_custom_elements = TRUE;
3221 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3222 int element, int real_element)
3224 int micro_chunk_size = 0;
3225 int conf_type = getFile8Bit(file);
3226 int byte_mask = conf_type & CONF_MASK_BYTES;
3227 boolean element_found = FALSE;
3230 micro_chunk_size += 1;
3232 if (byte_mask == CONF_MASK_MULTI_BYTES)
3234 int num_bytes = getFile16BitBE(file);
3235 byte *buffer = checked_malloc(num_bytes);
3237 ReadBytesFromFile(file, buffer, num_bytes);
3239 for (i = 0; conf[i].data_type != -1; i++)
3241 if (conf[i].element == element &&
3242 conf[i].conf_type == conf_type)
3244 int data_type = conf[i].data_type;
3245 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3246 int max_num_entities = conf[i].max_num_entities;
3248 if (num_entities > max_num_entities)
3250 Warn("truncating number of entities for element %d from %d to %d",
3251 element, num_entities, max_num_entities);
3253 num_entities = max_num_entities;
3256 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3257 data_type == TYPE_CONTENT_LIST))
3259 // for element and content lists, zero entities are not allowed
3260 Warn("found empty list of entities for element %d", element);
3262 // do not set "num_entities" here to prevent reading behind buffer
3264 *(int *)(conf[i].num_entities) = 1; // at least one is required
3268 *(int *)(conf[i].num_entities) = num_entities;
3271 element_found = TRUE;
3273 if (data_type == TYPE_STRING)
3275 char *string = (char *)(conf[i].value);
3278 for (j = 0; j < max_num_entities; j++)
3279 string[j] = (j < num_entities ? buffer[j] : '\0');
3281 else if (data_type == TYPE_ELEMENT_LIST)
3283 int *element_array = (int *)(conf[i].value);
3286 for (j = 0; j < num_entities; j++)
3288 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3290 else if (data_type == TYPE_CONTENT_LIST)
3292 struct Content *content= (struct Content *)(conf[i].value);
3295 for (c = 0; c < num_entities; c++)
3296 for (y = 0; y < 3; y++)
3297 for (x = 0; x < 3; x++)
3298 content[c].e[x][y] =
3299 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3302 element_found = FALSE;
3308 checked_free(buffer);
3310 micro_chunk_size += 2 + num_bytes;
3312 else // constant size configuration data (1, 2 or 4 bytes)
3314 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3315 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3316 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3318 for (i = 0; conf[i].data_type != -1; i++)
3320 if (conf[i].element == element &&
3321 conf[i].conf_type == conf_type)
3323 int data_type = conf[i].data_type;
3325 if (data_type == TYPE_ELEMENT)
3326 value = getMappedElement(value);
3328 if (data_type == TYPE_BOOLEAN)
3329 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3331 *(int *) (conf[i].value) = value;
3333 element_found = TRUE;
3339 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3344 char *error_conf_chunk_bytes =
3345 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3346 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3347 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3348 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3349 int error_element = real_element;
3351 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3352 error_conf_chunk_bytes, error_conf_chunk_token,
3353 error_element, EL_NAME(error_element));
3356 return micro_chunk_size;
3359 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3361 int real_chunk_size = 0;
3363 li = *level; // copy level data into temporary buffer
3365 while (!checkEndOfFile(file))
3367 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3369 if (real_chunk_size >= chunk_size)
3373 *level = li; // copy temporary buffer back to level data
3375 return real_chunk_size;
3378 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3380 int real_chunk_size = 0;
3382 li = *level; // copy level data into temporary buffer
3384 while (!checkEndOfFile(file))
3386 int element = getMappedElement(getFile16BitBE(file));
3388 real_chunk_size += 2;
3389 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3391 if (real_chunk_size >= chunk_size)
3395 *level = li; // copy temporary buffer back to level data
3397 return real_chunk_size;
3400 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3402 int real_chunk_size = 0;
3404 li = *level; // copy level data into temporary buffer
3406 while (!checkEndOfFile(file))
3408 int element = getMappedElement(getFile16BitBE(file));
3410 real_chunk_size += 2;
3411 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3413 if (real_chunk_size >= chunk_size)
3417 *level = li; // copy temporary buffer back to level data
3419 return real_chunk_size;
3422 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3424 int element = getMappedElement(getFile16BitBE(file));
3425 int envelope_nr = element - EL_ENVELOPE_1;
3426 int real_chunk_size = 2;
3428 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3430 while (!checkEndOfFile(file))
3432 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3435 if (real_chunk_size >= chunk_size)
3439 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3441 return real_chunk_size;
3444 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3446 int element = getMappedElement(getFile16BitBE(file));
3447 int real_chunk_size = 2;
3448 struct ElementInfo *ei = &element_info[element];
3451 xx_ei = *ei; // copy element data into temporary buffer
3453 xx_ei.num_change_pages = -1;
3455 while (!checkEndOfFile(file))
3457 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3459 if (xx_ei.num_change_pages != -1)
3462 if (real_chunk_size >= chunk_size)
3468 if (ei->num_change_pages == -1)
3470 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3473 ei->num_change_pages = 1;
3475 setElementChangePages(ei, 1);
3476 setElementChangeInfoToDefaults(ei->change);
3478 return real_chunk_size;
3481 // initialize number of change pages stored for this custom element
3482 setElementChangePages(ei, ei->num_change_pages);
3483 for (i = 0; i < ei->num_change_pages; i++)
3484 setElementChangeInfoToDefaults(&ei->change_page[i]);
3486 // start with reading properties for the first change page
3487 xx_current_change_page = 0;
3489 while (!checkEndOfFile(file))
3491 // level file might contain invalid change page number
3492 if (xx_current_change_page >= ei->num_change_pages)
3495 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3497 xx_change = *change; // copy change data into temporary buffer
3499 resetEventBits(); // reset bits; change page might have changed
3501 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3504 *change = xx_change;
3506 setEventFlagsFromEventBits(change);
3508 if (real_chunk_size >= chunk_size)
3512 level->file_has_custom_elements = TRUE;
3514 return real_chunk_size;
3517 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3519 int element = getMappedElement(getFile16BitBE(file));
3520 int real_chunk_size = 2;
3521 struct ElementInfo *ei = &element_info[element];
3522 struct ElementGroupInfo *group = ei->group;
3527 xx_ei = *ei; // copy element data into temporary buffer
3528 xx_group = *group; // copy group data into temporary buffer
3530 while (!checkEndOfFile(file))
3532 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3535 if (real_chunk_size >= chunk_size)
3542 level->file_has_custom_elements = TRUE;
3544 return real_chunk_size;
3547 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3549 int element = getMappedElement(getFile16BitBE(file));
3550 int real_chunk_size = 2;
3551 struct ElementInfo *ei = &element_info[element];
3553 xx_ei = *ei; // copy element data into temporary buffer
3555 while (!checkEndOfFile(file))
3557 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3560 if (real_chunk_size >= chunk_size)
3566 level->file_has_custom_elements = TRUE;
3568 return real_chunk_size;
3571 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3572 struct LevelFileInfo *level_file_info,
3573 boolean level_info_only)
3575 char *filename = level_file_info->filename;
3576 char cookie[MAX_LINE_LEN];
3577 char chunk_name[CHUNK_ID_LEN + 1];
3581 if (!(file = openFile(filename, MODE_READ)))
3583 level->no_valid_file = TRUE;
3584 level->no_level_file = TRUE;
3586 if (level_info_only)
3589 Warn("cannot read level '%s' -- using empty level", filename);
3591 if (!setup.editor.use_template_for_new_levels)
3594 // if level file not found, try to initialize level data from template
3595 filename = getGlobalLevelTemplateFilename();
3597 if (!(file = openFile(filename, MODE_READ)))
3600 // default: for empty levels, use level template for custom elements
3601 level->use_custom_template = TRUE;
3603 level->no_valid_file = FALSE;
3606 getFileChunkBE(file, chunk_name, NULL);
3607 if (strEqual(chunk_name, "RND1"))
3609 getFile32BitBE(file); // not used
3611 getFileChunkBE(file, chunk_name, NULL);
3612 if (!strEqual(chunk_name, "CAVE"))
3614 level->no_valid_file = TRUE;
3616 Warn("unknown format of level file '%s'", filename);
3623 else // check for pre-2.0 file format with cookie string
3625 strcpy(cookie, chunk_name);
3626 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3628 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3629 cookie[strlen(cookie) - 1] = '\0';
3631 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3633 level->no_valid_file = TRUE;
3635 Warn("unknown format of level file '%s'", filename);
3642 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3644 level->no_valid_file = TRUE;
3646 Warn("unsupported version of level file '%s'", filename);
3653 // pre-2.0 level files have no game version, so use file version here
3654 level->game_version = level->file_version;
3657 if (level->file_version < FILE_VERSION_1_2)
3659 // level files from versions before 1.2.0 without chunk structure
3660 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3661 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3669 int (*loader)(File *, int, struct LevelInfo *);
3673 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3674 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3675 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3676 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3677 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3678 { "INFO", -1, LoadLevel_INFO },
3679 { "BODY", -1, LoadLevel_BODY },
3680 { "CONT", -1, LoadLevel_CONT },
3681 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3682 { "CNT3", -1, LoadLevel_CNT3 },
3683 { "CUS1", -1, LoadLevel_CUS1 },
3684 { "CUS2", -1, LoadLevel_CUS2 },
3685 { "CUS3", -1, LoadLevel_CUS3 },
3686 { "CUS4", -1, LoadLevel_CUS4 },
3687 { "GRP1", -1, LoadLevel_GRP1 },
3688 { "CONF", -1, LoadLevel_CONF },
3689 { "ELEM", -1, LoadLevel_ELEM },
3690 { "NOTE", -1, LoadLevel_NOTE },
3691 { "CUSX", -1, LoadLevel_CUSX },
3692 { "GRPX", -1, LoadLevel_GRPX },
3693 { "EMPX", -1, LoadLevel_EMPX },
3698 while (getFileChunkBE(file, chunk_name, &chunk_size))
3702 while (chunk_info[i].name != NULL &&
3703 !strEqual(chunk_name, chunk_info[i].name))
3706 if (chunk_info[i].name == NULL)
3708 Warn("unknown chunk '%s' in level file '%s'",
3709 chunk_name, filename);
3711 ReadUnusedBytesFromFile(file, chunk_size);
3713 else if (chunk_info[i].size != -1 &&
3714 chunk_info[i].size != chunk_size)
3716 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3717 chunk_size, chunk_name, filename);
3719 ReadUnusedBytesFromFile(file, chunk_size);
3723 // call function to load this level chunk
3724 int chunk_size_expected =
3725 (chunk_info[i].loader)(file, chunk_size, level);
3727 if (chunk_size_expected < 0)
3729 Warn("error reading chunk '%s' in level file '%s'",
3730 chunk_name, filename);
3735 // the size of some chunks cannot be checked before reading other
3736 // chunks first (like "HEAD" and "BODY") that contain some header
3737 // information, so check them here
3738 if (chunk_size_expected != chunk_size)
3740 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3741 chunk_size, chunk_name, filename);
3753 // ----------------------------------------------------------------------------
3754 // functions for loading BD level
3755 // ----------------------------------------------------------------------------
3757 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3759 struct LevelInfo_BD *level_bd = level->native_bd_level;
3760 GdCave *cave = NULL; // will be changed below
3761 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3762 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3765 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3767 // cave and map newly allocated when set to defaults above
3768 cave = level_bd->cave;
3770 for (i = 0; i < 5; i++)
3773 cave->level_time[i] = level->time;
3774 cave->level_diamonds[i] = level->gems_needed;
3775 cave->level_magic_wall_time[i] = level->time_magic_wall;
3778 cave->level_speed[i] = level->bd_cycle_delay_ms;
3779 cave->level_ckdelay[i] = level->bd_cycle_delay_c64;
3780 cave->level_hatching_delay_frame[i] = level->bd_hatching_delay_cycles;
3781 cave->level_hatching_delay_time[i] = level->bd_hatching_delay_seconds;
3784 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3788 cave->diamond_value = level->score[SC_EMERALD];
3789 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3792 cave->scheduling = level->bd_scheduling_type;
3793 cave->pal_timing = level->bd_pal_timing;
3795 // compatibility settings
3796 cave->lineshift = level->bd_line_shifting_borders;
3797 cave->wraparound_objects = level->bd_wraparound_objects;
3798 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3799 cave->short_explosions = level->bd_short_explosions;
3800 cave->gravity_affects_all = level->bd_gravity_affects_all;
3803 cave->intermission = level->bd_intermission;
3805 // player properties
3806 cave->diagonal_movements = level->bd_diagonal_movements;
3807 cave->active_is_first_found = level->bd_topmost_player_active;
3808 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3809 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3810 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3813 strncpy(cave->name, level->name, sizeof(GdString));
3814 cave->name[sizeof(GdString) - 1] = '\0';
3816 // playfield elements
3817 for (x = 0; x < cave->w; x++)
3818 for (y = 0; y < cave->h; y++)
3819 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3822 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3824 struct LevelInfo_BD *level_bd = level->native_bd_level;
3825 GdCave *cave = level_bd->cave;
3826 int bd_level_nr = level_bd->level_nr;
3829 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3830 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3833 level->time = cave->level_time[bd_level_nr];
3834 level->gems_needed = cave->level_diamonds[bd_level_nr];
3835 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3838 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3839 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3840 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3841 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3844 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3845 level->score[SC_EMERALD] = cave->diamond_value;
3846 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3849 level->bd_scheduling_type = cave->scheduling;
3850 level->bd_pal_timing = cave->pal_timing;
3852 // compatibility settings
3853 level->bd_line_shifting_borders = cave->lineshift;
3854 level->bd_wraparound_objects = cave->wraparound_objects;
3855 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
3856 level->bd_short_explosions = cave->short_explosions;
3857 level->bd_gravity_affects_all = cave->gravity_affects_all;
3860 level->bd_intermission = cave->intermission;
3862 // player properties
3863 level->bd_diagonal_movements = cave->diagonal_movements;
3864 level->bd_topmost_player_active = cave->active_is_first_found;
3865 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
3866 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
3867 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
3870 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3872 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3873 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3875 // playfield elements
3876 for (x = 0; x < level->fieldx; x++)
3877 for (y = 0; y < level->fieldy; y++)
3878 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3880 checked_free(cave_name);
3883 static void setTapeInfoToDefaults(void);
3885 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3887 struct LevelInfo_BD *level_bd = level->native_bd_level;
3888 GdCave *cave = level_bd->cave;
3889 GdReplay *replay = level_bd->replay;
3895 // always start with reliable default values
3896 setTapeInfoToDefaults();
3898 tape.level_nr = level_nr; // (currently not used)
3899 tape.random_seed = replay->seed;
3901 TapeSetDateFromIsoDateString(replay->date);
3904 tape.pos[tape.counter].delay = 0;
3906 tape.bd_replay = TRUE;
3908 // all time calculations only used to display approximate tape time
3909 int cave_speed = cave->speed;
3910 int milliseconds_game = 0;
3911 int milliseconds_elapsed = 20;
3913 for (i = 0; i < replay->movements->len; i++)
3915 int replay_action = replay->movements->data[i];
3916 int tape_action = map_action_BD_to_RND(replay_action);
3917 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3918 boolean success = 0;
3922 success = TapeAddAction(action);
3924 milliseconds_game += milliseconds_elapsed;
3926 if (milliseconds_game >= cave_speed)
3928 milliseconds_game -= cave_speed;
3935 tape.pos[tape.counter].delay = 0;
3936 tape.pos[tape.counter].action[0] = 0;
3940 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3946 TapeHaltRecording();
3950 // ----------------------------------------------------------------------------
3951 // functions for loading EM level
3952 // ----------------------------------------------------------------------------
3954 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3956 static int ball_xy[8][2] =
3967 struct LevelInfo_EM *level_em = level->native_em_level;
3968 struct CAVE *cav = level_em->cav;
3971 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3972 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3974 cav->time_seconds = level->time;
3975 cav->gems_needed = level->gems_needed;
3977 cav->emerald_score = level->score[SC_EMERALD];
3978 cav->diamond_score = level->score[SC_DIAMOND];
3979 cav->alien_score = level->score[SC_ROBOT];
3980 cav->tank_score = level->score[SC_SPACESHIP];
3981 cav->bug_score = level->score[SC_BUG];
3982 cav->eater_score = level->score[SC_YAMYAM];
3983 cav->nut_score = level->score[SC_NUT];
3984 cav->dynamite_score = level->score[SC_DYNAMITE];
3985 cav->key_score = level->score[SC_KEY];
3986 cav->exit_score = level->score[SC_TIME_BONUS];
3988 cav->num_eater_arrays = level->num_yamyam_contents;
3990 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3991 for (y = 0; y < 3; y++)
3992 for (x = 0; x < 3; x++)
3993 cav->eater_array[i][y * 3 + x] =
3994 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3996 cav->amoeba_time = level->amoeba_speed;
3997 cav->wonderwall_time = level->time_magic_wall;
3998 cav->wheel_time = level->time_wheel;
4000 cav->android_move_time = level->android_move_time;
4001 cav->android_clone_time = level->android_clone_time;
4002 cav->ball_random = level->ball_random;
4003 cav->ball_active = level->ball_active_initial;
4004 cav->ball_time = level->ball_time;
4005 cav->num_ball_arrays = level->num_ball_contents;
4007 cav->lenses_score = level->lenses_score;
4008 cav->magnify_score = level->magnify_score;
4009 cav->slurp_score = level->slurp_score;
4011 cav->lenses_time = level->lenses_time;
4012 cav->magnify_time = level->magnify_time;
4014 cav->wind_time = 9999;
4015 cav->wind_direction =
4016 map_direction_RND_to_EM(level->wind_direction_initial);
4018 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4019 for (j = 0; j < 8; j++)
4020 cav->ball_array[i][j] =
4021 map_element_RND_to_EM_cave(level->ball_content[i].
4022 e[ball_xy[j][0]][ball_xy[j][1]]);
4024 map_android_clone_elements_RND_to_EM(level);
4026 // first fill the complete playfield with the empty space element
4027 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4028 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4029 cav->cave[x][y] = Cblank;
4031 // then copy the real level contents from level file into the playfield
4032 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4034 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4036 if (level->field[x][y] == EL_AMOEBA_DEAD)
4037 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4039 cav->cave[x][y] = new_element;
4042 for (i = 0; i < MAX_PLAYERS; i++)
4044 cav->player_x[i] = -1;
4045 cav->player_y[i] = -1;
4048 // initialize player positions and delete players from the playfield
4049 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4051 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4053 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4055 cav->player_x[player_nr] = x;
4056 cav->player_y[player_nr] = y;
4058 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4063 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4065 static int ball_xy[8][2] =
4076 struct LevelInfo_EM *level_em = level->native_em_level;
4077 struct CAVE *cav = level_em->cav;
4080 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4081 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4083 level->time = cav->time_seconds;
4084 level->gems_needed = cav->gems_needed;
4086 sprintf(level->name, "Level %d", level->file_info.nr);
4088 level->score[SC_EMERALD] = cav->emerald_score;
4089 level->score[SC_DIAMOND] = cav->diamond_score;
4090 level->score[SC_ROBOT] = cav->alien_score;
4091 level->score[SC_SPACESHIP] = cav->tank_score;
4092 level->score[SC_BUG] = cav->bug_score;
4093 level->score[SC_YAMYAM] = cav->eater_score;
4094 level->score[SC_NUT] = cav->nut_score;
4095 level->score[SC_DYNAMITE] = cav->dynamite_score;
4096 level->score[SC_KEY] = cav->key_score;
4097 level->score[SC_TIME_BONUS] = cav->exit_score;
4099 level->num_yamyam_contents = cav->num_eater_arrays;
4101 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4102 for (y = 0; y < 3; y++)
4103 for (x = 0; x < 3; x++)
4104 level->yamyam_content[i].e[x][y] =
4105 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4107 level->amoeba_speed = cav->amoeba_time;
4108 level->time_magic_wall = cav->wonderwall_time;
4109 level->time_wheel = cav->wheel_time;
4111 level->android_move_time = cav->android_move_time;
4112 level->android_clone_time = cav->android_clone_time;
4113 level->ball_random = cav->ball_random;
4114 level->ball_active_initial = cav->ball_active;
4115 level->ball_time = cav->ball_time;
4116 level->num_ball_contents = cav->num_ball_arrays;
4118 level->lenses_score = cav->lenses_score;
4119 level->magnify_score = cav->magnify_score;
4120 level->slurp_score = cav->slurp_score;
4122 level->lenses_time = cav->lenses_time;
4123 level->magnify_time = cav->magnify_time;
4125 level->wind_direction_initial =
4126 map_direction_EM_to_RND(cav->wind_direction);
4128 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4129 for (j = 0; j < 8; j++)
4130 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4131 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4133 map_android_clone_elements_EM_to_RND(level);
4135 // convert the playfield (some elements need special treatment)
4136 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4138 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4140 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4141 new_element = EL_AMOEBA_DEAD;
4143 level->field[x][y] = new_element;
4146 for (i = 0; i < MAX_PLAYERS; i++)
4148 // in case of all players set to the same field, use the first player
4149 int nr = MAX_PLAYERS - i - 1;
4150 int jx = cav->player_x[nr];
4151 int jy = cav->player_y[nr];
4153 if (jx != -1 && jy != -1)
4154 level->field[jx][jy] = EL_PLAYER_1 + nr;
4157 // time score is counted for each 10 seconds left in Emerald Mine levels
4158 level->time_score_base = 10;
4162 // ----------------------------------------------------------------------------
4163 // functions for loading SP level
4164 // ----------------------------------------------------------------------------
4166 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4168 struct LevelInfo_SP *level_sp = level->native_sp_level;
4169 LevelInfoType *header = &level_sp->header;
4172 level_sp->width = level->fieldx;
4173 level_sp->height = level->fieldy;
4175 for (x = 0; x < level->fieldx; x++)
4176 for (y = 0; y < level->fieldy; y++)
4177 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4179 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4181 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4182 header->LevelTitle[i] = level->name[i];
4183 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4185 header->InfotronsNeeded = level->gems_needed;
4187 header->SpecialPortCount = 0;
4189 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4191 boolean gravity_port_found = FALSE;
4192 boolean gravity_port_valid = FALSE;
4193 int gravity_port_flag;
4194 int gravity_port_base_element;
4195 int element = level->field[x][y];
4197 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4198 element <= EL_SP_GRAVITY_ON_PORT_UP)
4200 gravity_port_found = TRUE;
4201 gravity_port_valid = TRUE;
4202 gravity_port_flag = 1;
4203 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4205 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4206 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4208 gravity_port_found = TRUE;
4209 gravity_port_valid = TRUE;
4210 gravity_port_flag = 0;
4211 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4213 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4214 element <= EL_SP_GRAVITY_PORT_UP)
4216 // change R'n'D style gravity inverting special port to normal port
4217 // (there are no gravity inverting ports in native Supaplex engine)
4219 gravity_port_found = TRUE;
4220 gravity_port_valid = FALSE;
4221 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4224 if (gravity_port_found)
4226 if (gravity_port_valid &&
4227 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4229 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4231 port->PortLocation = (y * level->fieldx + x) * 2;
4232 port->Gravity = gravity_port_flag;
4234 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4236 header->SpecialPortCount++;
4240 // change special gravity port to normal port
4242 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4245 level_sp->playfield[x][y] = element - EL_SP_START;
4250 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4252 struct LevelInfo_SP *level_sp = level->native_sp_level;
4253 LevelInfoType *header = &level_sp->header;
4254 boolean num_invalid_elements = 0;
4257 level->fieldx = level_sp->width;
4258 level->fieldy = level_sp->height;
4260 for (x = 0; x < level->fieldx; x++)
4262 for (y = 0; y < level->fieldy; y++)
4264 int element_old = level_sp->playfield[x][y];
4265 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4267 if (element_new == EL_UNKNOWN)
4269 num_invalid_elements++;
4271 Debug("level:native:SP", "invalid element %d at position %d, %d",
4275 level->field[x][y] = element_new;
4279 if (num_invalid_elements > 0)
4280 Warn("found %d invalid elements%s", num_invalid_elements,
4281 (!options.debug ? " (use '--debug' for more details)" : ""));
4283 for (i = 0; i < MAX_PLAYERS; i++)
4284 level->initial_player_gravity[i] =
4285 (header->InitialGravity == 1 ? TRUE : FALSE);
4287 // skip leading spaces
4288 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4289 if (header->LevelTitle[i] != ' ')
4293 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4294 level->name[j] = header->LevelTitle[i];
4295 level->name[j] = '\0';
4297 // cut trailing spaces
4299 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4300 level->name[j - 1] = '\0';
4302 level->gems_needed = header->InfotronsNeeded;
4304 for (i = 0; i < header->SpecialPortCount; i++)
4306 SpecialPortType *port = &header->SpecialPort[i];
4307 int port_location = port->PortLocation;
4308 int gravity = port->Gravity;
4309 int port_x, port_y, port_element;
4311 port_x = (port_location / 2) % level->fieldx;
4312 port_y = (port_location / 2) / level->fieldx;
4314 if (port_x < 0 || port_x >= level->fieldx ||
4315 port_y < 0 || port_y >= level->fieldy)
4317 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4322 port_element = level->field[port_x][port_y];
4324 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4325 port_element > EL_SP_GRAVITY_PORT_UP)
4327 Warn("no special port at position (%d, %d)", port_x, port_y);
4332 // change previous (wrong) gravity inverting special port to either
4333 // gravity enabling special port or gravity disabling special port
4334 level->field[port_x][port_y] +=
4335 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4336 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4339 // change special gravity ports without database entries to normal ports
4340 for (x = 0; x < level->fieldx; x++)
4341 for (y = 0; y < level->fieldy; y++)
4342 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4343 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4344 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4346 level->time = 0; // no time limit
4347 level->amoeba_speed = 0;
4348 level->time_magic_wall = 0;
4349 level->time_wheel = 0;
4350 level->amoeba_content = EL_EMPTY;
4352 // original Supaplex does not use score values -- rate by playing time
4353 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4354 level->score[i] = 0;
4356 level->rate_time_over_score = TRUE;
4358 // there are no yamyams in supaplex levels
4359 for (i = 0; i < level->num_yamyam_contents; i++)
4360 for (x = 0; x < 3; x++)
4361 for (y = 0; y < 3; y++)
4362 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4365 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4367 struct LevelInfo_SP *level_sp = level->native_sp_level;
4368 struct DemoInfo_SP *demo = &level_sp->demo;
4371 // always start with reliable default values
4372 demo->is_available = FALSE;
4375 if (TAPE_IS_EMPTY(tape))
4378 demo->level_nr = tape.level_nr; // (currently not used)
4380 level_sp->header.DemoRandomSeed = tape.random_seed;
4384 for (i = 0; i < tape.length; i++)
4386 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4387 int demo_repeat = tape.pos[i].delay;
4388 int demo_entries = (demo_repeat + 15) / 16;
4390 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4392 Warn("tape truncated: size exceeds maximum SP demo size %d",
4398 for (j = 0; j < demo_repeat / 16; j++)
4399 demo->data[demo->length++] = 0xf0 | demo_action;
4401 if (demo_repeat % 16)
4402 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4405 demo->is_available = TRUE;
4408 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4410 struct LevelInfo_SP *level_sp = level->native_sp_level;
4411 struct DemoInfo_SP *demo = &level_sp->demo;
4412 char *filename = level->file_info.filename;
4415 // always start with reliable default values
4416 setTapeInfoToDefaults();
4418 if (!demo->is_available)
4421 tape.level_nr = demo->level_nr; // (currently not used)
4422 tape.random_seed = level_sp->header.DemoRandomSeed;
4424 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4427 tape.pos[tape.counter].delay = 0;
4429 for (i = 0; i < demo->length; i++)
4431 int demo_action = demo->data[i] & 0x0f;
4432 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4433 int tape_action = map_key_SP_to_RND(demo_action);
4434 int tape_repeat = demo_repeat + 1;
4435 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4436 boolean success = 0;
4439 for (j = 0; j < tape_repeat; j++)
4440 success = TapeAddAction(action);
4444 Warn("SP demo truncated: size exceeds maximum tape size %d",
4451 TapeHaltRecording();
4455 // ----------------------------------------------------------------------------
4456 // functions for loading MM level
4457 // ----------------------------------------------------------------------------
4459 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4461 struct LevelInfo_MM *level_mm = level->native_mm_level;
4464 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4465 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4467 level_mm->time = level->time;
4468 level_mm->kettles_needed = level->gems_needed;
4469 level_mm->auto_count_kettles = level->auto_count_gems;
4471 level_mm->mm_laser_red = level->mm_laser_red;
4472 level_mm->mm_laser_green = level->mm_laser_green;
4473 level_mm->mm_laser_blue = level->mm_laser_blue;
4475 level_mm->df_laser_red = level->df_laser_red;
4476 level_mm->df_laser_green = level->df_laser_green;
4477 level_mm->df_laser_blue = level->df_laser_blue;
4479 strcpy(level_mm->name, level->name);
4480 strcpy(level_mm->author, level->author);
4482 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4483 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4484 level_mm->score[SC_KEY] = level->score[SC_KEY];
4485 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4486 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4488 level_mm->amoeba_speed = level->amoeba_speed;
4489 level_mm->time_fuse = level->mm_time_fuse;
4490 level_mm->time_bomb = level->mm_time_bomb;
4491 level_mm->time_ball = level->mm_time_ball;
4492 level_mm->time_block = level->mm_time_block;
4494 level_mm->num_ball_contents = level->num_mm_ball_contents;
4495 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4496 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4497 level_mm->explode_ball = level->explode_mm_ball;
4499 for (i = 0; i < level->num_mm_ball_contents; i++)
4500 level_mm->ball_content[i] =
4501 map_element_RND_to_MM(level->mm_ball_content[i]);
4503 for (x = 0; x < level->fieldx; x++)
4504 for (y = 0; y < level->fieldy; y++)
4506 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4509 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4511 struct LevelInfo_MM *level_mm = level->native_mm_level;
4514 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4515 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4517 level->time = level_mm->time;
4518 level->gems_needed = level_mm->kettles_needed;
4519 level->auto_count_gems = level_mm->auto_count_kettles;
4521 level->mm_laser_red = level_mm->mm_laser_red;
4522 level->mm_laser_green = level_mm->mm_laser_green;
4523 level->mm_laser_blue = level_mm->mm_laser_blue;
4525 level->df_laser_red = level_mm->df_laser_red;
4526 level->df_laser_green = level_mm->df_laser_green;
4527 level->df_laser_blue = level_mm->df_laser_blue;
4529 strcpy(level->name, level_mm->name);
4531 // only overwrite author from 'levelinfo.conf' if author defined in level
4532 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4533 strcpy(level->author, level_mm->author);
4535 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4536 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4537 level->score[SC_KEY] = level_mm->score[SC_KEY];
4538 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4539 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4541 level->amoeba_speed = level_mm->amoeba_speed;
4542 level->mm_time_fuse = level_mm->time_fuse;
4543 level->mm_time_bomb = level_mm->time_bomb;
4544 level->mm_time_ball = level_mm->time_ball;
4545 level->mm_time_block = level_mm->time_block;
4547 level->num_mm_ball_contents = level_mm->num_ball_contents;
4548 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4549 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4550 level->explode_mm_ball = level_mm->explode_ball;
4552 for (i = 0; i < level->num_mm_ball_contents; i++)
4553 level->mm_ball_content[i] =
4554 map_element_MM_to_RND(level_mm->ball_content[i]);
4556 for (x = 0; x < level->fieldx; x++)
4557 for (y = 0; y < level->fieldy; y++)
4558 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4562 // ----------------------------------------------------------------------------
4563 // functions for loading DC level
4564 // ----------------------------------------------------------------------------
4566 #define DC_LEVEL_HEADER_SIZE 344
4568 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4571 static int last_data_encoded;
4575 int diff_hi, diff_lo;
4576 int data_hi, data_lo;
4577 unsigned short data_decoded;
4581 last_data_encoded = 0;
4588 diff = data_encoded - last_data_encoded;
4589 diff_hi = diff & ~0xff;
4590 diff_lo = diff & 0xff;
4594 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4595 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4596 data_hi = data_hi & 0xff00;
4598 data_decoded = data_hi | data_lo;
4600 last_data_encoded = data_encoded;
4602 offset1 = (offset1 + 1) % 31;
4603 offset2 = offset2 & 0xff;
4605 return data_decoded;
4608 static int getMappedElement_DC(int element)
4616 // 0x0117 - 0x036e: (?)
4619 // 0x042d - 0x0684: (?)
4635 element = EL_CRYSTAL;
4638 case 0x0e77: // quicksand (boulder)
4639 element = EL_QUICKSAND_FAST_FULL;
4642 case 0x0e99: // slow quicksand (boulder)
4643 element = EL_QUICKSAND_FULL;
4647 element = EL_EM_EXIT_OPEN;
4651 element = EL_EM_EXIT_CLOSED;
4655 element = EL_EM_STEEL_EXIT_OPEN;
4659 element = EL_EM_STEEL_EXIT_CLOSED;
4662 case 0x0f4f: // dynamite (lit 1)
4663 element = EL_EM_DYNAMITE_ACTIVE;
4666 case 0x0f57: // dynamite (lit 2)
4667 element = EL_EM_DYNAMITE_ACTIVE;
4670 case 0x0f5f: // dynamite (lit 3)
4671 element = EL_EM_DYNAMITE_ACTIVE;
4674 case 0x0f67: // dynamite (lit 4)
4675 element = EL_EM_DYNAMITE_ACTIVE;
4682 element = EL_AMOEBA_WET;
4686 element = EL_AMOEBA_DROP;
4690 element = EL_DC_MAGIC_WALL;
4694 element = EL_SPACESHIP_UP;
4698 element = EL_SPACESHIP_DOWN;
4702 element = EL_SPACESHIP_LEFT;
4706 element = EL_SPACESHIP_RIGHT;
4710 element = EL_BUG_UP;
4714 element = EL_BUG_DOWN;
4718 element = EL_BUG_LEFT;
4722 element = EL_BUG_RIGHT;
4726 element = EL_MOLE_UP;
4730 element = EL_MOLE_DOWN;
4734 element = EL_MOLE_LEFT;
4738 element = EL_MOLE_RIGHT;
4746 element = EL_YAMYAM_UP;
4750 element = EL_SWITCHGATE_OPEN;
4754 element = EL_SWITCHGATE_CLOSED;
4758 element = EL_DC_SWITCHGATE_SWITCH_UP;
4762 element = EL_TIMEGATE_CLOSED;
4765 case 0x144c: // conveyor belt switch (green)
4766 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4769 case 0x144f: // conveyor belt switch (red)
4770 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4773 case 0x1452: // conveyor belt switch (blue)
4774 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4778 element = EL_CONVEYOR_BELT_3_MIDDLE;
4782 element = EL_CONVEYOR_BELT_3_LEFT;
4786 element = EL_CONVEYOR_BELT_3_RIGHT;
4790 element = EL_CONVEYOR_BELT_1_MIDDLE;
4794 element = EL_CONVEYOR_BELT_1_LEFT;
4798 element = EL_CONVEYOR_BELT_1_RIGHT;
4802 element = EL_CONVEYOR_BELT_4_MIDDLE;
4806 element = EL_CONVEYOR_BELT_4_LEFT;
4810 element = EL_CONVEYOR_BELT_4_RIGHT;
4814 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4818 element = EL_EXPANDABLE_WALL_VERTICAL;
4822 element = EL_EXPANDABLE_WALL_ANY;
4825 case 0x14ce: // growing steel wall (left/right)
4826 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4829 case 0x14df: // growing steel wall (up/down)
4830 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4833 case 0x14e8: // growing steel wall (up/down/left/right)
4834 element = EL_EXPANDABLE_STEELWALL_ANY;
4838 element = EL_SHIELD_DEADLY;
4842 element = EL_EXTRA_TIME;
4850 element = EL_EMPTY_SPACE;
4853 case 0x1578: // quicksand (empty)
4854 element = EL_QUICKSAND_FAST_EMPTY;
4857 case 0x1579: // slow quicksand (empty)
4858 element = EL_QUICKSAND_EMPTY;
4868 element = EL_EM_DYNAMITE;
4871 case 0x15a1: // key (red)
4872 element = EL_EM_KEY_1;
4875 case 0x15a2: // key (yellow)
4876 element = EL_EM_KEY_2;
4879 case 0x15a3: // key (blue)
4880 element = EL_EM_KEY_4;
4883 case 0x15a4: // key (green)
4884 element = EL_EM_KEY_3;
4887 case 0x15a5: // key (white)
4888 element = EL_DC_KEY_WHITE;
4892 element = EL_WALL_SLIPPERY;
4899 case 0x15a8: // wall (not round)
4903 case 0x15a9: // (blue)
4904 element = EL_CHAR_A;
4907 case 0x15aa: // (blue)
4908 element = EL_CHAR_B;
4911 case 0x15ab: // (blue)
4912 element = EL_CHAR_C;
4915 case 0x15ac: // (blue)
4916 element = EL_CHAR_D;
4919 case 0x15ad: // (blue)
4920 element = EL_CHAR_E;
4923 case 0x15ae: // (blue)
4924 element = EL_CHAR_F;
4927 case 0x15af: // (blue)
4928 element = EL_CHAR_G;
4931 case 0x15b0: // (blue)
4932 element = EL_CHAR_H;
4935 case 0x15b1: // (blue)
4936 element = EL_CHAR_I;
4939 case 0x15b2: // (blue)
4940 element = EL_CHAR_J;
4943 case 0x15b3: // (blue)
4944 element = EL_CHAR_K;
4947 case 0x15b4: // (blue)
4948 element = EL_CHAR_L;
4951 case 0x15b5: // (blue)
4952 element = EL_CHAR_M;
4955 case 0x15b6: // (blue)
4956 element = EL_CHAR_N;
4959 case 0x15b7: // (blue)
4960 element = EL_CHAR_O;
4963 case 0x15b8: // (blue)
4964 element = EL_CHAR_P;
4967 case 0x15b9: // (blue)
4968 element = EL_CHAR_Q;
4971 case 0x15ba: // (blue)
4972 element = EL_CHAR_R;
4975 case 0x15bb: // (blue)
4976 element = EL_CHAR_S;
4979 case 0x15bc: // (blue)
4980 element = EL_CHAR_T;
4983 case 0x15bd: // (blue)
4984 element = EL_CHAR_U;
4987 case 0x15be: // (blue)
4988 element = EL_CHAR_V;
4991 case 0x15bf: // (blue)
4992 element = EL_CHAR_W;
4995 case 0x15c0: // (blue)
4996 element = EL_CHAR_X;
4999 case 0x15c1: // (blue)
5000 element = EL_CHAR_Y;
5003 case 0x15c2: // (blue)
5004 element = EL_CHAR_Z;
5007 case 0x15c3: // (blue)
5008 element = EL_CHAR_AUMLAUT;
5011 case 0x15c4: // (blue)
5012 element = EL_CHAR_OUMLAUT;
5015 case 0x15c5: // (blue)
5016 element = EL_CHAR_UUMLAUT;
5019 case 0x15c6: // (blue)
5020 element = EL_CHAR_0;
5023 case 0x15c7: // (blue)
5024 element = EL_CHAR_1;
5027 case 0x15c8: // (blue)
5028 element = EL_CHAR_2;
5031 case 0x15c9: // (blue)
5032 element = EL_CHAR_3;
5035 case 0x15ca: // (blue)
5036 element = EL_CHAR_4;
5039 case 0x15cb: // (blue)
5040 element = EL_CHAR_5;
5043 case 0x15cc: // (blue)
5044 element = EL_CHAR_6;
5047 case 0x15cd: // (blue)
5048 element = EL_CHAR_7;
5051 case 0x15ce: // (blue)
5052 element = EL_CHAR_8;
5055 case 0x15cf: // (blue)
5056 element = EL_CHAR_9;
5059 case 0x15d0: // (blue)
5060 element = EL_CHAR_PERIOD;
5063 case 0x15d1: // (blue)
5064 element = EL_CHAR_EXCLAM;
5067 case 0x15d2: // (blue)
5068 element = EL_CHAR_COLON;
5071 case 0x15d3: // (blue)
5072 element = EL_CHAR_LESS;
5075 case 0x15d4: // (blue)
5076 element = EL_CHAR_GREATER;
5079 case 0x15d5: // (blue)
5080 element = EL_CHAR_QUESTION;
5083 case 0x15d6: // (blue)
5084 element = EL_CHAR_COPYRIGHT;
5087 case 0x15d7: // (blue)
5088 element = EL_CHAR_UP;
5091 case 0x15d8: // (blue)
5092 element = EL_CHAR_DOWN;
5095 case 0x15d9: // (blue)
5096 element = EL_CHAR_BUTTON;
5099 case 0x15da: // (blue)
5100 element = EL_CHAR_PLUS;
5103 case 0x15db: // (blue)
5104 element = EL_CHAR_MINUS;
5107 case 0x15dc: // (blue)
5108 element = EL_CHAR_APOSTROPHE;
5111 case 0x15dd: // (blue)
5112 element = EL_CHAR_PARENLEFT;
5115 case 0x15de: // (blue)
5116 element = EL_CHAR_PARENRIGHT;
5119 case 0x15df: // (green)
5120 element = EL_CHAR_A;
5123 case 0x15e0: // (green)
5124 element = EL_CHAR_B;
5127 case 0x15e1: // (green)
5128 element = EL_CHAR_C;
5131 case 0x15e2: // (green)
5132 element = EL_CHAR_D;
5135 case 0x15e3: // (green)
5136 element = EL_CHAR_E;
5139 case 0x15e4: // (green)
5140 element = EL_CHAR_F;
5143 case 0x15e5: // (green)
5144 element = EL_CHAR_G;
5147 case 0x15e6: // (green)
5148 element = EL_CHAR_H;
5151 case 0x15e7: // (green)
5152 element = EL_CHAR_I;
5155 case 0x15e8: // (green)
5156 element = EL_CHAR_J;
5159 case 0x15e9: // (green)
5160 element = EL_CHAR_K;
5163 case 0x15ea: // (green)
5164 element = EL_CHAR_L;
5167 case 0x15eb: // (green)
5168 element = EL_CHAR_M;
5171 case 0x15ec: // (green)
5172 element = EL_CHAR_N;
5175 case 0x15ed: // (green)
5176 element = EL_CHAR_O;
5179 case 0x15ee: // (green)
5180 element = EL_CHAR_P;
5183 case 0x15ef: // (green)
5184 element = EL_CHAR_Q;
5187 case 0x15f0: // (green)
5188 element = EL_CHAR_R;
5191 case 0x15f1: // (green)
5192 element = EL_CHAR_S;
5195 case 0x15f2: // (green)
5196 element = EL_CHAR_T;
5199 case 0x15f3: // (green)
5200 element = EL_CHAR_U;
5203 case 0x15f4: // (green)
5204 element = EL_CHAR_V;
5207 case 0x15f5: // (green)
5208 element = EL_CHAR_W;
5211 case 0x15f6: // (green)
5212 element = EL_CHAR_X;
5215 case 0x15f7: // (green)
5216 element = EL_CHAR_Y;
5219 case 0x15f8: // (green)
5220 element = EL_CHAR_Z;
5223 case 0x15f9: // (green)
5224 element = EL_CHAR_AUMLAUT;
5227 case 0x15fa: // (green)
5228 element = EL_CHAR_OUMLAUT;
5231 case 0x15fb: // (green)
5232 element = EL_CHAR_UUMLAUT;
5235 case 0x15fc: // (green)
5236 element = EL_CHAR_0;
5239 case 0x15fd: // (green)
5240 element = EL_CHAR_1;
5243 case 0x15fe: // (green)
5244 element = EL_CHAR_2;
5247 case 0x15ff: // (green)
5248 element = EL_CHAR_3;
5251 case 0x1600: // (green)
5252 element = EL_CHAR_4;
5255 case 0x1601: // (green)
5256 element = EL_CHAR_5;
5259 case 0x1602: // (green)
5260 element = EL_CHAR_6;
5263 case 0x1603: // (green)
5264 element = EL_CHAR_7;
5267 case 0x1604: // (green)
5268 element = EL_CHAR_8;
5271 case 0x1605: // (green)
5272 element = EL_CHAR_9;
5275 case 0x1606: // (green)
5276 element = EL_CHAR_PERIOD;
5279 case 0x1607: // (green)
5280 element = EL_CHAR_EXCLAM;
5283 case 0x1608: // (green)
5284 element = EL_CHAR_COLON;
5287 case 0x1609: // (green)
5288 element = EL_CHAR_LESS;
5291 case 0x160a: // (green)
5292 element = EL_CHAR_GREATER;
5295 case 0x160b: // (green)
5296 element = EL_CHAR_QUESTION;
5299 case 0x160c: // (green)
5300 element = EL_CHAR_COPYRIGHT;
5303 case 0x160d: // (green)
5304 element = EL_CHAR_UP;
5307 case 0x160e: // (green)
5308 element = EL_CHAR_DOWN;
5311 case 0x160f: // (green)
5312 element = EL_CHAR_BUTTON;
5315 case 0x1610: // (green)
5316 element = EL_CHAR_PLUS;
5319 case 0x1611: // (green)
5320 element = EL_CHAR_MINUS;
5323 case 0x1612: // (green)
5324 element = EL_CHAR_APOSTROPHE;
5327 case 0x1613: // (green)
5328 element = EL_CHAR_PARENLEFT;
5331 case 0x1614: // (green)
5332 element = EL_CHAR_PARENRIGHT;
5335 case 0x1615: // (blue steel)
5336 element = EL_STEEL_CHAR_A;
5339 case 0x1616: // (blue steel)
5340 element = EL_STEEL_CHAR_B;
5343 case 0x1617: // (blue steel)
5344 element = EL_STEEL_CHAR_C;
5347 case 0x1618: // (blue steel)
5348 element = EL_STEEL_CHAR_D;
5351 case 0x1619: // (blue steel)
5352 element = EL_STEEL_CHAR_E;
5355 case 0x161a: // (blue steel)
5356 element = EL_STEEL_CHAR_F;
5359 case 0x161b: // (blue steel)
5360 element = EL_STEEL_CHAR_G;
5363 case 0x161c: // (blue steel)
5364 element = EL_STEEL_CHAR_H;
5367 case 0x161d: // (blue steel)
5368 element = EL_STEEL_CHAR_I;
5371 case 0x161e: // (blue steel)
5372 element = EL_STEEL_CHAR_J;
5375 case 0x161f: // (blue steel)
5376 element = EL_STEEL_CHAR_K;
5379 case 0x1620: // (blue steel)
5380 element = EL_STEEL_CHAR_L;
5383 case 0x1621: // (blue steel)
5384 element = EL_STEEL_CHAR_M;
5387 case 0x1622: // (blue steel)
5388 element = EL_STEEL_CHAR_N;
5391 case 0x1623: // (blue steel)
5392 element = EL_STEEL_CHAR_O;
5395 case 0x1624: // (blue steel)
5396 element = EL_STEEL_CHAR_P;
5399 case 0x1625: // (blue steel)
5400 element = EL_STEEL_CHAR_Q;
5403 case 0x1626: // (blue steel)
5404 element = EL_STEEL_CHAR_R;
5407 case 0x1627: // (blue steel)
5408 element = EL_STEEL_CHAR_S;
5411 case 0x1628: // (blue steel)
5412 element = EL_STEEL_CHAR_T;
5415 case 0x1629: // (blue steel)
5416 element = EL_STEEL_CHAR_U;
5419 case 0x162a: // (blue steel)
5420 element = EL_STEEL_CHAR_V;
5423 case 0x162b: // (blue steel)
5424 element = EL_STEEL_CHAR_W;
5427 case 0x162c: // (blue steel)
5428 element = EL_STEEL_CHAR_X;
5431 case 0x162d: // (blue steel)
5432 element = EL_STEEL_CHAR_Y;
5435 case 0x162e: // (blue steel)
5436 element = EL_STEEL_CHAR_Z;
5439 case 0x162f: // (blue steel)
5440 element = EL_STEEL_CHAR_AUMLAUT;
5443 case 0x1630: // (blue steel)
5444 element = EL_STEEL_CHAR_OUMLAUT;
5447 case 0x1631: // (blue steel)
5448 element = EL_STEEL_CHAR_UUMLAUT;
5451 case 0x1632: // (blue steel)
5452 element = EL_STEEL_CHAR_0;
5455 case 0x1633: // (blue steel)
5456 element = EL_STEEL_CHAR_1;
5459 case 0x1634: // (blue steel)
5460 element = EL_STEEL_CHAR_2;
5463 case 0x1635: // (blue steel)
5464 element = EL_STEEL_CHAR_3;
5467 case 0x1636: // (blue steel)
5468 element = EL_STEEL_CHAR_4;
5471 case 0x1637: // (blue steel)
5472 element = EL_STEEL_CHAR_5;
5475 case 0x1638: // (blue steel)
5476 element = EL_STEEL_CHAR_6;
5479 case 0x1639: // (blue steel)
5480 element = EL_STEEL_CHAR_7;
5483 case 0x163a: // (blue steel)
5484 element = EL_STEEL_CHAR_8;
5487 case 0x163b: // (blue steel)
5488 element = EL_STEEL_CHAR_9;
5491 case 0x163c: // (blue steel)
5492 element = EL_STEEL_CHAR_PERIOD;
5495 case 0x163d: // (blue steel)
5496 element = EL_STEEL_CHAR_EXCLAM;
5499 case 0x163e: // (blue steel)
5500 element = EL_STEEL_CHAR_COLON;
5503 case 0x163f: // (blue steel)
5504 element = EL_STEEL_CHAR_LESS;
5507 case 0x1640: // (blue steel)
5508 element = EL_STEEL_CHAR_GREATER;
5511 case 0x1641: // (blue steel)
5512 element = EL_STEEL_CHAR_QUESTION;
5515 case 0x1642: // (blue steel)
5516 element = EL_STEEL_CHAR_COPYRIGHT;
5519 case 0x1643: // (blue steel)
5520 element = EL_STEEL_CHAR_UP;
5523 case 0x1644: // (blue steel)
5524 element = EL_STEEL_CHAR_DOWN;
5527 case 0x1645: // (blue steel)
5528 element = EL_STEEL_CHAR_BUTTON;
5531 case 0x1646: // (blue steel)
5532 element = EL_STEEL_CHAR_PLUS;
5535 case 0x1647: // (blue steel)
5536 element = EL_STEEL_CHAR_MINUS;
5539 case 0x1648: // (blue steel)
5540 element = EL_STEEL_CHAR_APOSTROPHE;
5543 case 0x1649: // (blue steel)
5544 element = EL_STEEL_CHAR_PARENLEFT;
5547 case 0x164a: // (blue steel)
5548 element = EL_STEEL_CHAR_PARENRIGHT;
5551 case 0x164b: // (green steel)
5552 element = EL_STEEL_CHAR_A;
5555 case 0x164c: // (green steel)
5556 element = EL_STEEL_CHAR_B;
5559 case 0x164d: // (green steel)
5560 element = EL_STEEL_CHAR_C;
5563 case 0x164e: // (green steel)
5564 element = EL_STEEL_CHAR_D;
5567 case 0x164f: // (green steel)
5568 element = EL_STEEL_CHAR_E;
5571 case 0x1650: // (green steel)
5572 element = EL_STEEL_CHAR_F;
5575 case 0x1651: // (green steel)
5576 element = EL_STEEL_CHAR_G;
5579 case 0x1652: // (green steel)
5580 element = EL_STEEL_CHAR_H;
5583 case 0x1653: // (green steel)
5584 element = EL_STEEL_CHAR_I;
5587 case 0x1654: // (green steel)
5588 element = EL_STEEL_CHAR_J;
5591 case 0x1655: // (green steel)
5592 element = EL_STEEL_CHAR_K;
5595 case 0x1656: // (green steel)
5596 element = EL_STEEL_CHAR_L;
5599 case 0x1657: // (green steel)
5600 element = EL_STEEL_CHAR_M;
5603 case 0x1658: // (green steel)
5604 element = EL_STEEL_CHAR_N;
5607 case 0x1659: // (green steel)
5608 element = EL_STEEL_CHAR_O;
5611 case 0x165a: // (green steel)
5612 element = EL_STEEL_CHAR_P;
5615 case 0x165b: // (green steel)
5616 element = EL_STEEL_CHAR_Q;
5619 case 0x165c: // (green steel)
5620 element = EL_STEEL_CHAR_R;
5623 case 0x165d: // (green steel)
5624 element = EL_STEEL_CHAR_S;
5627 case 0x165e: // (green steel)
5628 element = EL_STEEL_CHAR_T;
5631 case 0x165f: // (green steel)
5632 element = EL_STEEL_CHAR_U;
5635 case 0x1660: // (green steel)
5636 element = EL_STEEL_CHAR_V;
5639 case 0x1661: // (green steel)
5640 element = EL_STEEL_CHAR_W;
5643 case 0x1662: // (green steel)
5644 element = EL_STEEL_CHAR_X;
5647 case 0x1663: // (green steel)
5648 element = EL_STEEL_CHAR_Y;
5651 case 0x1664: // (green steel)
5652 element = EL_STEEL_CHAR_Z;
5655 case 0x1665: // (green steel)
5656 element = EL_STEEL_CHAR_AUMLAUT;
5659 case 0x1666: // (green steel)
5660 element = EL_STEEL_CHAR_OUMLAUT;
5663 case 0x1667: // (green steel)
5664 element = EL_STEEL_CHAR_UUMLAUT;
5667 case 0x1668: // (green steel)
5668 element = EL_STEEL_CHAR_0;
5671 case 0x1669: // (green steel)
5672 element = EL_STEEL_CHAR_1;
5675 case 0x166a: // (green steel)
5676 element = EL_STEEL_CHAR_2;
5679 case 0x166b: // (green steel)
5680 element = EL_STEEL_CHAR_3;
5683 case 0x166c: // (green steel)
5684 element = EL_STEEL_CHAR_4;
5687 case 0x166d: // (green steel)
5688 element = EL_STEEL_CHAR_5;
5691 case 0x166e: // (green steel)
5692 element = EL_STEEL_CHAR_6;
5695 case 0x166f: // (green steel)
5696 element = EL_STEEL_CHAR_7;
5699 case 0x1670: // (green steel)
5700 element = EL_STEEL_CHAR_8;
5703 case 0x1671: // (green steel)
5704 element = EL_STEEL_CHAR_9;
5707 case 0x1672: // (green steel)
5708 element = EL_STEEL_CHAR_PERIOD;
5711 case 0x1673: // (green steel)
5712 element = EL_STEEL_CHAR_EXCLAM;
5715 case 0x1674: // (green steel)
5716 element = EL_STEEL_CHAR_COLON;
5719 case 0x1675: // (green steel)
5720 element = EL_STEEL_CHAR_LESS;
5723 case 0x1676: // (green steel)
5724 element = EL_STEEL_CHAR_GREATER;
5727 case 0x1677: // (green steel)
5728 element = EL_STEEL_CHAR_QUESTION;
5731 case 0x1678: // (green steel)
5732 element = EL_STEEL_CHAR_COPYRIGHT;
5735 case 0x1679: // (green steel)
5736 element = EL_STEEL_CHAR_UP;
5739 case 0x167a: // (green steel)
5740 element = EL_STEEL_CHAR_DOWN;
5743 case 0x167b: // (green steel)
5744 element = EL_STEEL_CHAR_BUTTON;
5747 case 0x167c: // (green steel)
5748 element = EL_STEEL_CHAR_PLUS;
5751 case 0x167d: // (green steel)
5752 element = EL_STEEL_CHAR_MINUS;
5755 case 0x167e: // (green steel)
5756 element = EL_STEEL_CHAR_APOSTROPHE;
5759 case 0x167f: // (green steel)
5760 element = EL_STEEL_CHAR_PARENLEFT;
5763 case 0x1680: // (green steel)
5764 element = EL_STEEL_CHAR_PARENRIGHT;
5767 case 0x1681: // gate (red)
5768 element = EL_EM_GATE_1;
5771 case 0x1682: // secret gate (red)
5772 element = EL_EM_GATE_1_GRAY;
5775 case 0x1683: // gate (yellow)
5776 element = EL_EM_GATE_2;
5779 case 0x1684: // secret gate (yellow)
5780 element = EL_EM_GATE_2_GRAY;
5783 case 0x1685: // gate (blue)
5784 element = EL_EM_GATE_4;
5787 case 0x1686: // secret gate (blue)
5788 element = EL_EM_GATE_4_GRAY;
5791 case 0x1687: // gate (green)
5792 element = EL_EM_GATE_3;
5795 case 0x1688: // secret gate (green)
5796 element = EL_EM_GATE_3_GRAY;
5799 case 0x1689: // gate (white)
5800 element = EL_DC_GATE_WHITE;
5803 case 0x168a: // secret gate (white)
5804 element = EL_DC_GATE_WHITE_GRAY;
5807 case 0x168b: // secret gate (no key)
5808 element = EL_DC_GATE_FAKE_GRAY;
5812 element = EL_ROBOT_WHEEL;
5816 element = EL_DC_TIMEGATE_SWITCH;
5820 element = EL_ACID_POOL_BOTTOM;
5824 element = EL_ACID_POOL_TOPLEFT;
5828 element = EL_ACID_POOL_TOPRIGHT;
5832 element = EL_ACID_POOL_BOTTOMLEFT;
5836 element = EL_ACID_POOL_BOTTOMRIGHT;
5840 element = EL_STEELWALL;
5844 element = EL_STEELWALL_SLIPPERY;
5847 case 0x1695: // steel wall (not round)
5848 element = EL_STEELWALL;
5851 case 0x1696: // steel wall (left)
5852 element = EL_DC_STEELWALL_1_LEFT;
5855 case 0x1697: // steel wall (bottom)
5856 element = EL_DC_STEELWALL_1_BOTTOM;
5859 case 0x1698: // steel wall (right)
5860 element = EL_DC_STEELWALL_1_RIGHT;
5863 case 0x1699: // steel wall (top)
5864 element = EL_DC_STEELWALL_1_TOP;
5867 case 0x169a: // steel wall (left/bottom)
5868 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5871 case 0x169b: // steel wall (right/bottom)
5872 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5875 case 0x169c: // steel wall (right/top)
5876 element = EL_DC_STEELWALL_1_TOPRIGHT;
5879 case 0x169d: // steel wall (left/top)
5880 element = EL_DC_STEELWALL_1_TOPLEFT;
5883 case 0x169e: // steel wall (right/bottom small)
5884 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5887 case 0x169f: // steel wall (left/bottom small)
5888 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5891 case 0x16a0: // steel wall (right/top small)
5892 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5895 case 0x16a1: // steel wall (left/top small)
5896 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5899 case 0x16a2: // steel wall (left/right)
5900 element = EL_DC_STEELWALL_1_VERTICAL;
5903 case 0x16a3: // steel wall (top/bottom)
5904 element = EL_DC_STEELWALL_1_HORIZONTAL;
5907 case 0x16a4: // steel wall 2 (left end)
5908 element = EL_DC_STEELWALL_2_LEFT;
5911 case 0x16a5: // steel wall 2 (right end)
5912 element = EL_DC_STEELWALL_2_RIGHT;
5915 case 0x16a6: // steel wall 2 (top end)
5916 element = EL_DC_STEELWALL_2_TOP;
5919 case 0x16a7: // steel wall 2 (bottom end)
5920 element = EL_DC_STEELWALL_2_BOTTOM;
5923 case 0x16a8: // steel wall 2 (left/right)
5924 element = EL_DC_STEELWALL_2_HORIZONTAL;
5927 case 0x16a9: // steel wall 2 (up/down)
5928 element = EL_DC_STEELWALL_2_VERTICAL;
5931 case 0x16aa: // steel wall 2 (mid)
5932 element = EL_DC_STEELWALL_2_MIDDLE;
5936 element = EL_SIGN_EXCLAMATION;
5940 element = EL_SIGN_RADIOACTIVITY;
5944 element = EL_SIGN_STOP;
5948 element = EL_SIGN_WHEELCHAIR;
5952 element = EL_SIGN_PARKING;
5956 element = EL_SIGN_NO_ENTRY;
5960 element = EL_SIGN_HEART;
5964 element = EL_SIGN_GIVE_WAY;
5968 element = EL_SIGN_ENTRY_FORBIDDEN;
5972 element = EL_SIGN_EMERGENCY_EXIT;
5976 element = EL_SIGN_YIN_YANG;
5980 element = EL_WALL_EMERALD;
5984 element = EL_WALL_DIAMOND;
5988 element = EL_WALL_PEARL;
5992 element = EL_WALL_CRYSTAL;
5996 element = EL_INVISIBLE_WALL;
6000 element = EL_INVISIBLE_STEELWALL;
6004 // EL_INVISIBLE_SAND
6007 element = EL_LIGHT_SWITCH;
6011 element = EL_ENVELOPE_1;
6015 if (element >= 0x0117 && element <= 0x036e) // (?)
6016 element = EL_DIAMOND;
6017 else if (element >= 0x042d && element <= 0x0684) // (?)
6018 element = EL_EMERALD;
6019 else if (element >= 0x157c && element <= 0x158b)
6021 else if (element >= 0x1590 && element <= 0x159f)
6022 element = EL_DC_LANDMINE;
6023 else if (element >= 0x16bc && element <= 0x16cb)
6024 element = EL_INVISIBLE_SAND;
6027 Warn("unknown Diamond Caves element 0x%04x", element);
6029 element = EL_UNKNOWN;
6034 return getMappedElement(element);
6037 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6039 byte header[DC_LEVEL_HEADER_SIZE];
6041 int envelope_header_pos = 62;
6042 int envelope_content_pos = 94;
6043 int level_name_pos = 251;
6044 int level_author_pos = 292;
6045 int envelope_header_len;
6046 int envelope_content_len;
6048 int level_author_len;
6050 int num_yamyam_contents;
6053 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6055 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6057 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6059 header[i * 2 + 0] = header_word >> 8;
6060 header[i * 2 + 1] = header_word & 0xff;
6063 // read some values from level header to check level decoding integrity
6064 fieldx = header[6] | (header[7] << 8);
6065 fieldy = header[8] | (header[9] << 8);
6066 num_yamyam_contents = header[60] | (header[61] << 8);
6068 // do some simple sanity checks to ensure that level was correctly decoded
6069 if (fieldx < 1 || fieldx > 256 ||
6070 fieldy < 1 || fieldy > 256 ||
6071 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6073 level->no_valid_file = TRUE;
6075 Warn("cannot decode level from stream -- using empty level");
6080 // maximum envelope header size is 31 bytes
6081 envelope_header_len = header[envelope_header_pos];
6082 // maximum envelope content size is 110 (156?) bytes
6083 envelope_content_len = header[envelope_content_pos];
6085 // maximum level title size is 40 bytes
6086 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6087 // maximum level author size is 30 (51?) bytes
6088 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6092 for (i = 0; i < envelope_header_len; i++)
6093 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6094 level->envelope[0].text[envelope_size++] =
6095 header[envelope_header_pos + 1 + i];
6097 if (envelope_header_len > 0 && envelope_content_len > 0)
6099 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6100 level->envelope[0].text[envelope_size++] = '\n';
6101 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6102 level->envelope[0].text[envelope_size++] = '\n';
6105 for (i = 0; i < envelope_content_len; i++)
6106 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6107 level->envelope[0].text[envelope_size++] =
6108 header[envelope_content_pos + 1 + i];
6110 level->envelope[0].text[envelope_size] = '\0';
6112 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6113 level->envelope[0].ysize = 10;
6114 level->envelope[0].autowrap = TRUE;
6115 level->envelope[0].centered = TRUE;
6117 for (i = 0; i < level_name_len; i++)
6118 level->name[i] = header[level_name_pos + 1 + i];
6119 level->name[level_name_len] = '\0';
6121 for (i = 0; i < level_author_len; i++)
6122 level->author[i] = header[level_author_pos + 1 + i];
6123 level->author[level_author_len] = '\0';
6125 num_yamyam_contents = header[60] | (header[61] << 8);
6126 level->num_yamyam_contents =
6127 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6129 for (i = 0; i < num_yamyam_contents; i++)
6131 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6133 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6134 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6136 if (i < MAX_ELEMENT_CONTENTS)
6137 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6141 fieldx = header[6] | (header[7] << 8);
6142 fieldy = header[8] | (header[9] << 8);
6143 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6144 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6146 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6148 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6149 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6151 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6152 level->field[x][y] = getMappedElement_DC(element_dc);
6155 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6156 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6157 level->field[x][y] = EL_PLAYER_1;
6159 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6160 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6161 level->field[x][y] = EL_PLAYER_2;
6163 level->gems_needed = header[18] | (header[19] << 8);
6165 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6166 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6167 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6168 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6169 level->score[SC_NUT] = header[28] | (header[29] << 8);
6170 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6171 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6172 level->score[SC_BUG] = header[34] | (header[35] << 8);
6173 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6174 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6175 level->score[SC_KEY] = header[40] | (header[41] << 8);
6176 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6178 level->time = header[44] | (header[45] << 8);
6180 level->amoeba_speed = header[46] | (header[47] << 8);
6181 level->time_light = header[48] | (header[49] << 8);
6182 level->time_timegate = header[50] | (header[51] << 8);
6183 level->time_wheel = header[52] | (header[53] << 8);
6184 level->time_magic_wall = header[54] | (header[55] << 8);
6185 level->extra_time = header[56] | (header[57] << 8);
6186 level->shield_normal_time = header[58] | (header[59] << 8);
6188 // shield and extra time elements do not have a score
6189 level->score[SC_SHIELD] = 0;
6190 level->extra_time_score = 0;
6192 // set time for normal and deadly shields to the same value
6193 level->shield_deadly_time = level->shield_normal_time;
6195 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6196 // can slip down from flat walls, like normal walls and steel walls
6197 level->em_slippery_gems = TRUE;
6199 // time score is counted for each 10 seconds left in Diamond Caves levels
6200 level->time_score_base = 10;
6203 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6204 struct LevelFileInfo *level_file_info,
6205 boolean level_info_only)
6207 char *filename = level_file_info->filename;
6209 int num_magic_bytes = 8;
6210 char magic_bytes[num_magic_bytes + 1];
6211 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6213 if (!(file = openFile(filename, MODE_READ)))
6215 level->no_valid_file = TRUE;
6217 if (!level_info_only)
6218 Warn("cannot read level '%s' -- using empty level", filename);
6223 // fseek(file, 0x0000, SEEK_SET);
6225 if (level_file_info->packed)
6227 // read "magic bytes" from start of file
6228 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6229 magic_bytes[0] = '\0';
6231 // check "magic bytes" for correct file format
6232 if (!strPrefix(magic_bytes, "DC2"))
6234 level->no_valid_file = TRUE;
6236 Warn("unknown DC level file '%s' -- using empty level", filename);
6241 if (strPrefix(magic_bytes, "DC2Win95") ||
6242 strPrefix(magic_bytes, "DC2Win98"))
6244 int position_first_level = 0x00fa;
6245 int extra_bytes = 4;
6248 // advance file stream to first level inside the level package
6249 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6251 // each block of level data is followed by block of non-level data
6252 num_levels_to_skip *= 2;
6254 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6255 while (num_levels_to_skip >= 0)
6257 // advance file stream to next level inside the level package
6258 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6260 level->no_valid_file = TRUE;
6262 Warn("cannot fseek in file '%s' -- using empty level", filename);
6267 // skip apparently unused extra bytes following each level
6268 ReadUnusedBytesFromFile(file, extra_bytes);
6270 // read size of next level in level package
6271 skip_bytes = getFile32BitLE(file);
6273 num_levels_to_skip--;
6278 level->no_valid_file = TRUE;
6280 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6286 LoadLevelFromFileStream_DC(file, level);
6292 // ----------------------------------------------------------------------------
6293 // functions for loading SB level
6294 // ----------------------------------------------------------------------------
6296 int getMappedElement_SB(int element_ascii, boolean use_ces)
6304 sb_element_mapping[] =
6306 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6307 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6308 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6309 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6310 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6311 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6312 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6313 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6320 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6321 if (element_ascii == sb_element_mapping[i].ascii)
6322 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6324 return EL_UNDEFINED;
6327 static void SetLevelSettings_SB(struct LevelInfo *level)
6331 level->use_step_counter = TRUE;
6334 level->score[SC_TIME_BONUS] = 0;
6335 level->time_score_base = 1;
6336 level->rate_time_over_score = TRUE;
6339 level->auto_exit_sokoban = TRUE;
6342 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6343 struct LevelFileInfo *level_file_info,
6344 boolean level_info_only)
6346 char *filename = level_file_info->filename;
6347 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6348 char last_comment[MAX_LINE_LEN];
6349 char level_name[MAX_LINE_LEN];
6352 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6353 boolean read_continued_line = FALSE;
6354 boolean reading_playfield = FALSE;
6355 boolean got_valid_playfield_line = FALSE;
6356 boolean invalid_playfield_char = FALSE;
6357 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6358 int file_level_nr = 0;
6359 int x = 0, y = 0; // initialized to make compilers happy
6361 last_comment[0] = '\0';
6362 level_name[0] = '\0';
6364 if (!(file = openFile(filename, MODE_READ)))
6366 level->no_valid_file = TRUE;
6368 if (!level_info_only)
6369 Warn("cannot read level '%s' -- using empty level", filename);
6374 while (!checkEndOfFile(file))
6376 // level successfully read, but next level may follow here
6377 if (!got_valid_playfield_line && reading_playfield)
6379 // read playfield from single level file -- skip remaining file
6380 if (!level_file_info->packed)
6383 if (file_level_nr >= num_levels_to_skip)
6388 last_comment[0] = '\0';
6389 level_name[0] = '\0';
6391 reading_playfield = FALSE;
6394 got_valid_playfield_line = FALSE;
6396 // read next line of input file
6397 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6400 // cut trailing line break (this can be newline and/or carriage return)
6401 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6402 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6405 // copy raw input line for later use (mainly debugging output)
6406 strcpy(line_raw, line);
6408 if (read_continued_line)
6410 // append new line to existing line, if there is enough space
6411 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6412 strcat(previous_line, line_ptr);
6414 strcpy(line, previous_line); // copy storage buffer to line
6416 read_continued_line = FALSE;
6419 // if the last character is '\', continue at next line
6420 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6422 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6423 strcpy(previous_line, line); // copy line to storage buffer
6425 read_continued_line = TRUE;
6431 if (line[0] == '\0')
6434 // extract comment text from comment line
6437 for (line_ptr = line; *line_ptr; line_ptr++)
6438 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6441 strcpy(last_comment, line_ptr);
6446 // extract level title text from line containing level title
6447 if (line[0] == '\'')
6449 strcpy(level_name, &line[1]);
6451 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6452 level_name[strlen(level_name) - 1] = '\0';
6457 // skip lines containing only spaces (or empty lines)
6458 for (line_ptr = line; *line_ptr; line_ptr++)
6459 if (*line_ptr != ' ')
6461 if (*line_ptr == '\0')
6464 // at this point, we have found a line containing part of a playfield
6466 got_valid_playfield_line = TRUE;
6468 if (!reading_playfield)
6470 reading_playfield = TRUE;
6471 invalid_playfield_char = FALSE;
6473 for (x = 0; x < MAX_LEV_FIELDX; x++)
6474 for (y = 0; y < MAX_LEV_FIELDY; y++)
6475 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6480 // start with topmost tile row
6484 // skip playfield line if larger row than allowed
6485 if (y >= MAX_LEV_FIELDY)
6488 // start with leftmost tile column
6491 // read playfield elements from line
6492 for (line_ptr = line; *line_ptr; line_ptr++)
6494 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6496 // stop parsing playfield line if larger column than allowed
6497 if (x >= MAX_LEV_FIELDX)
6500 if (mapped_sb_element == EL_UNDEFINED)
6502 invalid_playfield_char = TRUE;
6507 level->field[x][y] = mapped_sb_element;
6509 // continue with next tile column
6512 level->fieldx = MAX(x, level->fieldx);
6515 if (invalid_playfield_char)
6517 // if first playfield line, treat invalid lines as comment lines
6519 reading_playfield = FALSE;
6524 // continue with next tile row
6532 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6533 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6535 if (!reading_playfield)
6537 level->no_valid_file = TRUE;
6539 Warn("cannot read level '%s' -- using empty level", filename);
6544 if (*level_name != '\0')
6546 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6547 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6549 else if (*last_comment != '\0')
6551 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6552 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6556 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6559 // set all empty fields beyond the border walls to invisible steel wall
6560 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6562 if ((x == 0 || x == level->fieldx - 1 ||
6563 y == 0 || y == level->fieldy - 1) &&
6564 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6565 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6566 level->field, level->fieldx, level->fieldy);
6569 // set special level settings for Sokoban levels
6570 SetLevelSettings_SB(level);
6572 if (load_xsb_to_ces)
6574 // special global settings can now be set in level template
6575 level->use_custom_template = TRUE;
6580 // -------------------------------------------------------------------------
6581 // functions for handling native levels
6582 // -------------------------------------------------------------------------
6584 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6585 struct LevelFileInfo *level_file_info,
6586 boolean level_info_only)
6590 // determine position of requested level inside level package
6591 if (level_file_info->packed)
6592 pos = level_file_info->nr - leveldir_current->first_level;
6594 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6595 level->no_valid_file = TRUE;
6598 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6599 struct LevelFileInfo *level_file_info,
6600 boolean level_info_only)
6602 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6603 level->no_valid_file = TRUE;
6606 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6607 struct LevelFileInfo *level_file_info,
6608 boolean level_info_only)
6612 // determine position of requested level inside level package
6613 if (level_file_info->packed)
6614 pos = level_file_info->nr - leveldir_current->first_level;
6616 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6617 level->no_valid_file = TRUE;
6620 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6621 struct LevelFileInfo *level_file_info,
6622 boolean level_info_only)
6624 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6625 level->no_valid_file = TRUE;
6628 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6630 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6631 CopyNativeLevel_RND_to_BD(level);
6632 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6633 CopyNativeLevel_RND_to_EM(level);
6634 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6635 CopyNativeLevel_RND_to_SP(level);
6636 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6637 CopyNativeLevel_RND_to_MM(level);
6640 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6642 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6643 CopyNativeLevel_BD_to_RND(level);
6644 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6645 CopyNativeLevel_EM_to_RND(level);
6646 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6647 CopyNativeLevel_SP_to_RND(level);
6648 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6649 CopyNativeLevel_MM_to_RND(level);
6652 void SaveNativeLevel(struct LevelInfo *level)
6654 // saving native level files only supported for some game engines
6655 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6656 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6659 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6660 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6661 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6662 char *filename = getLevelFilenameFromBasename(basename);
6664 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6667 boolean success = FALSE;
6669 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6671 CopyNativeLevel_RND_to_BD(level);
6672 // CopyNativeTape_RND_to_BD(level);
6674 success = SaveNativeLevel_BD(filename);
6676 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6678 CopyNativeLevel_RND_to_SP(level);
6679 CopyNativeTape_RND_to_SP(level);
6681 success = SaveNativeLevel_SP(filename);
6685 Request("Native level file saved!", REQ_CONFIRM);
6687 Request("Failed to save native level file!", REQ_CONFIRM);
6691 // ----------------------------------------------------------------------------
6692 // functions for loading generic level
6693 // ----------------------------------------------------------------------------
6695 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6696 struct LevelFileInfo *level_file_info,
6697 boolean level_info_only)
6699 // always start with reliable default values
6700 setLevelInfoToDefaults(level, level_info_only, TRUE);
6702 switch (level_file_info->type)
6704 case LEVEL_FILE_TYPE_RND:
6705 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6708 case LEVEL_FILE_TYPE_BD:
6709 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6710 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6713 case LEVEL_FILE_TYPE_EM:
6714 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6715 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6718 case LEVEL_FILE_TYPE_SP:
6719 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6720 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6723 case LEVEL_FILE_TYPE_MM:
6724 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6725 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6728 case LEVEL_FILE_TYPE_DC:
6729 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6732 case LEVEL_FILE_TYPE_SB:
6733 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6737 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6741 // if level file is invalid, restore level structure to default values
6742 if (level->no_valid_file)
6743 setLevelInfoToDefaults(level, level_info_only, FALSE);
6745 if (check_special_flags("use_native_bd_game_engine"))
6746 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6748 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6749 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6751 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6752 CopyNativeLevel_Native_to_RND(level);
6755 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6757 static struct LevelFileInfo level_file_info;
6759 // always start with reliable default values
6760 setFileInfoToDefaults(&level_file_info);
6762 level_file_info.nr = 0; // unknown level number
6763 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6765 setString(&level_file_info.filename, filename);
6767 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6770 static void LoadLevel_InitVersion(struct LevelInfo *level)
6774 if (leveldir_current == NULL) // only when dumping level
6777 // all engine modifications also valid for levels which use latest engine
6778 if (level->game_version < VERSION_IDENT(3,2,0,5))
6780 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6781 level->time_score_base = 10;
6784 if (leveldir_current->latest_engine)
6786 // ---------- use latest game engine --------------------------------------
6788 /* For all levels which are forced to use the latest game engine version
6789 (normally all but user contributed, private and undefined levels), set
6790 the game engine version to the actual version; this allows for actual
6791 corrections in the game engine to take effect for existing, converted
6792 levels (from "classic" or other existing games) to make the emulation
6793 of the corresponding game more accurate, while (hopefully) not breaking
6794 existing levels created from other players. */
6796 level->game_version = GAME_VERSION_ACTUAL;
6798 /* Set special EM style gems behaviour: EM style gems slip down from
6799 normal, steel and growing wall. As this is a more fundamental change,
6800 it seems better to set the default behaviour to "off" (as it is more
6801 natural) and make it configurable in the level editor (as a property
6802 of gem style elements). Already existing converted levels (neither
6803 private nor contributed levels) are changed to the new behaviour. */
6805 if (level->file_version < FILE_VERSION_2_0)
6806 level->em_slippery_gems = TRUE;
6811 // ---------- use game engine the level was created with --------------------
6813 /* For all levels which are not forced to use the latest game engine
6814 version (normally user contributed, private and undefined levels),
6815 use the version of the game engine the levels were created for.
6817 Since 2.0.1, the game engine version is now directly stored
6818 in the level file (chunk "VERS"), so there is no need anymore
6819 to set the game version from the file version (except for old,
6820 pre-2.0 levels, where the game version is still taken from the
6821 file format version used to store the level -- see above). */
6823 // player was faster than enemies in 1.0.0 and before
6824 if (level->file_version == FILE_VERSION_1_0)
6825 for (i = 0; i < MAX_PLAYERS; i++)
6826 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6828 // default behaviour for EM style gems was "slippery" only in 2.0.1
6829 if (level->game_version == VERSION_IDENT(2,0,1,0))
6830 level->em_slippery_gems = TRUE;
6832 // springs could be pushed over pits before (pre-release version) 2.2.0
6833 if (level->game_version < VERSION_IDENT(2,2,0,0))
6834 level->use_spring_bug = TRUE;
6836 if (level->game_version < VERSION_IDENT(3,2,0,5))
6838 // time orb caused limited time in endless time levels before 3.2.0-5
6839 level->use_time_orb_bug = TRUE;
6841 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6842 level->block_snap_field = FALSE;
6844 // extra time score was same value as time left score before 3.2.0-5
6845 level->extra_time_score = level->score[SC_TIME_BONUS];
6848 if (level->game_version < VERSION_IDENT(3,2,0,7))
6850 // default behaviour for snapping was "not continuous" before 3.2.0-7
6851 level->continuous_snapping = FALSE;
6854 // only few elements were able to actively move into acid before 3.1.0
6855 // trigger settings did not exist before 3.1.0; set to default "any"
6856 if (level->game_version < VERSION_IDENT(3,1,0,0))
6858 // correct "can move into acid" settings (all zero in old levels)
6860 level->can_move_into_acid_bits = 0; // nothing can move into acid
6861 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6863 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6864 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6865 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6866 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6868 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6869 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6871 // correct trigger settings (stored as zero == "none" in old levels)
6873 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6875 int element = EL_CUSTOM_START + i;
6876 struct ElementInfo *ei = &element_info[element];
6878 for (j = 0; j < ei->num_change_pages; j++)
6880 struct ElementChangeInfo *change = &ei->change_page[j];
6882 change->trigger_player = CH_PLAYER_ANY;
6883 change->trigger_page = CH_PAGE_ANY;
6888 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6890 int element = EL_CUSTOM_256;
6891 struct ElementInfo *ei = &element_info[element];
6892 struct ElementChangeInfo *change = &ei->change_page[0];
6894 /* This is needed to fix a problem that was caused by a bugfix in function
6895 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6896 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6897 not replace walkable elements, but instead just placed the player on it,
6898 without placing the Sokoban field under the player). Unfortunately, this
6899 breaks "Snake Bite" style levels when the snake is halfway through a door
6900 that just closes (the snake head is still alive and can be moved in this
6901 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6902 player (without Sokoban element) which then gets killed as designed). */
6904 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6905 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6906 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6907 change->target_element = EL_PLAYER_1;
6910 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6911 if (level->game_version < VERSION_IDENT(3,2,5,0))
6913 /* This is needed to fix a problem that was caused by a bugfix in function
6914 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6915 corrects the behaviour when a custom element changes to another custom
6916 element with a higher element number that has change actions defined.
6917 Normally, only one change per frame is allowed for custom elements.
6918 Therefore, it is checked if a custom element already changed in the
6919 current frame; if it did, subsequent changes are suppressed.
6920 Unfortunately, this is only checked for element changes, but not for
6921 change actions, which are still executed. As the function above loops
6922 through all custom elements from lower to higher, an element change
6923 resulting in a lower CE number won't be checked again, while a target
6924 element with a higher number will also be checked, and potential change
6925 actions will get executed for this CE, too (which is wrong), while
6926 further changes are ignored (which is correct). As this bugfix breaks
6927 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6928 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6929 behaviour for existing levels and tapes that make use of this bug */
6931 level->use_action_after_change_bug = TRUE;
6934 // not centering level after relocating player was default only in 3.2.3
6935 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6936 level->shifted_relocation = TRUE;
6938 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6939 if (level->game_version < VERSION_IDENT(3,2,6,0))
6940 level->em_explodes_by_fire = TRUE;
6942 // levels were solved by the first player entering an exit up to 4.1.0.0
6943 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6944 level->solved_by_one_player = TRUE;
6946 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6947 if (level->game_version < VERSION_IDENT(4,1,1,1))
6948 level->use_life_bugs = TRUE;
6950 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6951 if (level->game_version < VERSION_IDENT(4,1,1,1))
6952 level->sb_objects_needed = FALSE;
6954 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6955 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6956 level->finish_dig_collect = FALSE;
6958 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6959 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6960 level->keep_walkable_ce = TRUE;
6963 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6965 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6968 // check if this level is (not) a Sokoban level
6969 for (y = 0; y < level->fieldy; y++)
6970 for (x = 0; x < level->fieldx; x++)
6971 if (!IS_SB_ELEMENT(Tile[x][y]))
6972 is_sokoban_level = FALSE;
6974 if (is_sokoban_level)
6976 // set special level settings for Sokoban levels
6977 SetLevelSettings_SB(level);
6981 static void LoadLevel_InitSettings(struct LevelInfo *level)
6983 // adjust level settings for (non-native) Sokoban-style levels
6984 LoadLevel_InitSettings_SB(level);
6986 // rename levels with title "nameless level" or if renaming is forced
6987 if (leveldir_current->empty_level_name != NULL &&
6988 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6989 leveldir_current->force_level_name))
6990 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6991 leveldir_current->empty_level_name, level_nr);
6994 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6998 // map elements that have changed in newer versions
6999 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7000 level->game_version);
7001 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7002 for (x = 0; x < 3; x++)
7003 for (y = 0; y < 3; y++)
7004 level->yamyam_content[i].e[x][y] =
7005 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7006 level->game_version);
7010 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7014 // map custom element change events that have changed in newer versions
7015 // (these following values were accidentally changed in version 3.0.1)
7016 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7017 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7019 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7021 int element = EL_CUSTOM_START + i;
7023 // order of checking and copying events to be mapped is important
7024 // (do not change the start and end value -- they are constant)
7025 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7027 if (HAS_CHANGE_EVENT(element, j - 2))
7029 SET_CHANGE_EVENT(element, j - 2, FALSE);
7030 SET_CHANGE_EVENT(element, j, TRUE);
7034 // order of checking and copying events to be mapped is important
7035 // (do not change the start and end value -- they are constant)
7036 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7038 if (HAS_CHANGE_EVENT(element, j - 1))
7040 SET_CHANGE_EVENT(element, j - 1, FALSE);
7041 SET_CHANGE_EVENT(element, j, TRUE);
7047 // initialize "can_change" field for old levels with only one change page
7048 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7050 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7052 int element = EL_CUSTOM_START + i;
7054 if (CAN_CHANGE(element))
7055 element_info[element].change->can_change = TRUE;
7059 // correct custom element values (for old levels without these options)
7060 if (level->game_version < VERSION_IDENT(3,1,1,0))
7062 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7064 int element = EL_CUSTOM_START + i;
7065 struct ElementInfo *ei = &element_info[element];
7067 if (ei->access_direction == MV_NO_DIRECTION)
7068 ei->access_direction = MV_ALL_DIRECTIONS;
7072 // correct custom element values (fix invalid values for all versions)
7075 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7077 int element = EL_CUSTOM_START + i;
7078 struct ElementInfo *ei = &element_info[element];
7080 for (j = 0; j < ei->num_change_pages; j++)
7082 struct ElementChangeInfo *change = &ei->change_page[j];
7084 if (change->trigger_player == CH_PLAYER_NONE)
7085 change->trigger_player = CH_PLAYER_ANY;
7087 if (change->trigger_side == CH_SIDE_NONE)
7088 change->trigger_side = CH_SIDE_ANY;
7093 // initialize "can_explode" field for old levels which did not store this
7094 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7095 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7097 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7099 int element = EL_CUSTOM_START + i;
7101 if (EXPLODES_1X1_OLD(element))
7102 element_info[element].explosion_type = EXPLODES_1X1;
7104 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7105 EXPLODES_SMASHED(element) ||
7106 EXPLODES_IMPACT(element)));
7110 // correct previously hard-coded move delay values for maze runner style
7111 if (level->game_version < VERSION_IDENT(3,1,1,0))
7113 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7115 int element = EL_CUSTOM_START + i;
7117 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7119 // previously hard-coded and therefore ignored
7120 element_info[element].move_delay_fixed = 9;
7121 element_info[element].move_delay_random = 0;
7126 // set some other uninitialized values of custom elements in older levels
7127 if (level->game_version < VERSION_IDENT(3,1,0,0))
7129 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7131 int element = EL_CUSTOM_START + i;
7133 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7135 element_info[element].explosion_delay = 17;
7136 element_info[element].ignition_delay = 8;
7140 // set mouse click change events to work for left/middle/right mouse button
7141 if (level->game_version < VERSION_IDENT(4,2,3,0))
7143 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7145 int element = EL_CUSTOM_START + i;
7146 struct ElementInfo *ei = &element_info[element];
7148 for (j = 0; j < ei->num_change_pages; j++)
7150 struct ElementChangeInfo *change = &ei->change_page[j];
7152 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7153 change->has_event[CE_PRESSED_BY_MOUSE] ||
7154 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7155 change->has_event[CE_MOUSE_PRESSED_ON_X])
7156 change->trigger_side = CH_SIDE_ANY;
7162 static void LoadLevel_InitElements(struct LevelInfo *level)
7164 LoadLevel_InitStandardElements(level);
7166 if (level->file_has_custom_elements)
7167 LoadLevel_InitCustomElements(level);
7169 // initialize element properties for level editor etc.
7170 InitElementPropertiesEngine(level->game_version);
7171 InitElementPropertiesGfxElement();
7174 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7178 // map elements that have changed in newer versions
7179 for (y = 0; y < level->fieldy; y++)
7180 for (x = 0; x < level->fieldx; x++)
7181 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7182 level->game_version);
7184 // clear unused playfield data (nicer if level gets resized in editor)
7185 for (x = 0; x < MAX_LEV_FIELDX; x++)
7186 for (y = 0; y < MAX_LEV_FIELDY; y++)
7187 if (x >= level->fieldx || y >= level->fieldy)
7188 level->field[x][y] = EL_EMPTY;
7190 // copy elements to runtime playfield array
7191 for (x = 0; x < MAX_LEV_FIELDX; x++)
7192 for (y = 0; y < MAX_LEV_FIELDY; y++)
7193 Tile[x][y] = level->field[x][y];
7195 // initialize level size variables for faster access
7196 lev_fieldx = level->fieldx;
7197 lev_fieldy = level->fieldy;
7199 // determine border element for this level
7200 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7201 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7206 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7208 struct LevelFileInfo *level_file_info = &level->file_info;
7210 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7211 CopyNativeLevel_RND_to_Native(level);
7214 static void LoadLevelTemplate_LoadAndInit(void)
7216 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7218 LoadLevel_InitVersion(&level_template);
7219 LoadLevel_InitElements(&level_template);
7220 LoadLevel_InitSettings(&level_template);
7222 ActivateLevelTemplate();
7225 void LoadLevelTemplate(int nr)
7227 if (!fileExists(getGlobalLevelTemplateFilename()))
7229 Warn("no level template found for this level");
7234 setLevelFileInfo(&level_template.file_info, nr);
7236 LoadLevelTemplate_LoadAndInit();
7239 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7241 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7243 LoadLevelTemplate_LoadAndInit();
7246 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7248 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7250 if (level.use_custom_template)
7252 if (network_level != NULL)
7253 LoadNetworkLevelTemplate(network_level);
7255 LoadLevelTemplate(-1);
7258 LoadLevel_InitVersion(&level);
7259 LoadLevel_InitElements(&level);
7260 LoadLevel_InitPlayfield(&level);
7261 LoadLevel_InitSettings(&level);
7263 LoadLevel_InitNativeEngines(&level);
7266 void LoadLevel(int nr)
7268 SetLevelSetInfo(leveldir_current->identifier, nr);
7270 setLevelFileInfo(&level.file_info, nr);
7272 LoadLevel_LoadAndInit(NULL);
7275 void LoadLevelInfoOnly(int nr)
7277 setLevelFileInfo(&level.file_info, nr);
7279 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7282 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7284 SetLevelSetInfo(network_level->leveldir_identifier,
7285 network_level->file_info.nr);
7287 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7289 LoadLevel_LoadAndInit(network_level);
7292 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7296 chunk_size += putFileVersion(file, level->file_version);
7297 chunk_size += putFileVersion(file, level->game_version);
7302 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7306 chunk_size += putFile16BitBE(file, level->creation_date.year);
7307 chunk_size += putFile8Bit(file, level->creation_date.month);
7308 chunk_size += putFile8Bit(file, level->creation_date.day);
7313 #if ENABLE_HISTORIC_CHUNKS
7314 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7318 putFile8Bit(file, level->fieldx);
7319 putFile8Bit(file, level->fieldy);
7321 putFile16BitBE(file, level->time);
7322 putFile16BitBE(file, level->gems_needed);
7324 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7325 putFile8Bit(file, level->name[i]);
7327 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7328 putFile8Bit(file, level->score[i]);
7330 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7331 for (y = 0; y < 3; y++)
7332 for (x = 0; x < 3; x++)
7333 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7334 level->yamyam_content[i].e[x][y]));
7335 putFile8Bit(file, level->amoeba_speed);
7336 putFile8Bit(file, level->time_magic_wall);
7337 putFile8Bit(file, level->time_wheel);
7338 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7339 level->amoeba_content));
7340 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7341 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7342 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7343 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7345 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7347 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7348 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7349 putFile32BitBE(file, level->can_move_into_acid_bits);
7350 putFile8Bit(file, level->dont_collide_with_bits);
7352 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7353 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7355 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7356 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7357 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7359 putFile8Bit(file, level->game_engine_type);
7361 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7365 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7370 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7371 chunk_size += putFile8Bit(file, level->name[i]);
7376 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7381 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7382 chunk_size += putFile8Bit(file, level->author[i]);
7387 #if ENABLE_HISTORIC_CHUNKS
7388 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7393 for (y = 0; y < level->fieldy; y++)
7394 for (x = 0; x < level->fieldx; x++)
7395 if (level->encoding_16bit_field)
7396 chunk_size += putFile16BitBE(file, level->field[x][y]);
7398 chunk_size += putFile8Bit(file, level->field[x][y]);
7404 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7409 for (y = 0; y < level->fieldy; y++)
7410 for (x = 0; x < level->fieldx; x++)
7411 chunk_size += putFile16BitBE(file, level->field[x][y]);
7416 #if ENABLE_HISTORIC_CHUNKS
7417 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7421 putFile8Bit(file, EL_YAMYAM);
7422 putFile8Bit(file, level->num_yamyam_contents);
7423 putFile8Bit(file, 0);
7424 putFile8Bit(file, 0);
7426 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7427 for (y = 0; y < 3; y++)
7428 for (x = 0; x < 3; x++)
7429 if (level->encoding_16bit_field)
7430 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7432 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7436 #if ENABLE_HISTORIC_CHUNKS
7437 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7440 int num_contents, content_xsize, content_ysize;
7441 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7443 if (element == EL_YAMYAM)
7445 num_contents = level->num_yamyam_contents;
7449 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7450 for (y = 0; y < 3; y++)
7451 for (x = 0; x < 3; x++)
7452 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7454 else if (element == EL_BD_AMOEBA)
7460 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7461 for (y = 0; y < 3; y++)
7462 for (x = 0; x < 3; x++)
7463 content_array[i][x][y] = EL_EMPTY;
7464 content_array[0][0][0] = level->amoeba_content;
7468 // chunk header already written -- write empty chunk data
7469 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7471 Warn("cannot save content for element '%d'", element);
7476 putFile16BitBE(file, element);
7477 putFile8Bit(file, num_contents);
7478 putFile8Bit(file, content_xsize);
7479 putFile8Bit(file, content_ysize);
7481 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7483 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7484 for (y = 0; y < 3; y++)
7485 for (x = 0; x < 3; x++)
7486 putFile16BitBE(file, content_array[i][x][y]);
7490 #if ENABLE_HISTORIC_CHUNKS
7491 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7493 int envelope_nr = element - EL_ENVELOPE_1;
7494 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7498 chunk_size += putFile16BitBE(file, element);
7499 chunk_size += putFile16BitBE(file, envelope_len);
7500 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7501 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7503 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7504 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7506 for (i = 0; i < envelope_len; i++)
7507 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7513 #if ENABLE_HISTORIC_CHUNKS
7514 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7515 int num_changed_custom_elements)
7519 putFile16BitBE(file, num_changed_custom_elements);
7521 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7523 int element = EL_CUSTOM_START + i;
7525 struct ElementInfo *ei = &element_info[element];
7527 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7529 if (check < num_changed_custom_elements)
7531 putFile16BitBE(file, element);
7532 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7539 if (check != num_changed_custom_elements) // should not happen
7540 Warn("inconsistent number of custom element properties");
7544 #if ENABLE_HISTORIC_CHUNKS
7545 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7546 int num_changed_custom_elements)
7550 putFile16BitBE(file, num_changed_custom_elements);
7552 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7554 int element = EL_CUSTOM_START + i;
7556 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7558 if (check < num_changed_custom_elements)
7560 putFile16BitBE(file, element);
7561 putFile16BitBE(file, element_info[element].change->target_element);
7568 if (check != num_changed_custom_elements) // should not happen
7569 Warn("inconsistent number of custom target elements");
7573 #if ENABLE_HISTORIC_CHUNKS
7574 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7575 int num_changed_custom_elements)
7577 int i, j, x, y, check = 0;
7579 putFile16BitBE(file, num_changed_custom_elements);
7581 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7583 int element = EL_CUSTOM_START + i;
7584 struct ElementInfo *ei = &element_info[element];
7586 if (ei->modified_settings)
7588 if (check < num_changed_custom_elements)
7590 putFile16BitBE(file, element);
7592 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7593 putFile8Bit(file, ei->description[j]);
7595 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7597 // some free bytes for future properties and padding
7598 WriteUnusedBytesToFile(file, 7);
7600 putFile8Bit(file, ei->use_gfx_element);
7601 putFile16BitBE(file, ei->gfx_element_initial);
7603 putFile8Bit(file, ei->collect_score_initial);
7604 putFile8Bit(file, ei->collect_count_initial);
7606 putFile16BitBE(file, ei->push_delay_fixed);
7607 putFile16BitBE(file, ei->push_delay_random);
7608 putFile16BitBE(file, ei->move_delay_fixed);
7609 putFile16BitBE(file, ei->move_delay_random);
7611 putFile16BitBE(file, ei->move_pattern);
7612 putFile8Bit(file, ei->move_direction_initial);
7613 putFile8Bit(file, ei->move_stepsize);
7615 for (y = 0; y < 3; y++)
7616 for (x = 0; x < 3; x++)
7617 putFile16BitBE(file, ei->content.e[x][y]);
7619 putFile32BitBE(file, ei->change->events);
7621 putFile16BitBE(file, ei->change->target_element);
7623 putFile16BitBE(file, ei->change->delay_fixed);
7624 putFile16BitBE(file, ei->change->delay_random);
7625 putFile16BitBE(file, ei->change->delay_frames);
7627 putFile16BitBE(file, ei->change->initial_trigger_element);
7629 putFile8Bit(file, ei->change->explode);
7630 putFile8Bit(file, ei->change->use_target_content);
7631 putFile8Bit(file, ei->change->only_if_complete);
7632 putFile8Bit(file, ei->change->use_random_replace);
7634 putFile8Bit(file, ei->change->random_percentage);
7635 putFile8Bit(file, ei->change->replace_when);
7637 for (y = 0; y < 3; y++)
7638 for (x = 0; x < 3; x++)
7639 putFile16BitBE(file, ei->change->content.e[x][y]);
7641 putFile8Bit(file, ei->slippery_type);
7643 // some free bytes for future properties and padding
7644 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7651 if (check != num_changed_custom_elements) // should not happen
7652 Warn("inconsistent number of custom element properties");
7656 #if ENABLE_HISTORIC_CHUNKS
7657 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7659 struct ElementInfo *ei = &element_info[element];
7662 // ---------- custom element base property values (96 bytes) ----------------
7664 putFile16BitBE(file, element);
7666 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7667 putFile8Bit(file, ei->description[i]);
7669 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7671 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7673 putFile8Bit(file, ei->num_change_pages);
7675 putFile16BitBE(file, ei->ce_value_fixed_initial);
7676 putFile16BitBE(file, ei->ce_value_random_initial);
7677 putFile8Bit(file, ei->use_last_ce_value);
7679 putFile8Bit(file, ei->use_gfx_element);
7680 putFile16BitBE(file, ei->gfx_element_initial);
7682 putFile8Bit(file, ei->collect_score_initial);
7683 putFile8Bit(file, ei->collect_count_initial);
7685 putFile8Bit(file, ei->drop_delay_fixed);
7686 putFile8Bit(file, ei->push_delay_fixed);
7687 putFile8Bit(file, ei->drop_delay_random);
7688 putFile8Bit(file, ei->push_delay_random);
7689 putFile16BitBE(file, ei->move_delay_fixed);
7690 putFile16BitBE(file, ei->move_delay_random);
7692 // bits 0 - 15 of "move_pattern" ...
7693 putFile16BitBE(file, ei->move_pattern & 0xffff);
7694 putFile8Bit(file, ei->move_direction_initial);
7695 putFile8Bit(file, ei->move_stepsize);
7697 putFile8Bit(file, ei->slippery_type);
7699 for (y = 0; y < 3; y++)
7700 for (x = 0; x < 3; x++)
7701 putFile16BitBE(file, ei->content.e[x][y]);
7703 putFile16BitBE(file, ei->move_enter_element);
7704 putFile16BitBE(file, ei->move_leave_element);
7705 putFile8Bit(file, ei->move_leave_type);
7707 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7708 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7710 putFile8Bit(file, ei->access_direction);
7712 putFile8Bit(file, ei->explosion_delay);
7713 putFile8Bit(file, ei->ignition_delay);
7714 putFile8Bit(file, ei->explosion_type);
7716 // some free bytes for future custom property values and padding
7717 WriteUnusedBytesToFile(file, 1);
7719 // ---------- change page property values (48 bytes) ------------------------
7721 for (i = 0; i < ei->num_change_pages; i++)
7723 struct ElementChangeInfo *change = &ei->change_page[i];
7724 unsigned int event_bits;
7726 // bits 0 - 31 of "has_event[]" ...
7728 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7729 if (change->has_event[j])
7730 event_bits |= (1u << j);
7731 putFile32BitBE(file, event_bits);
7733 putFile16BitBE(file, change->target_element);
7735 putFile16BitBE(file, change->delay_fixed);
7736 putFile16BitBE(file, change->delay_random);
7737 putFile16BitBE(file, change->delay_frames);
7739 putFile16BitBE(file, change->initial_trigger_element);
7741 putFile8Bit(file, change->explode);
7742 putFile8Bit(file, change->use_target_content);
7743 putFile8Bit(file, change->only_if_complete);
7744 putFile8Bit(file, change->use_random_replace);
7746 putFile8Bit(file, change->random_percentage);
7747 putFile8Bit(file, change->replace_when);
7749 for (y = 0; y < 3; y++)
7750 for (x = 0; x < 3; x++)
7751 putFile16BitBE(file, change->target_content.e[x][y]);
7753 putFile8Bit(file, change->can_change);
7755 putFile8Bit(file, change->trigger_side);
7757 putFile8Bit(file, change->trigger_player);
7758 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7759 log_2(change->trigger_page)));
7761 putFile8Bit(file, change->has_action);
7762 putFile8Bit(file, change->action_type);
7763 putFile8Bit(file, change->action_mode);
7764 putFile16BitBE(file, change->action_arg);
7766 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7768 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7769 if (change->has_event[j])
7770 event_bits |= (1u << (j - 32));
7771 putFile8Bit(file, event_bits);
7776 #if ENABLE_HISTORIC_CHUNKS
7777 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7779 struct ElementInfo *ei = &element_info[element];
7780 struct ElementGroupInfo *group = ei->group;
7783 putFile16BitBE(file, element);
7785 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7786 putFile8Bit(file, ei->description[i]);
7788 putFile8Bit(file, group->num_elements);
7790 putFile8Bit(file, ei->use_gfx_element);
7791 putFile16BitBE(file, ei->gfx_element_initial);
7793 putFile8Bit(file, group->choice_mode);
7795 // some free bytes for future values and padding
7796 WriteUnusedBytesToFile(file, 3);
7798 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7799 putFile16BitBE(file, group->element[i]);
7803 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7804 boolean write_element)
7806 int save_type = entry->save_type;
7807 int data_type = entry->data_type;
7808 int conf_type = entry->conf_type;
7809 int byte_mask = conf_type & CONF_MASK_BYTES;
7810 int element = entry->element;
7811 int default_value = entry->default_value;
7813 boolean modified = FALSE;
7815 if (byte_mask != CONF_MASK_MULTI_BYTES)
7817 void *value_ptr = entry->value;
7818 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7821 // check if any settings have been modified before saving them
7822 if (value != default_value)
7825 // do not save if explicitly told or if unmodified default settings
7826 if ((save_type == SAVE_CONF_NEVER) ||
7827 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7831 num_bytes += putFile16BitBE(file, element);
7833 num_bytes += putFile8Bit(file, conf_type);
7834 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7835 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7836 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7839 else if (data_type == TYPE_STRING)
7841 char *default_string = entry->default_string;
7842 char *string = (char *)(entry->value);
7843 int string_length = strlen(string);
7846 // check if any settings have been modified before saving them
7847 if (!strEqual(string, default_string))
7850 // do not save if explicitly told or if unmodified default settings
7851 if ((save_type == SAVE_CONF_NEVER) ||
7852 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7856 num_bytes += putFile16BitBE(file, element);
7858 num_bytes += putFile8Bit(file, conf_type);
7859 num_bytes += putFile16BitBE(file, string_length);
7861 for (i = 0; i < string_length; i++)
7862 num_bytes += putFile8Bit(file, string[i]);
7864 else if (data_type == TYPE_ELEMENT_LIST)
7866 int *element_array = (int *)(entry->value);
7867 int num_elements = *(int *)(entry->num_entities);
7870 // check if any settings have been modified before saving them
7871 for (i = 0; i < num_elements; i++)
7872 if (element_array[i] != default_value)
7875 // do not save if explicitly told or if unmodified default settings
7876 if ((save_type == SAVE_CONF_NEVER) ||
7877 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7881 num_bytes += putFile16BitBE(file, element);
7883 num_bytes += putFile8Bit(file, conf_type);
7884 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7886 for (i = 0; i < num_elements; i++)
7887 num_bytes += putFile16BitBE(file, element_array[i]);
7889 else if (data_type == TYPE_CONTENT_LIST)
7891 struct Content *content = (struct Content *)(entry->value);
7892 int num_contents = *(int *)(entry->num_entities);
7895 // check if any settings have been modified before saving them
7896 for (i = 0; i < num_contents; i++)
7897 for (y = 0; y < 3; y++)
7898 for (x = 0; x < 3; x++)
7899 if (content[i].e[x][y] != default_value)
7902 // do not save if explicitly told or if unmodified default settings
7903 if ((save_type == SAVE_CONF_NEVER) ||
7904 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7908 num_bytes += putFile16BitBE(file, element);
7910 num_bytes += putFile8Bit(file, conf_type);
7911 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7913 for (i = 0; i < num_contents; i++)
7914 for (y = 0; y < 3; y++)
7915 for (x = 0; x < 3; x++)
7916 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7922 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7927 li = *level; // copy level data into temporary buffer
7929 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7930 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7935 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7940 li = *level; // copy level data into temporary buffer
7942 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7943 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7948 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7950 int envelope_nr = element - EL_ENVELOPE_1;
7954 chunk_size += putFile16BitBE(file, element);
7956 // copy envelope data into temporary buffer
7957 xx_envelope = level->envelope[envelope_nr];
7959 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7960 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7965 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7967 struct ElementInfo *ei = &element_info[element];
7971 chunk_size += putFile16BitBE(file, element);
7973 xx_ei = *ei; // copy element data into temporary buffer
7975 // set default description string for this specific element
7976 strcpy(xx_default_description, getDefaultElementDescription(ei));
7978 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7979 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7981 for (i = 0; i < ei->num_change_pages; i++)
7983 struct ElementChangeInfo *change = &ei->change_page[i];
7985 xx_current_change_page = i;
7987 xx_change = *change; // copy change data into temporary buffer
7990 setEventBitsFromEventFlags(change);
7992 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7993 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8000 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8002 struct ElementInfo *ei = &element_info[element];
8003 struct ElementGroupInfo *group = ei->group;
8007 chunk_size += putFile16BitBE(file, element);
8009 xx_ei = *ei; // copy element data into temporary buffer
8010 xx_group = *group; // copy group data into temporary buffer
8012 // set default description string for this specific element
8013 strcpy(xx_default_description, getDefaultElementDescription(ei));
8015 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8016 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8021 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8023 struct ElementInfo *ei = &element_info[element];
8027 chunk_size += putFile16BitBE(file, element);
8029 xx_ei = *ei; // copy element data into temporary buffer
8031 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8032 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8037 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8038 boolean save_as_template)
8044 if (!(file = fopen(filename, MODE_WRITE)))
8046 Warn("cannot save level file '%s'", filename);
8051 level->file_version = FILE_VERSION_ACTUAL;
8052 level->game_version = GAME_VERSION_ACTUAL;
8054 level->creation_date = getCurrentDate();
8056 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8057 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8059 chunk_size = SaveLevel_VERS(NULL, level);
8060 putFileChunkBE(file, "VERS", chunk_size);
8061 SaveLevel_VERS(file, level);
8063 chunk_size = SaveLevel_DATE(NULL, level);
8064 putFileChunkBE(file, "DATE", chunk_size);
8065 SaveLevel_DATE(file, level);
8067 chunk_size = SaveLevel_NAME(NULL, level);
8068 putFileChunkBE(file, "NAME", chunk_size);
8069 SaveLevel_NAME(file, level);
8071 chunk_size = SaveLevel_AUTH(NULL, level);
8072 putFileChunkBE(file, "AUTH", chunk_size);
8073 SaveLevel_AUTH(file, level);
8075 chunk_size = SaveLevel_INFO(NULL, level);
8076 putFileChunkBE(file, "INFO", chunk_size);
8077 SaveLevel_INFO(file, level);
8079 chunk_size = SaveLevel_BODY(NULL, level);
8080 putFileChunkBE(file, "BODY", chunk_size);
8081 SaveLevel_BODY(file, level);
8083 chunk_size = SaveLevel_ELEM(NULL, level);
8084 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8086 putFileChunkBE(file, "ELEM", chunk_size);
8087 SaveLevel_ELEM(file, level);
8090 for (i = 0; i < NUM_ENVELOPES; i++)
8092 int element = EL_ENVELOPE_1 + i;
8094 chunk_size = SaveLevel_NOTE(NULL, level, element);
8095 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8097 putFileChunkBE(file, "NOTE", chunk_size);
8098 SaveLevel_NOTE(file, level, element);
8102 // if not using template level, check for non-default custom/group elements
8103 if (!level->use_custom_template || save_as_template)
8105 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8107 int element = EL_CUSTOM_START + i;
8109 chunk_size = SaveLevel_CUSX(NULL, level, element);
8110 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8112 putFileChunkBE(file, "CUSX", chunk_size);
8113 SaveLevel_CUSX(file, level, element);
8117 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8119 int element = EL_GROUP_START + i;
8121 chunk_size = SaveLevel_GRPX(NULL, level, element);
8122 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8124 putFileChunkBE(file, "GRPX", chunk_size);
8125 SaveLevel_GRPX(file, level, element);
8129 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8131 int element = GET_EMPTY_ELEMENT(i);
8133 chunk_size = SaveLevel_EMPX(NULL, level, element);
8134 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8136 putFileChunkBE(file, "EMPX", chunk_size);
8137 SaveLevel_EMPX(file, level, element);
8144 SetFilePermissions(filename, PERMS_PRIVATE);
8147 void SaveLevel(int nr)
8149 char *filename = getDefaultLevelFilename(nr);
8151 SaveLevelFromFilename(&level, filename, FALSE);
8154 void SaveLevelTemplate(void)
8156 char *filename = getLocalLevelTemplateFilename();
8158 SaveLevelFromFilename(&level, filename, TRUE);
8161 boolean SaveLevelChecked(int nr)
8163 char *filename = getDefaultLevelFilename(nr);
8164 boolean new_level = !fileExists(filename);
8165 boolean level_saved = FALSE;
8167 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8172 Request("Level saved!", REQ_CONFIRM);
8180 void DumpLevel(struct LevelInfo *level)
8182 if (level->no_level_file || level->no_valid_file)
8184 Warn("cannot dump -- no valid level file found");
8190 Print("Level xxx (file version %08d, game version %08d)\n",
8191 level->file_version, level->game_version);
8194 Print("Level author: '%s'\n", level->author);
8195 Print("Level title: '%s'\n", level->name);
8197 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8199 Print("Level time: %d seconds\n", level->time);
8200 Print("Gems needed: %d\n", level->gems_needed);
8202 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8203 Print("Time for wheel: %d seconds\n", level->time_wheel);
8204 Print("Time for light: %d seconds\n", level->time_light);
8205 Print("Time for timegate: %d seconds\n", level->time_timegate);
8207 Print("Amoeba speed: %d\n", level->amoeba_speed);
8210 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8211 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8212 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8213 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8214 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8215 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8221 for (i = 0; i < NUM_ENVELOPES; i++)
8223 char *text = level->envelope[i].text;
8224 int text_len = strlen(text);
8225 boolean has_text = FALSE;
8227 for (j = 0; j < text_len; j++)
8228 if (text[j] != ' ' && text[j] != '\n')
8234 Print("Envelope %d:\n'%s'\n", i + 1, text);
8242 void DumpLevels(void)
8244 static LevelDirTree *dumplevel_leveldir = NULL;
8246 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8247 global.dumplevel_leveldir);
8249 if (dumplevel_leveldir == NULL)
8250 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8252 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8253 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8254 Fail("no such level number: %d", global.dumplevel_level_nr);
8256 leveldir_current = dumplevel_leveldir;
8258 LoadLevel(global.dumplevel_level_nr);
8265 // ============================================================================
8266 // tape file functions
8267 // ============================================================================
8269 static void setTapeInfoToDefaults(void)
8273 // always start with reliable default values (empty tape)
8276 // default values (also for pre-1.2 tapes) with only the first player
8277 tape.player_participates[0] = TRUE;
8278 for (i = 1; i < MAX_PLAYERS; i++)
8279 tape.player_participates[i] = FALSE;
8281 // at least one (default: the first) player participates in every tape
8282 tape.num_participating_players = 1;
8284 tape.property_bits = TAPE_PROPERTY_NONE;
8286 tape.level_nr = level_nr;
8288 tape.changed = FALSE;
8289 tape.solved = FALSE;
8291 tape.recording = FALSE;
8292 tape.playing = FALSE;
8293 tape.pausing = FALSE;
8295 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8296 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8298 tape.no_info_chunk = TRUE;
8299 tape.no_valid_file = FALSE;
8302 static int getTapePosSize(struct TapeInfo *tape)
8304 int tape_pos_size = 0;
8306 if (tape->use_key_actions)
8307 tape_pos_size += tape->num_participating_players;
8309 if (tape->use_mouse_actions)
8310 tape_pos_size += 3; // x and y position and mouse button mask
8312 tape_pos_size += 1; // tape action delay value
8314 return tape_pos_size;
8317 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8319 tape->use_key_actions = FALSE;
8320 tape->use_mouse_actions = FALSE;
8322 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8323 tape->use_key_actions = TRUE;
8325 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8326 tape->use_mouse_actions = TRUE;
8329 static int getTapeActionValue(struct TapeInfo *tape)
8331 return (tape->use_key_actions &&
8332 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8333 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8334 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8335 TAPE_ACTIONS_DEFAULT);
8338 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8340 tape->file_version = getFileVersion(file);
8341 tape->game_version = getFileVersion(file);
8346 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8350 tape->random_seed = getFile32BitBE(file);
8351 tape->date = getFile32BitBE(file);
8352 tape->length = getFile32BitBE(file);
8354 // read header fields that are new since version 1.2
8355 if (tape->file_version >= FILE_VERSION_1_2)
8357 byte store_participating_players = getFile8Bit(file);
8360 // since version 1.2, tapes store which players participate in the tape
8361 tape->num_participating_players = 0;
8362 for (i = 0; i < MAX_PLAYERS; i++)
8364 tape->player_participates[i] = FALSE;
8366 if (store_participating_players & (1 << i))
8368 tape->player_participates[i] = TRUE;
8369 tape->num_participating_players++;
8373 setTapeActionFlags(tape, getFile8Bit(file));
8375 tape->property_bits = getFile8Bit(file);
8376 tape->solved = getFile8Bit(file);
8378 engine_version = getFileVersion(file);
8379 if (engine_version > 0)
8380 tape->engine_version = engine_version;
8382 tape->engine_version = tape->game_version;
8388 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8390 tape->scr_fieldx = getFile8Bit(file);
8391 tape->scr_fieldy = getFile8Bit(file);
8396 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8398 char *level_identifier = NULL;
8399 int level_identifier_size;
8402 tape->no_info_chunk = FALSE;
8404 level_identifier_size = getFile16BitBE(file);
8406 level_identifier = checked_malloc(level_identifier_size);
8408 for (i = 0; i < level_identifier_size; i++)
8409 level_identifier[i] = getFile8Bit(file);
8411 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8412 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8414 checked_free(level_identifier);
8416 tape->level_nr = getFile16BitBE(file);
8418 chunk_size = 2 + level_identifier_size + 2;
8423 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8426 int tape_pos_size = getTapePosSize(tape);
8427 int chunk_size_expected = tape_pos_size * tape->length;
8429 if (chunk_size_expected != chunk_size)
8431 ReadUnusedBytesFromFile(file, chunk_size);
8432 return chunk_size_expected;
8435 for (i = 0; i < tape->length; i++)
8437 if (i >= MAX_TAPE_LEN)
8439 Warn("tape truncated -- size exceeds maximum tape size %d",
8442 // tape too large; read and ignore remaining tape data from this chunk
8443 for (;i < tape->length; i++)
8444 ReadUnusedBytesFromFile(file, tape_pos_size);
8449 if (tape->use_key_actions)
8451 for (j = 0; j < MAX_PLAYERS; j++)
8453 tape->pos[i].action[j] = MV_NONE;
8455 if (tape->player_participates[j])
8456 tape->pos[i].action[j] = getFile8Bit(file);
8460 if (tape->use_mouse_actions)
8462 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8463 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8464 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8467 tape->pos[i].delay = getFile8Bit(file);
8469 if (tape->file_version == FILE_VERSION_1_0)
8471 // eliminate possible diagonal moves in old tapes
8472 // this is only for backward compatibility
8474 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8475 byte action = tape->pos[i].action[0];
8476 int k, num_moves = 0;
8478 for (k = 0; k < 4; k++)
8480 if (action & joy_dir[k])
8482 tape->pos[i + num_moves].action[0] = joy_dir[k];
8484 tape->pos[i + num_moves].delay = 0;
8493 tape->length += num_moves;
8496 else if (tape->file_version < FILE_VERSION_2_0)
8498 // convert pre-2.0 tapes to new tape format
8500 if (tape->pos[i].delay > 1)
8503 tape->pos[i + 1] = tape->pos[i];
8504 tape->pos[i + 1].delay = 1;
8507 for (j = 0; j < MAX_PLAYERS; j++)
8508 tape->pos[i].action[j] = MV_NONE;
8509 tape->pos[i].delay--;
8516 if (checkEndOfFile(file))
8520 if (i != tape->length)
8521 chunk_size = tape_pos_size * i;
8526 static void LoadTape_SokobanSolution(char *filename)
8529 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8531 if (!(file = openFile(filename, MODE_READ)))
8533 tape.no_valid_file = TRUE;
8538 while (!checkEndOfFile(file))
8540 unsigned char c = getByteFromFile(file);
8542 if (checkEndOfFile(file))
8549 tape.pos[tape.length].action[0] = MV_UP;
8550 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8556 tape.pos[tape.length].action[0] = MV_DOWN;
8557 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8563 tape.pos[tape.length].action[0] = MV_LEFT;
8564 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8570 tape.pos[tape.length].action[0] = MV_RIGHT;
8571 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8579 // ignore white-space characters
8583 tape.no_valid_file = TRUE;
8585 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8593 if (tape.no_valid_file)
8596 tape.length_frames = GetTapeLengthFrames();
8597 tape.length_seconds = GetTapeLengthSeconds();
8600 void LoadTapeFromFilename(char *filename)
8602 char cookie[MAX_LINE_LEN];
8603 char chunk_name[CHUNK_ID_LEN + 1];
8607 // always start with reliable default values
8608 setTapeInfoToDefaults();
8610 if (strSuffix(filename, ".sln"))
8612 LoadTape_SokobanSolution(filename);
8617 if (!(file = openFile(filename, MODE_READ)))
8619 tape.no_valid_file = TRUE;
8624 getFileChunkBE(file, chunk_name, NULL);
8625 if (strEqual(chunk_name, "RND1"))
8627 getFile32BitBE(file); // not used
8629 getFileChunkBE(file, chunk_name, NULL);
8630 if (!strEqual(chunk_name, "TAPE"))
8632 tape.no_valid_file = TRUE;
8634 Warn("unknown format of tape file '%s'", filename);
8641 else // check for pre-2.0 file format with cookie string
8643 strcpy(cookie, chunk_name);
8644 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8646 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8647 cookie[strlen(cookie) - 1] = '\0';
8649 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8651 tape.no_valid_file = TRUE;
8653 Warn("unknown format of tape file '%s'", filename);
8660 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8662 tape.no_valid_file = TRUE;
8664 Warn("unsupported version of tape file '%s'", filename);
8671 // pre-2.0 tape files have no game version, so use file version here
8672 tape.game_version = tape.file_version;
8675 if (tape.file_version < FILE_VERSION_1_2)
8677 // tape files from versions before 1.2.0 without chunk structure
8678 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8679 LoadTape_BODY(file, 2 * tape.length, &tape);
8687 int (*loader)(File *, int, struct TapeInfo *);
8691 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8692 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8693 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8694 { "INFO", -1, LoadTape_INFO },
8695 { "BODY", -1, LoadTape_BODY },
8699 while (getFileChunkBE(file, chunk_name, &chunk_size))
8703 while (chunk_info[i].name != NULL &&
8704 !strEqual(chunk_name, chunk_info[i].name))
8707 if (chunk_info[i].name == NULL)
8709 Warn("unknown chunk '%s' in tape file '%s'",
8710 chunk_name, filename);
8712 ReadUnusedBytesFromFile(file, chunk_size);
8714 else if (chunk_info[i].size != -1 &&
8715 chunk_info[i].size != chunk_size)
8717 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8718 chunk_size, chunk_name, filename);
8720 ReadUnusedBytesFromFile(file, chunk_size);
8724 // call function to load this tape chunk
8725 int chunk_size_expected =
8726 (chunk_info[i].loader)(file, chunk_size, &tape);
8728 // the size of some chunks cannot be checked before reading other
8729 // chunks first (like "HEAD" and "BODY") that contain some header
8730 // information, so check them here
8731 if (chunk_size_expected != chunk_size)
8733 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8734 chunk_size, chunk_name, filename);
8742 tape.length_frames = GetTapeLengthFrames();
8743 tape.length_seconds = GetTapeLengthSeconds();
8746 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8748 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8750 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8751 tape.engine_version);
8755 void LoadTape(int nr)
8757 char *filename = getTapeFilename(nr);
8759 LoadTapeFromFilename(filename);
8762 void LoadSolutionTape(int nr)
8764 char *filename = getSolutionTapeFilename(nr);
8766 LoadTapeFromFilename(filename);
8768 if (TAPE_IS_EMPTY(tape))
8770 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8771 level.native_bd_level->replay != NULL)
8772 CopyNativeTape_BD_to_RND(&level);
8773 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8774 level.native_sp_level->demo.is_available)
8775 CopyNativeTape_SP_to_RND(&level);
8779 void LoadScoreTape(char *score_tape_basename, int nr)
8781 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8783 LoadTapeFromFilename(filename);
8786 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8788 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8790 LoadTapeFromFilename(filename);
8793 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8795 // chunk required for team mode tapes with non-default screen size
8796 return (tape->num_participating_players > 1 &&
8797 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8798 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8801 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8803 putFileVersion(file, tape->file_version);
8804 putFileVersion(file, tape->game_version);
8807 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8810 byte store_participating_players = 0;
8812 // set bits for participating players for compact storage
8813 for (i = 0; i < MAX_PLAYERS; i++)
8814 if (tape->player_participates[i])
8815 store_participating_players |= (1 << i);
8817 putFile32BitBE(file, tape->random_seed);
8818 putFile32BitBE(file, tape->date);
8819 putFile32BitBE(file, tape->length);
8821 putFile8Bit(file, store_participating_players);
8823 putFile8Bit(file, getTapeActionValue(tape));
8825 putFile8Bit(file, tape->property_bits);
8826 putFile8Bit(file, tape->solved);
8828 putFileVersion(file, tape->engine_version);
8831 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8833 putFile8Bit(file, tape->scr_fieldx);
8834 putFile8Bit(file, tape->scr_fieldy);
8837 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8839 int level_identifier_size = strlen(tape->level_identifier) + 1;
8842 putFile16BitBE(file, level_identifier_size);
8844 for (i = 0; i < level_identifier_size; i++)
8845 putFile8Bit(file, tape->level_identifier[i]);
8847 putFile16BitBE(file, tape->level_nr);
8850 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8854 for (i = 0; i < tape->length; i++)
8856 if (tape->use_key_actions)
8858 for (j = 0; j < MAX_PLAYERS; j++)
8859 if (tape->player_participates[j])
8860 putFile8Bit(file, tape->pos[i].action[j]);
8863 if (tape->use_mouse_actions)
8865 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8866 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8867 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8870 putFile8Bit(file, tape->pos[i].delay);
8874 void SaveTapeToFilename(char *filename)
8878 int info_chunk_size;
8879 int body_chunk_size;
8881 if (!(file = fopen(filename, MODE_WRITE)))
8883 Warn("cannot save level recording file '%s'", filename);
8888 tape_pos_size = getTapePosSize(&tape);
8890 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8891 body_chunk_size = tape_pos_size * tape.length;
8893 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8894 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8896 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8897 SaveTape_VERS(file, &tape);
8899 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8900 SaveTape_HEAD(file, &tape);
8902 if (checkSaveTape_SCRN(&tape))
8904 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8905 SaveTape_SCRN(file, &tape);
8908 putFileChunkBE(file, "INFO", info_chunk_size);
8909 SaveTape_INFO(file, &tape);
8911 putFileChunkBE(file, "BODY", body_chunk_size);
8912 SaveTape_BODY(file, &tape);
8916 SetFilePermissions(filename, PERMS_PRIVATE);
8919 static void SaveTapeExt(char *filename)
8923 tape.file_version = FILE_VERSION_ACTUAL;
8924 tape.game_version = GAME_VERSION_ACTUAL;
8926 tape.num_participating_players = 0;
8928 // count number of participating players
8929 for (i = 0; i < MAX_PLAYERS; i++)
8930 if (tape.player_participates[i])
8931 tape.num_participating_players++;
8933 SaveTapeToFilename(filename);
8935 tape.changed = FALSE;
8938 void SaveTape(int nr)
8940 char *filename = getTapeFilename(nr);
8942 InitTapeDirectory(leveldir_current->subdir);
8944 SaveTapeExt(filename);
8947 void SaveScoreTape(int nr)
8949 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8951 // used instead of "leveldir_current->subdir" (for network games)
8952 InitScoreTapeDirectory(levelset.identifier, nr);
8954 SaveTapeExt(filename);
8957 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8958 unsigned int req_state_added)
8960 char *filename = getTapeFilename(nr);
8961 boolean new_tape = !fileExists(filename);
8962 boolean tape_saved = FALSE;
8964 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8969 Request(msg_saved, REQ_CONFIRM | req_state_added);
8977 boolean SaveTapeChecked(int nr)
8979 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8982 boolean SaveTapeChecked_LevelSolved(int nr)
8984 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8985 "Level solved! Tape saved!", REQ_STAY_OPEN);
8988 void DumpTape(struct TapeInfo *tape)
8990 int tape_frame_counter;
8993 if (tape->no_valid_file)
8995 Warn("cannot dump -- no valid tape file found");
9002 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9003 tape->level_nr, tape->file_version, tape->game_version);
9004 Print(" (effective engine version %08d)\n",
9005 tape->engine_version);
9006 Print("Level series identifier: '%s'\n", tape->level_identifier);
9008 Print("Solution tape: %s\n",
9009 tape->solved ? "yes" :
9010 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9012 Print("Special tape properties: ");
9013 if (tape->property_bits == TAPE_PROPERTY_NONE)
9015 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9016 Print("[em_random_bug]");
9017 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9018 Print("[game_speed]");
9019 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9021 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9022 Print("[single_step]");
9023 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9024 Print("[snapshot]");
9025 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9026 Print("[replayed]");
9027 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9028 Print("[tas_keys]");
9029 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9030 Print("[small_graphics]");
9033 int year2 = tape->date / 10000;
9034 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9035 int month_index_raw = (tape->date / 100) % 100;
9036 int month_index = month_index_raw % 12; // prevent invalid index
9037 int month = month_index + 1;
9038 int day = tape->date % 100;
9040 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9044 tape_frame_counter = 0;
9046 for (i = 0; i < tape->length; i++)
9048 if (i >= MAX_TAPE_LEN)
9053 for (j = 0; j < MAX_PLAYERS; j++)
9055 if (tape->player_participates[j])
9057 int action = tape->pos[i].action[j];
9059 Print("%d:%02x ", j, action);
9060 Print("[%c%c%c%c|%c%c] - ",
9061 (action & JOY_LEFT ? '<' : ' '),
9062 (action & JOY_RIGHT ? '>' : ' '),
9063 (action & JOY_UP ? '^' : ' '),
9064 (action & JOY_DOWN ? 'v' : ' '),
9065 (action & JOY_BUTTON_1 ? '1' : ' '),
9066 (action & JOY_BUTTON_2 ? '2' : ' '));
9070 Print("(%03d) ", tape->pos[i].delay);
9071 Print("[%05d]\n", tape_frame_counter);
9073 tape_frame_counter += tape->pos[i].delay;
9079 void DumpTapes(void)
9081 static LevelDirTree *dumptape_leveldir = NULL;
9083 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9084 global.dumptape_leveldir);
9086 if (dumptape_leveldir == NULL)
9087 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9089 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9090 global.dumptape_level_nr > dumptape_leveldir->last_level)
9091 Fail("no such level number: %d", global.dumptape_level_nr);
9093 leveldir_current = dumptape_leveldir;
9095 if (options.mytapes)
9096 LoadTape(global.dumptape_level_nr);
9098 LoadSolutionTape(global.dumptape_level_nr);
9106 // ============================================================================
9107 // score file functions
9108 // ============================================================================
9110 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9114 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9116 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9117 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9118 scores->entry[i].score = 0;
9119 scores->entry[i].time = 0;
9121 scores->entry[i].id = -1;
9122 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9123 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9124 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9125 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9126 strcpy(scores->entry[i].country_code, "??");
9129 scores->num_entries = 0;
9130 scores->last_added = -1;
9131 scores->last_added_local = -1;
9133 scores->updated = FALSE;
9134 scores->uploaded = FALSE;
9135 scores->tape_downloaded = FALSE;
9136 scores->force_last_added = FALSE;
9138 // The following values are intentionally not reset here:
9142 // - continue_playing
9143 // - continue_on_return
9146 static void setScoreInfoToDefaults(void)
9148 setScoreInfoToDefaultsExt(&scores);
9151 static void setServerScoreInfoToDefaults(void)
9153 setScoreInfoToDefaultsExt(&server_scores);
9156 static void LoadScore_OLD(int nr)
9159 char *filename = getScoreFilename(nr);
9160 char cookie[MAX_LINE_LEN];
9161 char line[MAX_LINE_LEN];
9165 if (!(file = fopen(filename, MODE_READ)))
9168 // check file identifier
9169 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9171 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9172 cookie[strlen(cookie) - 1] = '\0';
9174 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9176 Warn("unknown format of score file '%s'", filename);
9183 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9185 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9186 Warn("fscanf() failed; %s", strerror(errno));
9188 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9191 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9192 line[strlen(line) - 1] = '\0';
9194 for (line_ptr = line; *line_ptr; line_ptr++)
9196 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9198 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9199 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9208 static void ConvertScore_OLD(void)
9210 // only convert score to time for levels that rate playing time over score
9211 if (!level.rate_time_over_score)
9214 // convert old score to playing time for score-less levels (like Supaplex)
9215 int time_final_max = 999;
9218 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9220 int score = scores.entry[i].score;
9222 if (score > 0 && score < time_final_max)
9223 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9227 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9229 scores->file_version = getFileVersion(file);
9230 scores->game_version = getFileVersion(file);
9235 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9237 char *level_identifier = NULL;
9238 int level_identifier_size;
9241 level_identifier_size = getFile16BitBE(file);
9243 level_identifier = checked_malloc(level_identifier_size);
9245 for (i = 0; i < level_identifier_size; i++)
9246 level_identifier[i] = getFile8Bit(file);
9248 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9249 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9251 checked_free(level_identifier);
9253 scores->level_nr = getFile16BitBE(file);
9254 scores->num_entries = getFile16BitBE(file);
9256 chunk_size = 2 + level_identifier_size + 2 + 2;
9261 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9265 for (i = 0; i < scores->num_entries; i++)
9267 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9268 scores->entry[i].name[j] = getFile8Bit(file);
9270 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9273 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9278 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9282 for (i = 0; i < scores->num_entries; i++)
9283 scores->entry[i].score = getFile16BitBE(file);
9285 chunk_size = scores->num_entries * 2;
9290 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9294 for (i = 0; i < scores->num_entries; i++)
9295 scores->entry[i].score = getFile32BitBE(file);
9297 chunk_size = scores->num_entries * 4;
9302 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9306 for (i = 0; i < scores->num_entries; i++)
9307 scores->entry[i].time = getFile32BitBE(file);
9309 chunk_size = scores->num_entries * 4;
9314 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9318 for (i = 0; i < scores->num_entries; i++)
9320 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9321 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9323 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9326 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9331 void LoadScore(int nr)
9333 char *filename = getScoreFilename(nr);
9334 char cookie[MAX_LINE_LEN];
9335 char chunk_name[CHUNK_ID_LEN + 1];
9337 boolean old_score_file_format = FALSE;
9340 // always start with reliable default values
9341 setScoreInfoToDefaults();
9343 if (!(file = openFile(filename, MODE_READ)))
9346 getFileChunkBE(file, chunk_name, NULL);
9347 if (strEqual(chunk_name, "RND1"))
9349 getFile32BitBE(file); // not used
9351 getFileChunkBE(file, chunk_name, NULL);
9352 if (!strEqual(chunk_name, "SCOR"))
9354 Warn("unknown format of score file '%s'", filename);
9361 else // check for old file format with cookie string
9363 strcpy(cookie, chunk_name);
9364 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9366 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9367 cookie[strlen(cookie) - 1] = '\0';
9369 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9371 Warn("unknown format of score file '%s'", filename);
9378 old_score_file_format = TRUE;
9381 if (old_score_file_format)
9383 // score files from versions before 4.2.4.0 without chunk structure
9386 // convert score to time, if possible (mainly for Supaplex levels)
9395 int (*loader)(File *, int, struct ScoreInfo *);
9399 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9400 { "INFO", -1, LoadScore_INFO },
9401 { "NAME", -1, LoadScore_NAME },
9402 { "SCOR", -1, LoadScore_SCOR },
9403 { "SC4R", -1, LoadScore_SC4R },
9404 { "TIME", -1, LoadScore_TIME },
9405 { "TAPE", -1, LoadScore_TAPE },
9410 while (getFileChunkBE(file, chunk_name, &chunk_size))
9414 while (chunk_info[i].name != NULL &&
9415 !strEqual(chunk_name, chunk_info[i].name))
9418 if (chunk_info[i].name == NULL)
9420 Warn("unknown chunk '%s' in score file '%s'",
9421 chunk_name, filename);
9423 ReadUnusedBytesFromFile(file, chunk_size);
9425 else if (chunk_info[i].size != -1 &&
9426 chunk_info[i].size != chunk_size)
9428 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9429 chunk_size, chunk_name, filename);
9431 ReadUnusedBytesFromFile(file, chunk_size);
9435 // call function to load this score chunk
9436 int chunk_size_expected =
9437 (chunk_info[i].loader)(file, chunk_size, &scores);
9439 // the size of some chunks cannot be checked before reading other
9440 // chunks first (like "HEAD" and "BODY") that contain some header
9441 // information, so check them here
9442 if (chunk_size_expected != chunk_size)
9444 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9445 chunk_size, chunk_name, filename);
9454 #if ENABLE_HISTORIC_CHUNKS
9455 void SaveScore_OLD(int nr)
9458 char *filename = getScoreFilename(nr);
9461 // used instead of "leveldir_current->subdir" (for network games)
9462 InitScoreDirectory(levelset.identifier);
9464 if (!(file = fopen(filename, MODE_WRITE)))
9466 Warn("cannot save score for level %d", nr);
9471 fprintf(file, "%s\n\n", SCORE_COOKIE);
9473 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9474 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9478 SetFilePermissions(filename, PERMS_PRIVATE);
9482 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9484 putFileVersion(file, scores->file_version);
9485 putFileVersion(file, scores->game_version);
9488 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9490 int level_identifier_size = strlen(scores->level_identifier) + 1;
9493 putFile16BitBE(file, level_identifier_size);
9495 for (i = 0; i < level_identifier_size; i++)
9496 putFile8Bit(file, scores->level_identifier[i]);
9498 putFile16BitBE(file, scores->level_nr);
9499 putFile16BitBE(file, scores->num_entries);
9502 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9506 for (i = 0; i < scores->num_entries; i++)
9508 int name_size = strlen(scores->entry[i].name);
9510 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9511 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9515 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9519 for (i = 0; i < scores->num_entries; i++)
9520 putFile16BitBE(file, scores->entry[i].score);
9523 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9527 for (i = 0; i < scores->num_entries; i++)
9528 putFile32BitBE(file, scores->entry[i].score);
9531 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9535 for (i = 0; i < scores->num_entries; i++)
9536 putFile32BitBE(file, scores->entry[i].time);
9539 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9543 for (i = 0; i < scores->num_entries; i++)
9545 int size = strlen(scores->entry[i].tape_basename);
9547 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9548 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9552 static void SaveScoreToFilename(char *filename)
9555 int info_chunk_size;
9556 int name_chunk_size;
9557 int scor_chunk_size;
9558 int sc4r_chunk_size;
9559 int time_chunk_size;
9560 int tape_chunk_size;
9561 boolean has_large_score_values;
9564 if (!(file = fopen(filename, MODE_WRITE)))
9566 Warn("cannot save score file '%s'", filename);
9571 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9572 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9573 scor_chunk_size = scores.num_entries * 2;
9574 sc4r_chunk_size = scores.num_entries * 4;
9575 time_chunk_size = scores.num_entries * 4;
9576 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9578 has_large_score_values = FALSE;
9579 for (i = 0; i < scores.num_entries; i++)
9580 if (scores.entry[i].score > 0xffff)
9581 has_large_score_values = TRUE;
9583 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9584 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9586 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9587 SaveScore_VERS(file, &scores);
9589 putFileChunkBE(file, "INFO", info_chunk_size);
9590 SaveScore_INFO(file, &scores);
9592 putFileChunkBE(file, "NAME", name_chunk_size);
9593 SaveScore_NAME(file, &scores);
9595 if (has_large_score_values)
9597 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9598 SaveScore_SC4R(file, &scores);
9602 putFileChunkBE(file, "SCOR", scor_chunk_size);
9603 SaveScore_SCOR(file, &scores);
9606 putFileChunkBE(file, "TIME", time_chunk_size);
9607 SaveScore_TIME(file, &scores);
9609 putFileChunkBE(file, "TAPE", tape_chunk_size);
9610 SaveScore_TAPE(file, &scores);
9614 SetFilePermissions(filename, PERMS_PRIVATE);
9617 void SaveScore(int nr)
9619 char *filename = getScoreFilename(nr);
9622 // used instead of "leveldir_current->subdir" (for network games)
9623 InitScoreDirectory(levelset.identifier);
9625 scores.file_version = FILE_VERSION_ACTUAL;
9626 scores.game_version = GAME_VERSION_ACTUAL;
9628 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9629 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9630 scores.level_nr = level_nr;
9632 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9633 if (scores.entry[i].score == 0 &&
9634 scores.entry[i].time == 0 &&
9635 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9638 scores.num_entries = i;
9640 if (scores.num_entries == 0)
9643 SaveScoreToFilename(filename);
9646 static void LoadServerScoreFromCache(int nr)
9648 struct ScoreEntry score_entry;
9657 { &score_entry.score, FALSE, 0 },
9658 { &score_entry.time, FALSE, 0 },
9659 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9660 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9661 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9662 { &score_entry.id, FALSE, 0 },
9663 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9664 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9665 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9666 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9670 char *filename = getScoreCacheFilename(nr);
9671 SetupFileHash *score_hash = loadSetupFileHash(filename);
9674 server_scores.num_entries = 0;
9676 if (score_hash == NULL)
9679 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9681 score_entry = server_scores.entry[i];
9683 for (j = 0; score_mapping[j].value != NULL; j++)
9687 sprintf(token, "%02d.%d", i, j);
9689 char *value = getHashEntry(score_hash, token);
9694 if (score_mapping[j].is_string)
9696 char *score_value = (char *)score_mapping[j].value;
9697 int value_size = score_mapping[j].string_size;
9699 strncpy(score_value, value, value_size);
9700 score_value[value_size] = '\0';
9704 int *score_value = (int *)score_mapping[j].value;
9706 *score_value = atoi(value);
9709 server_scores.num_entries = i + 1;
9712 server_scores.entry[i] = score_entry;
9715 freeSetupFileHash(score_hash);
9718 void LoadServerScore(int nr, boolean download_score)
9720 if (!setup.use_api_server)
9723 // always start with reliable default values
9724 setServerScoreInfoToDefaults();
9726 // 1st step: load server scores from cache file (which may not exist)
9727 // (this should prevent reading it while the thread is writing to it)
9728 LoadServerScoreFromCache(nr);
9730 if (download_score && runtime.use_api_server)
9732 // 2nd step: download server scores from score server to cache file
9733 // (as thread, as it might time out if the server is not reachable)
9734 ApiGetScoreAsThread(nr);
9738 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9740 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9742 // if score tape not uploaded, ask for uploading missing tapes later
9743 if (!setup.has_remaining_tapes)
9744 setup.ask_for_remaining_tapes = TRUE;
9746 setup.provide_uploading_tapes = TRUE;
9747 setup.has_remaining_tapes = TRUE;
9749 SaveSetup_ServerSetup();
9752 void SaveServerScore(int nr, boolean tape_saved)
9754 if (!runtime.use_api_server)
9756 PrepareScoreTapesForUpload(leveldir_current->subdir);
9761 ApiAddScoreAsThread(nr, tape_saved, NULL);
9764 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9765 char *score_tape_filename)
9767 if (!runtime.use_api_server)
9770 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9773 void LoadLocalAndServerScore(int nr, boolean download_score)
9775 int last_added_local = scores.last_added_local;
9776 boolean force_last_added = scores.force_last_added;
9778 // needed if only showing server scores
9779 setScoreInfoToDefaults();
9781 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9784 // restore last added local score entry (before merging server scores)
9785 scores.last_added = scores.last_added_local = last_added_local;
9787 if (setup.use_api_server &&
9788 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9790 // load server scores from cache file and trigger update from server
9791 LoadServerScore(nr, download_score);
9793 // merge local scores with scores from server
9797 if (force_last_added)
9798 scores.force_last_added = force_last_added;
9802 // ============================================================================
9803 // setup file functions
9804 // ============================================================================
9806 #define TOKEN_STR_PLAYER_PREFIX "player_"
9809 static struct TokenInfo global_setup_tokens[] =
9813 &setup.player_name, "player_name"
9817 &setup.multiple_users, "multiple_users"
9821 &setup.sound, "sound"
9825 &setup.sound_loops, "repeating_sound_loops"
9829 &setup.sound_music, "background_music"
9833 &setup.sound_simple, "simple_sound_effects"
9837 &setup.toons, "toons"
9841 &setup.global_animations, "global_animations"
9845 &setup.scroll_delay, "scroll_delay"
9849 &setup.forced_scroll_delay, "forced_scroll_delay"
9853 &setup.scroll_delay_value, "scroll_delay_value"
9857 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9861 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9865 &setup.fade_screens, "fade_screens"
9869 &setup.autorecord, "automatic_tape_recording"
9873 &setup.autorecord_after_replay, "autorecord_after_replay"
9877 &setup.auto_pause_on_start, "auto_pause_on_start"
9881 &setup.show_titlescreen, "show_titlescreen"
9885 &setup.quick_doors, "quick_doors"
9889 &setup.team_mode, "team_mode"
9893 &setup.handicap, "handicap"
9897 &setup.skip_levels, "skip_levels"
9901 &setup.increment_levels, "increment_levels"
9905 &setup.auto_play_next_level, "auto_play_next_level"
9909 &setup.count_score_after_game, "count_score_after_game"
9913 &setup.show_scores_after_game, "show_scores_after_game"
9917 &setup.time_limit, "time_limit"
9921 &setup.fullscreen, "fullscreen"
9925 &setup.window_scaling_percent, "window_scaling_percent"
9929 &setup.window_scaling_quality, "window_scaling_quality"
9933 &setup.screen_rendering_mode, "screen_rendering_mode"
9937 &setup.vsync_mode, "vsync_mode"
9941 &setup.ask_on_escape, "ask_on_escape"
9945 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9949 &setup.ask_on_game_over, "ask_on_game_over"
9953 &setup.ask_on_quit_game, "ask_on_quit_game"
9957 &setup.ask_on_quit_program, "ask_on_quit_program"
9961 &setup.quick_switch, "quick_player_switch"
9965 &setup.input_on_focus, "input_on_focus"
9969 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9973 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9977 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9981 &setup.game_speed_extended, "game_speed_extended"
9985 &setup.game_frame_delay, "game_frame_delay"
9989 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9993 &setup.bd_skip_hatching, "bd_skip_hatching"
9997 &setup.bd_scroll_delay, "bd_scroll_delay"
10001 &setup.bd_smooth_movements, "bd_smooth_movements"
10005 &setup.sp_show_border_elements, "sp_show_border_elements"
10009 &setup.small_game_graphics, "small_game_graphics"
10013 &setup.show_load_save_buttons, "show_load_save_buttons"
10017 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10021 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10025 &setup.graphics_set, "graphics_set"
10029 &setup.sounds_set, "sounds_set"
10033 &setup.music_set, "music_set"
10037 &setup.override_level_graphics, "override_level_graphics"
10041 &setup.override_level_sounds, "override_level_sounds"
10045 &setup.override_level_music, "override_level_music"
10049 &setup.volume_simple, "volume_simple"
10053 &setup.volume_loops, "volume_loops"
10057 &setup.volume_music, "volume_music"
10061 &setup.network_mode, "network_mode"
10065 &setup.network_player_nr, "network_player"
10069 &setup.network_server_hostname, "network_server_hostname"
10073 &setup.touch.control_type, "touch.control_type"
10077 &setup.touch.move_distance, "touch.move_distance"
10081 &setup.touch.drop_distance, "touch.drop_distance"
10085 &setup.touch.transparency, "touch.transparency"
10089 &setup.touch.draw_outlined, "touch.draw_outlined"
10093 &setup.touch.draw_pressed, "touch.draw_pressed"
10097 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10101 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10105 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10109 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10113 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10117 static struct TokenInfo auto_setup_tokens[] =
10121 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10125 static struct TokenInfo server_setup_tokens[] =
10129 &setup.player_uuid, "player_uuid"
10133 &setup.player_version, "player_version"
10137 &setup.use_api_server, TEST_PREFIX "use_api_server"
10141 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10145 &setup.api_server_password, TEST_PREFIX "api_server_password"
10149 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10153 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10157 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10161 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10165 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10169 static struct TokenInfo editor_setup_tokens[] =
10173 &setup.editor.el_classic, "editor.el_classic"
10177 &setup.editor.el_custom, "editor.el_custom"
10181 &setup.editor.el_user_defined, "editor.el_user_defined"
10185 &setup.editor.el_dynamic, "editor.el_dynamic"
10189 &setup.editor.el_headlines, "editor.el_headlines"
10193 &setup.editor.show_element_token, "editor.show_element_token"
10197 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10201 static struct TokenInfo editor_cascade_setup_tokens[] =
10205 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10209 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10213 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10217 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10221 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10225 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10229 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10233 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10237 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10241 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10245 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10249 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10253 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10257 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10261 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10265 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10269 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10273 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10277 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10281 static struct TokenInfo shortcut_setup_tokens[] =
10285 &setup.shortcut.save_game, "shortcut.save_game"
10289 &setup.shortcut.load_game, "shortcut.load_game"
10293 &setup.shortcut.restart_game, "shortcut.restart_game"
10297 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10301 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10305 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10309 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10313 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10317 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10321 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10325 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10329 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10333 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10337 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10341 &setup.shortcut.tape_record, "shortcut.tape_record"
10345 &setup.shortcut.tape_play, "shortcut.tape_play"
10349 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10353 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10357 &setup.shortcut.sound_music, "shortcut.sound_music"
10361 &setup.shortcut.snap_left, "shortcut.snap_left"
10365 &setup.shortcut.snap_right, "shortcut.snap_right"
10369 &setup.shortcut.snap_up, "shortcut.snap_up"
10373 &setup.shortcut.snap_down, "shortcut.snap_down"
10377 static struct SetupInputInfo setup_input;
10378 static struct TokenInfo player_setup_tokens[] =
10382 &setup_input.use_joystick, ".use_joystick"
10386 &setup_input.joy.device_name, ".joy.device_name"
10390 &setup_input.joy.xleft, ".joy.xleft"
10394 &setup_input.joy.xmiddle, ".joy.xmiddle"
10398 &setup_input.joy.xright, ".joy.xright"
10402 &setup_input.joy.yupper, ".joy.yupper"
10406 &setup_input.joy.ymiddle, ".joy.ymiddle"
10410 &setup_input.joy.ylower, ".joy.ylower"
10414 &setup_input.joy.snap, ".joy.snap_field"
10418 &setup_input.joy.drop, ".joy.place_bomb"
10422 &setup_input.key.left, ".key.move_left"
10426 &setup_input.key.right, ".key.move_right"
10430 &setup_input.key.up, ".key.move_up"
10434 &setup_input.key.down, ".key.move_down"
10438 &setup_input.key.snap, ".key.snap_field"
10442 &setup_input.key.drop, ".key.place_bomb"
10446 static struct TokenInfo system_setup_tokens[] =
10450 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10454 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10458 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10462 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10466 static struct TokenInfo internal_setup_tokens[] =
10470 &setup.internal.program_title, "program_title"
10474 &setup.internal.program_version, "program_version"
10478 &setup.internal.program_author, "program_author"
10482 &setup.internal.program_email, "program_email"
10486 &setup.internal.program_website, "program_website"
10490 &setup.internal.program_copyright, "program_copyright"
10494 &setup.internal.program_company, "program_company"
10498 &setup.internal.program_icon_file, "program_icon_file"
10502 &setup.internal.default_graphics_set, "default_graphics_set"
10506 &setup.internal.default_sounds_set, "default_sounds_set"
10510 &setup.internal.default_music_set, "default_music_set"
10514 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10518 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10522 &setup.internal.fallback_music_file, "fallback_music_file"
10526 &setup.internal.default_level_series, "default_level_series"
10530 &setup.internal.default_window_width, "default_window_width"
10534 &setup.internal.default_window_height, "default_window_height"
10538 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10542 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10546 &setup.internal.create_user_levelset, "create_user_levelset"
10550 &setup.internal.info_screens_from_main, "info_screens_from_main"
10554 &setup.internal.menu_game, "menu_game"
10558 &setup.internal.menu_engines, "menu_engines"
10562 &setup.internal.menu_editor, "menu_editor"
10566 &setup.internal.menu_graphics, "menu_graphics"
10570 &setup.internal.menu_sound, "menu_sound"
10574 &setup.internal.menu_artwork, "menu_artwork"
10578 &setup.internal.menu_input, "menu_input"
10582 &setup.internal.menu_touch, "menu_touch"
10586 &setup.internal.menu_shortcuts, "menu_shortcuts"
10590 &setup.internal.menu_exit, "menu_exit"
10594 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10598 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10602 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10606 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10610 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10614 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10618 &setup.internal.info_title, "info_title"
10622 &setup.internal.info_elements, "info_elements"
10626 &setup.internal.info_music, "info_music"
10630 &setup.internal.info_credits, "info_credits"
10634 &setup.internal.info_program, "info_program"
10638 &setup.internal.info_version, "info_version"
10642 &setup.internal.info_levelset, "info_levelset"
10646 &setup.internal.info_exit, "info_exit"
10650 static struct TokenInfo debug_setup_tokens[] =
10654 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10658 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10662 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10666 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10670 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10674 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10678 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10682 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10686 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10690 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10694 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10698 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10702 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10706 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10710 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10714 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10718 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10722 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10726 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10730 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10734 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10737 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10741 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10745 &setup.debug.xsn_mode, "debug.xsn_mode"
10749 &setup.debug.xsn_percent, "debug.xsn_percent"
10753 static struct TokenInfo options_setup_tokens[] =
10757 &setup.options.verbose, "options.verbose"
10761 &setup.options.debug, "options.debug"
10765 &setup.options.debug_mode, "options.debug_mode"
10769 static void setSetupInfoToDefaults(struct SetupInfo *si)
10773 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10775 si->multiple_users = TRUE;
10778 si->sound_loops = TRUE;
10779 si->sound_music = TRUE;
10780 si->sound_simple = TRUE;
10782 si->global_animations = TRUE;
10783 si->scroll_delay = TRUE;
10784 si->forced_scroll_delay = FALSE;
10785 si->scroll_delay_value = STD_SCROLL_DELAY;
10786 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10787 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10788 si->fade_screens = TRUE;
10789 si->autorecord = TRUE;
10790 si->autorecord_after_replay = TRUE;
10791 si->auto_pause_on_start = FALSE;
10792 si->show_titlescreen = TRUE;
10793 si->quick_doors = FALSE;
10794 si->team_mode = FALSE;
10795 si->handicap = TRUE;
10796 si->skip_levels = TRUE;
10797 si->increment_levels = TRUE;
10798 si->auto_play_next_level = TRUE;
10799 si->count_score_after_game = TRUE;
10800 si->show_scores_after_game = TRUE;
10801 si->time_limit = TRUE;
10802 si->fullscreen = FALSE;
10803 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10804 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10805 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10806 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10807 si->ask_on_escape = TRUE;
10808 si->ask_on_escape_editor = TRUE;
10809 si->ask_on_game_over = TRUE;
10810 si->ask_on_quit_game = TRUE;
10811 si->ask_on_quit_program = TRUE;
10812 si->quick_switch = FALSE;
10813 si->input_on_focus = FALSE;
10814 si->prefer_aga_graphics = TRUE;
10815 si->prefer_lowpass_sounds = FALSE;
10816 si->prefer_extra_panel_items = TRUE;
10817 si->game_speed_extended = FALSE;
10818 si->game_frame_delay = GAME_FRAME_DELAY;
10819 si->bd_skip_uncovering = FALSE;
10820 si->bd_skip_hatching = FALSE;
10821 si->bd_scroll_delay = TRUE;
10822 si->bd_smooth_movements = AUTO;
10823 si->sp_show_border_elements = FALSE;
10824 si->small_game_graphics = FALSE;
10825 si->show_load_save_buttons = FALSE;
10826 si->show_undo_redo_buttons = FALSE;
10827 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10829 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10830 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10831 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10833 si->override_level_graphics = FALSE;
10834 si->override_level_sounds = FALSE;
10835 si->override_level_music = FALSE;
10837 si->volume_simple = 100; // percent
10838 si->volume_loops = 100; // percent
10839 si->volume_music = 100; // percent
10841 si->network_mode = FALSE;
10842 si->network_player_nr = 0; // first player
10843 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10845 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10846 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10847 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10848 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10849 si->touch.draw_outlined = TRUE;
10850 si->touch.draw_pressed = TRUE;
10852 for (i = 0; i < 2; i++)
10854 char *default_grid_button[6][2] =
10860 { "111222", " vv " },
10861 { "111222", " vv " }
10863 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10864 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10865 int min_xsize = MIN(6, grid_xsize);
10866 int min_ysize = MIN(6, grid_ysize);
10867 int startx = grid_xsize - min_xsize;
10868 int starty = grid_ysize - min_ysize;
10871 // virtual buttons grid can only be set to defaults if video is initialized
10872 // (this will be repeated if virtual buttons are not loaded from setup file)
10873 if (video.initialized)
10875 si->touch.grid_xsize[i] = grid_xsize;
10876 si->touch.grid_ysize[i] = grid_ysize;
10880 si->touch.grid_xsize[i] = -1;
10881 si->touch.grid_ysize[i] = -1;
10884 for (x = 0; x < MAX_GRID_XSIZE; x++)
10885 for (y = 0; y < MAX_GRID_YSIZE; y++)
10886 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10888 for (x = 0; x < min_xsize; x++)
10889 for (y = 0; y < min_ysize; y++)
10890 si->touch.grid_button[i][x][starty + y] =
10891 default_grid_button[y][0][x];
10893 for (x = 0; x < min_xsize; x++)
10894 for (y = 0; y < min_ysize; y++)
10895 si->touch.grid_button[i][startx + x][starty + y] =
10896 default_grid_button[y][1][x];
10899 si->touch.grid_initialized = video.initialized;
10901 si->touch.overlay_buttons = FALSE;
10903 si->editor.el_boulderdash = TRUE;
10904 si->editor.el_boulderdash_native = TRUE;
10905 si->editor.el_emerald_mine = TRUE;
10906 si->editor.el_emerald_mine_club = TRUE;
10907 si->editor.el_more = TRUE;
10908 si->editor.el_sokoban = TRUE;
10909 si->editor.el_supaplex = TRUE;
10910 si->editor.el_diamond_caves = TRUE;
10911 si->editor.el_dx_boulderdash = TRUE;
10913 si->editor.el_mirror_magic = TRUE;
10914 si->editor.el_deflektor = TRUE;
10916 si->editor.el_chars = TRUE;
10917 si->editor.el_steel_chars = TRUE;
10919 si->editor.el_classic = TRUE;
10920 si->editor.el_custom = TRUE;
10922 si->editor.el_user_defined = FALSE;
10923 si->editor.el_dynamic = TRUE;
10925 si->editor.el_headlines = TRUE;
10927 si->editor.show_element_token = FALSE;
10929 si->editor.show_read_only_warning = TRUE;
10931 si->editor.use_template_for_new_levels = TRUE;
10933 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10934 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10935 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10936 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10937 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10939 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10940 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10941 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10942 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10943 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10945 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10946 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10947 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10948 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10949 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10950 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10952 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10953 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10954 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10956 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10957 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10958 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10959 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10961 for (i = 0; i < MAX_PLAYERS; i++)
10963 si->input[i].use_joystick = FALSE;
10964 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10965 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10966 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10967 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10968 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10969 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10970 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10971 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10972 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10973 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10974 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10975 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10976 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10977 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10978 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10981 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10982 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10983 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10984 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10986 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10987 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10988 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10989 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10990 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10991 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10992 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10994 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10996 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10997 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10998 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11000 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11001 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11002 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11004 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11005 si->internal.choose_from_top_leveldir = FALSE;
11006 si->internal.show_scaling_in_title = TRUE;
11007 si->internal.create_user_levelset = TRUE;
11008 si->internal.info_screens_from_main = FALSE;
11010 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11011 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11013 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11014 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11015 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11016 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11017 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11018 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11019 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11020 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11021 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11022 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11024 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11025 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11026 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11027 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11028 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11029 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11030 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11031 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11032 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11033 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11035 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11036 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11038 si->debug.show_frames_per_second = FALSE;
11040 si->debug.xsn_mode = AUTO;
11041 si->debug.xsn_percent = 0;
11043 si->options.verbose = FALSE;
11044 si->options.debug = FALSE;
11045 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11047 #if defined(PLATFORM_ANDROID)
11048 si->fullscreen = TRUE;
11049 si->touch.overlay_buttons = TRUE;
11052 setHideSetupEntry(&setup.debug.xsn_mode);
11055 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11057 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11060 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11062 si->player_uuid = NULL; // (will be set later)
11063 si->player_version = 1; // (will be set later)
11065 si->use_api_server = TRUE;
11066 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11067 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11068 si->ask_for_uploading_tapes = TRUE;
11069 si->ask_for_remaining_tapes = FALSE;
11070 si->provide_uploading_tapes = TRUE;
11071 si->ask_for_using_api_server = TRUE;
11072 si->has_remaining_tapes = FALSE;
11075 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11077 si->editor_cascade.el_bd = TRUE;
11078 si->editor_cascade.el_bd_native = TRUE;
11079 si->editor_cascade.el_em = TRUE;
11080 si->editor_cascade.el_emc = TRUE;
11081 si->editor_cascade.el_rnd = TRUE;
11082 si->editor_cascade.el_sb = TRUE;
11083 si->editor_cascade.el_sp = TRUE;
11084 si->editor_cascade.el_dc = TRUE;
11085 si->editor_cascade.el_dx = TRUE;
11087 si->editor_cascade.el_mm = TRUE;
11088 si->editor_cascade.el_df = TRUE;
11090 si->editor_cascade.el_chars = FALSE;
11091 si->editor_cascade.el_steel_chars = FALSE;
11092 si->editor_cascade.el_ce = FALSE;
11093 si->editor_cascade.el_ge = FALSE;
11094 si->editor_cascade.el_es = FALSE;
11095 si->editor_cascade.el_ref = FALSE;
11096 si->editor_cascade.el_user = FALSE;
11097 si->editor_cascade.el_dynamic = FALSE;
11100 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11102 static char *getHideSetupToken(void *setup_value)
11104 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11106 if (setup_value != NULL)
11107 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11109 return hide_setup_token;
11112 void setHideSetupEntry(void *setup_value)
11114 char *hide_setup_token = getHideSetupToken(setup_value);
11116 if (hide_setup_hash == NULL)
11117 hide_setup_hash = newSetupFileHash();
11119 if (setup_value != NULL)
11120 setHashEntry(hide_setup_hash, hide_setup_token, "");
11123 void removeHideSetupEntry(void *setup_value)
11125 char *hide_setup_token = getHideSetupToken(setup_value);
11127 if (setup_value != NULL)
11128 removeHashEntry(hide_setup_hash, hide_setup_token);
11131 boolean hideSetupEntry(void *setup_value)
11133 char *hide_setup_token = getHideSetupToken(setup_value);
11135 return (setup_value != NULL &&
11136 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11139 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11140 struct TokenInfo *token_info,
11141 int token_nr, char *token_text)
11143 char *token_hide_text = getStringCat2(token_text, ".hide");
11144 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11146 // set the value of this setup option in the setup option structure
11147 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11149 // check if this setup option should be hidden in the setup menu
11150 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11151 setHideSetupEntry(token_info[token_nr].value);
11153 free(token_hide_text);
11156 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11157 struct TokenInfo *token_info,
11160 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11161 token_info[token_nr].text);
11164 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11168 if (!setup_file_hash)
11171 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11172 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11174 setup.touch.grid_initialized = TRUE;
11175 for (i = 0; i < 2; i++)
11177 int grid_xsize = setup.touch.grid_xsize[i];
11178 int grid_ysize = setup.touch.grid_ysize[i];
11181 // if virtual buttons are not loaded from setup file, repeat initializing
11182 // virtual buttons grid with default values later when video is initialized
11183 if (grid_xsize == -1 ||
11186 setup.touch.grid_initialized = FALSE;
11191 for (y = 0; y < grid_ysize; y++)
11193 char token_string[MAX_LINE_LEN];
11195 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11197 char *value_string = getHashEntry(setup_file_hash, token_string);
11199 if (value_string == NULL)
11202 for (x = 0; x < grid_xsize; x++)
11204 char c = value_string[x];
11206 setup.touch.grid_button[i][x][y] =
11207 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11212 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11213 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11215 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11216 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11218 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11222 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11224 setup_input = setup.input[pnr];
11225 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11227 char full_token[100];
11229 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11230 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11233 setup.input[pnr] = setup_input;
11236 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11237 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11239 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11240 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11242 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11243 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11245 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11246 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11248 setHideRelatedSetupEntries();
11251 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11255 if (!setup_file_hash)
11258 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11259 setSetupInfo(auto_setup_tokens, i,
11260 getHashEntry(setup_file_hash,
11261 auto_setup_tokens[i].text));
11264 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11268 if (!setup_file_hash)
11271 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11272 setSetupInfo(server_setup_tokens, i,
11273 getHashEntry(setup_file_hash,
11274 server_setup_tokens[i].text));
11277 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11281 if (!setup_file_hash)
11284 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11285 setSetupInfo(editor_cascade_setup_tokens, i,
11286 getHashEntry(setup_file_hash,
11287 editor_cascade_setup_tokens[i].text));
11290 void LoadUserNames(void)
11292 int last_user_nr = user.nr;
11295 if (global.user_names != NULL)
11297 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11298 checked_free(global.user_names[i]);
11300 checked_free(global.user_names);
11303 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11305 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11309 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11311 if (setup_file_hash)
11313 char *player_name = getHashEntry(setup_file_hash, "player_name");
11315 global.user_names[i] = getFixedUserName(player_name);
11317 freeSetupFileHash(setup_file_hash);
11320 if (global.user_names[i] == NULL)
11321 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11324 user.nr = last_user_nr;
11327 void LoadSetupFromFilename(char *filename)
11329 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11331 if (setup_file_hash)
11333 decodeSetupFileHash_Default(setup_file_hash);
11335 freeSetupFileHash(setup_file_hash);
11339 Debug("setup", "using default setup values");
11343 static void LoadSetup_SpecialPostProcessing(void)
11345 char *player_name_new;
11347 // needed to work around problems with fixed length strings
11348 player_name_new = getFixedUserName(setup.player_name);
11349 free(setup.player_name);
11350 setup.player_name = player_name_new;
11352 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11353 if (setup.scroll_delay == FALSE)
11355 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11356 setup.scroll_delay = TRUE; // now always "on"
11359 // make sure that scroll delay value stays inside valid range
11360 setup.scroll_delay_value =
11361 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11364 void LoadSetup_Default(void)
11368 // always start with reliable default values
11369 setSetupInfoToDefaults(&setup);
11371 // try to load setup values from default setup file
11372 filename = getDefaultSetupFilename();
11374 if (fileExists(filename))
11375 LoadSetupFromFilename(filename);
11377 // try to load setup values from platform setup file
11378 filename = getPlatformSetupFilename();
11380 if (fileExists(filename))
11381 LoadSetupFromFilename(filename);
11383 // try to load setup values from user setup file
11384 filename = getSetupFilename();
11386 LoadSetupFromFilename(filename);
11388 LoadSetup_SpecialPostProcessing();
11391 void LoadSetup_AutoSetup(void)
11393 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11394 SetupFileHash *setup_file_hash = NULL;
11396 // always start with reliable default values
11397 setSetupInfoToDefaults_AutoSetup(&setup);
11399 setup_file_hash = loadSetupFileHash(filename);
11401 if (setup_file_hash)
11403 decodeSetupFileHash_AutoSetup(setup_file_hash);
11405 freeSetupFileHash(setup_file_hash);
11411 void LoadSetup_ServerSetup(void)
11413 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11414 SetupFileHash *setup_file_hash = NULL;
11416 // always start with reliable default values
11417 setSetupInfoToDefaults_ServerSetup(&setup);
11419 setup_file_hash = loadSetupFileHash(filename);
11421 if (setup_file_hash)
11423 decodeSetupFileHash_ServerSetup(setup_file_hash);
11425 freeSetupFileHash(setup_file_hash);
11430 if (setup.player_uuid == NULL)
11432 // player UUID does not yet exist in setup file
11433 setup.player_uuid = getStringCopy(getUUID());
11434 setup.player_version = 2;
11436 SaveSetup_ServerSetup();
11440 void LoadSetup_EditorCascade(void)
11442 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11443 SetupFileHash *setup_file_hash = NULL;
11445 // always start with reliable default values
11446 setSetupInfoToDefaults_EditorCascade(&setup);
11448 setup_file_hash = loadSetupFileHash(filename);
11450 if (setup_file_hash)
11452 decodeSetupFileHash_EditorCascade(setup_file_hash);
11454 freeSetupFileHash(setup_file_hash);
11460 void LoadSetup(void)
11462 LoadSetup_Default();
11463 LoadSetup_AutoSetup();
11464 LoadSetup_ServerSetup();
11465 LoadSetup_EditorCascade();
11468 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11469 char *mapping_line)
11471 char mapping_guid[MAX_LINE_LEN];
11472 char *mapping_start, *mapping_end;
11474 // get GUID from game controller mapping line: copy complete line
11475 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11476 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11478 // get GUID from game controller mapping line: cut after GUID part
11479 mapping_start = strchr(mapping_guid, ',');
11480 if (mapping_start != NULL)
11481 *mapping_start = '\0';
11483 // cut newline from game controller mapping line
11484 mapping_end = strchr(mapping_line, '\n');
11485 if (mapping_end != NULL)
11486 *mapping_end = '\0';
11488 // add mapping entry to game controller mappings hash
11489 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11492 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11497 if (!(file = fopen(filename, MODE_READ)))
11499 Warn("cannot read game controller mappings file '%s'", filename);
11504 while (!feof(file))
11506 char line[MAX_LINE_LEN];
11508 if (!fgets(line, MAX_LINE_LEN, file))
11511 addGameControllerMappingToHash(mappings_hash, line);
11517 void SaveSetup_Default(void)
11519 char *filename = getSetupFilename();
11523 InitUserDataDirectory();
11525 if (!(file = fopen(filename, MODE_WRITE)))
11527 Warn("cannot write setup file '%s'", filename);
11532 fprintFileHeader(file, SETUP_FILENAME);
11534 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11536 // just to make things nicer :)
11537 if (global_setup_tokens[i].value == &setup.multiple_users ||
11538 global_setup_tokens[i].value == &setup.sound ||
11539 global_setup_tokens[i].value == &setup.graphics_set ||
11540 global_setup_tokens[i].value == &setup.volume_simple ||
11541 global_setup_tokens[i].value == &setup.network_mode ||
11542 global_setup_tokens[i].value == &setup.touch.control_type ||
11543 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11544 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11545 fprintf(file, "\n");
11547 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11550 for (i = 0; i < 2; i++)
11552 int grid_xsize = setup.touch.grid_xsize[i];
11553 int grid_ysize = setup.touch.grid_ysize[i];
11556 fprintf(file, "\n");
11558 for (y = 0; y < grid_ysize; y++)
11560 char token_string[MAX_LINE_LEN];
11561 char value_string[MAX_LINE_LEN];
11563 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11565 for (x = 0; x < grid_xsize; x++)
11567 char c = setup.touch.grid_button[i][x][y];
11569 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11572 value_string[grid_xsize] = '\0';
11574 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11578 fprintf(file, "\n");
11579 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11580 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11582 fprintf(file, "\n");
11583 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11584 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11586 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11590 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11591 fprintf(file, "\n");
11593 setup_input = setup.input[pnr];
11594 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11595 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11598 fprintf(file, "\n");
11599 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11600 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11602 // (internal setup values not saved to user setup file)
11604 fprintf(file, "\n");
11605 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11606 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11607 setup.debug.xsn_mode != AUTO)
11608 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11610 fprintf(file, "\n");
11611 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11612 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11616 SetFilePermissions(filename, PERMS_PRIVATE);
11619 void SaveSetup_AutoSetup(void)
11621 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11625 InitUserDataDirectory();
11627 if (!(file = fopen(filename, MODE_WRITE)))
11629 Warn("cannot write auto setup file '%s'", filename);
11636 fprintFileHeader(file, AUTOSETUP_FILENAME);
11638 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11639 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11643 SetFilePermissions(filename, PERMS_PRIVATE);
11648 void SaveSetup_ServerSetup(void)
11650 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11654 InitUserDataDirectory();
11656 if (!(file = fopen(filename, MODE_WRITE)))
11658 Warn("cannot write server setup file '%s'", filename);
11665 fprintFileHeader(file, SERVERSETUP_FILENAME);
11667 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11669 // just to make things nicer :)
11670 if (server_setup_tokens[i].value == &setup.use_api_server)
11671 fprintf(file, "\n");
11673 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11678 SetFilePermissions(filename, PERMS_PRIVATE);
11683 void SaveSetup_EditorCascade(void)
11685 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11689 InitUserDataDirectory();
11691 if (!(file = fopen(filename, MODE_WRITE)))
11693 Warn("cannot write editor cascade state file '%s'", filename);
11700 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11702 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11703 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11707 SetFilePermissions(filename, PERMS_PRIVATE);
11712 void SaveSetup(void)
11714 SaveSetup_Default();
11715 SaveSetup_AutoSetup();
11716 SaveSetup_ServerSetup();
11717 SaveSetup_EditorCascade();
11720 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11725 if (!(file = fopen(filename, MODE_WRITE)))
11727 Warn("cannot write game controller mappings file '%s'", filename);
11732 BEGIN_HASH_ITERATION(mappings_hash, itr)
11734 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11736 END_HASH_ITERATION(mappings_hash, itr)
11741 void SaveSetup_AddGameControllerMapping(char *mapping)
11743 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11744 SetupFileHash *mappings_hash = newSetupFileHash();
11746 InitUserDataDirectory();
11748 // load existing personal game controller mappings
11749 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11751 // add new mapping to personal game controller mappings
11752 addGameControllerMappingToHash(mappings_hash, mapping);
11754 // save updated personal game controller mappings
11755 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11757 freeSetupFileHash(mappings_hash);
11761 void LoadCustomElementDescriptions(void)
11763 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11764 SetupFileHash *setup_file_hash;
11767 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11769 if (element_info[i].custom_description != NULL)
11771 free(element_info[i].custom_description);
11772 element_info[i].custom_description = NULL;
11776 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11779 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11781 char *token = getStringCat2(element_info[i].token_name, ".name");
11782 char *value = getHashEntry(setup_file_hash, token);
11785 element_info[i].custom_description = getStringCopy(value);
11790 freeSetupFileHash(setup_file_hash);
11793 static int getElementFromToken(char *token)
11795 char *value = getHashEntry(element_token_hash, token);
11798 return atoi(value);
11800 Warn("unknown element token '%s'", token);
11802 return EL_UNDEFINED;
11805 void FreeGlobalAnimEventInfo(void)
11807 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11809 if (gaei->event_list == NULL)
11814 for (i = 0; i < gaei->num_event_lists; i++)
11816 checked_free(gaei->event_list[i]->event_value);
11817 checked_free(gaei->event_list[i]);
11820 checked_free(gaei->event_list);
11822 gaei->event_list = NULL;
11823 gaei->num_event_lists = 0;
11826 static int AddGlobalAnimEventList(void)
11828 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11829 int list_pos = gaei->num_event_lists++;
11831 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11832 sizeof(struct GlobalAnimEventListInfo *));
11834 gaei->event_list[list_pos] =
11835 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11837 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11839 gaeli->event_value = NULL;
11840 gaeli->num_event_values = 0;
11845 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11847 // do not add empty global animation events
11848 if (event_value == ANIM_EVENT_NONE)
11851 // if list position is undefined, create new list
11852 if (list_pos == ANIM_EVENT_UNDEFINED)
11853 list_pos = AddGlobalAnimEventList();
11855 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11856 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11857 int value_pos = gaeli->num_event_values++;
11859 gaeli->event_value = checked_realloc(gaeli->event_value,
11860 gaeli->num_event_values * sizeof(int *));
11862 gaeli->event_value[value_pos] = event_value;
11867 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11869 if (list_pos == ANIM_EVENT_UNDEFINED)
11870 return ANIM_EVENT_NONE;
11872 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11873 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11875 return gaeli->event_value[value_pos];
11878 int GetGlobalAnimEventValueCount(int list_pos)
11880 if (list_pos == ANIM_EVENT_UNDEFINED)
11883 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11884 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11886 return gaeli->num_event_values;
11889 // This function checks if a string <s> of the format "string1, string2, ..."
11890 // exactly contains a string <s_contained>.
11892 static boolean string_has_parameter(char *s, char *s_contained)
11896 if (s == NULL || s_contained == NULL)
11899 if (strlen(s_contained) > strlen(s))
11902 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11904 char next_char = s[strlen(s_contained)];
11906 // check if next character is delimiter or whitespace
11907 if (next_char == ',' || next_char == '\0' ||
11908 next_char == ' ' || next_char == '\t')
11912 // check if string contains another parameter string after a comma
11913 substring = strchr(s, ',');
11914 if (substring == NULL) // string does not contain a comma
11917 // advance string pointer to next character after the comma
11920 // skip potential whitespaces after the comma
11921 while (*substring == ' ' || *substring == '\t')
11924 return string_has_parameter(substring, s_contained);
11927 static int get_anim_parameter_value_ce(char *s)
11930 char *pattern_1 = "ce_change:custom_";
11931 char *pattern_2 = ".page_";
11932 int pattern_1_len = strlen(pattern_1);
11933 char *matching_char = strstr(s_ptr, pattern_1);
11934 int result = ANIM_EVENT_NONE;
11936 if (matching_char == NULL)
11937 return ANIM_EVENT_NONE;
11939 result = ANIM_EVENT_CE_CHANGE;
11941 s_ptr = matching_char + pattern_1_len;
11943 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11944 if (*s_ptr >= '0' && *s_ptr <= '9')
11946 int gic_ce_nr = (*s_ptr++ - '0');
11948 if (*s_ptr >= '0' && *s_ptr <= '9')
11950 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11952 if (*s_ptr >= '0' && *s_ptr <= '9')
11953 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11956 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11957 return ANIM_EVENT_NONE;
11959 // custom element stored as 0 to 255
11962 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11966 // invalid custom element number specified
11968 return ANIM_EVENT_NONE;
11971 // check for change page number ("page_X" or "page_XX") (optional)
11972 if (strPrefix(s_ptr, pattern_2))
11974 s_ptr += strlen(pattern_2);
11976 if (*s_ptr >= '0' && *s_ptr <= '9')
11978 int gic_page_nr = (*s_ptr++ - '0');
11980 if (*s_ptr >= '0' && *s_ptr <= '9')
11981 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11983 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11984 return ANIM_EVENT_NONE;
11986 // change page stored as 1 to 32 (0 means "all change pages")
11988 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11992 // invalid animation part number specified
11994 return ANIM_EVENT_NONE;
11998 // discard result if next character is neither delimiter nor whitespace
11999 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12000 *s_ptr == ' ' || *s_ptr == '\t'))
12001 return ANIM_EVENT_NONE;
12006 static int get_anim_parameter_value(char *s)
12008 int event_value[] =
12016 char *pattern_1[] =
12024 char *pattern_2 = ".part_";
12025 char *matching_char = NULL;
12027 int pattern_1_len = 0;
12028 int result = ANIM_EVENT_NONE;
12031 result = get_anim_parameter_value_ce(s);
12033 if (result != ANIM_EVENT_NONE)
12036 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12038 matching_char = strstr(s_ptr, pattern_1[i]);
12039 pattern_1_len = strlen(pattern_1[i]);
12040 result = event_value[i];
12042 if (matching_char != NULL)
12046 if (matching_char == NULL)
12047 return ANIM_EVENT_NONE;
12049 s_ptr = matching_char + pattern_1_len;
12051 // check for main animation number ("anim_X" or "anim_XX")
12052 if (*s_ptr >= '0' && *s_ptr <= '9')
12054 int gic_anim_nr = (*s_ptr++ - '0');
12056 if (*s_ptr >= '0' && *s_ptr <= '9')
12057 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12059 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12060 return ANIM_EVENT_NONE;
12062 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12066 // invalid main animation number specified
12068 return ANIM_EVENT_NONE;
12071 // check for animation part number ("part_X" or "part_XX") (optional)
12072 if (strPrefix(s_ptr, pattern_2))
12074 s_ptr += strlen(pattern_2);
12076 if (*s_ptr >= '0' && *s_ptr <= '9')
12078 int gic_part_nr = (*s_ptr++ - '0');
12080 if (*s_ptr >= '0' && *s_ptr <= '9')
12081 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12083 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12084 return ANIM_EVENT_NONE;
12086 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12090 // invalid animation part number specified
12092 return ANIM_EVENT_NONE;
12096 // discard result if next character is neither delimiter nor whitespace
12097 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12098 *s_ptr == ' ' || *s_ptr == '\t'))
12099 return ANIM_EVENT_NONE;
12104 static int get_anim_parameter_values(char *s)
12106 int list_pos = ANIM_EVENT_UNDEFINED;
12107 int event_value = ANIM_EVENT_DEFAULT;
12109 if (string_has_parameter(s, "any"))
12110 event_value |= ANIM_EVENT_ANY;
12112 if (string_has_parameter(s, "click:self") ||
12113 string_has_parameter(s, "click") ||
12114 string_has_parameter(s, "self"))
12115 event_value |= ANIM_EVENT_SELF;
12117 if (string_has_parameter(s, "unclick:any"))
12118 event_value |= ANIM_EVENT_UNCLICK_ANY;
12120 // if animation event found, add it to global animation event list
12121 if (event_value != ANIM_EVENT_NONE)
12122 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12126 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12127 event_value = get_anim_parameter_value(s);
12129 // if animation event found, add it to global animation event list
12130 if (event_value != ANIM_EVENT_NONE)
12131 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12133 // continue with next part of the string, starting with next comma
12134 s = strchr(s + 1, ',');
12140 static int get_anim_action_parameter_value(char *token)
12142 // check most common default case first to massively speed things up
12143 if (strEqual(token, ARG_UNDEFINED))
12144 return ANIM_EVENT_ACTION_NONE;
12146 int result = getImageIDFromToken(token);
12150 char *gfx_token = getStringCat2("gfx.", token);
12152 result = getImageIDFromToken(gfx_token);
12154 checked_free(gfx_token);
12159 Key key = getKeyFromX11KeyName(token);
12161 if (key != KSYM_UNDEFINED)
12162 result = -(int)key;
12169 result = get_hash_from_string(token); // unsigned int => int
12170 result = ABS(result); // may be negative now
12171 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12173 setHashEntry(anim_url_hash, int2str(result, 0), token);
12178 result = ANIM_EVENT_ACTION_NONE;
12183 int get_parameter_value(char *value_raw, char *suffix, int type)
12185 char *value = getStringToLower(value_raw);
12186 int result = 0; // probably a save default value
12188 if (strEqual(suffix, ".direction"))
12190 result = (strEqual(value, "left") ? MV_LEFT :
12191 strEqual(value, "right") ? MV_RIGHT :
12192 strEqual(value, "up") ? MV_UP :
12193 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12195 else if (strEqual(suffix, ".position"))
12197 result = (strEqual(value, "left") ? POS_LEFT :
12198 strEqual(value, "right") ? POS_RIGHT :
12199 strEqual(value, "top") ? POS_TOP :
12200 strEqual(value, "upper") ? POS_UPPER :
12201 strEqual(value, "middle") ? POS_MIDDLE :
12202 strEqual(value, "lower") ? POS_LOWER :
12203 strEqual(value, "bottom") ? POS_BOTTOM :
12204 strEqual(value, "any") ? POS_ANY :
12205 strEqual(value, "ce") ? POS_CE :
12206 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12207 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12209 else if (strEqual(suffix, ".align"))
12211 result = (strEqual(value, "left") ? ALIGN_LEFT :
12212 strEqual(value, "right") ? ALIGN_RIGHT :
12213 strEqual(value, "center") ? ALIGN_CENTER :
12214 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12216 else if (strEqual(suffix, ".valign"))
12218 result = (strEqual(value, "top") ? VALIGN_TOP :
12219 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12220 strEqual(value, "middle") ? VALIGN_MIDDLE :
12221 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12223 else if (strEqual(suffix, ".anim_mode"))
12225 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12226 string_has_parameter(value, "loop") ? ANIM_LOOP :
12227 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12228 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12229 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12230 string_has_parameter(value, "random") ? ANIM_RANDOM :
12231 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12232 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12233 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12234 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12235 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12236 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12237 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12238 string_has_parameter(value, "all") ? ANIM_ALL :
12239 string_has_parameter(value, "tiled") ? ANIM_TILED :
12240 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12243 if (string_has_parameter(value, "once"))
12244 result |= ANIM_ONCE;
12246 if (string_has_parameter(value, "reverse"))
12247 result |= ANIM_REVERSE;
12249 if (string_has_parameter(value, "opaque_player"))
12250 result |= ANIM_OPAQUE_PLAYER;
12252 if (string_has_parameter(value, "static_panel"))
12253 result |= ANIM_STATIC_PANEL;
12255 else if (strEqual(suffix, ".init_event") ||
12256 strEqual(suffix, ".anim_event"))
12258 result = get_anim_parameter_values(value);
12260 else if (strEqual(suffix, ".init_delay_action") ||
12261 strEqual(suffix, ".anim_delay_action") ||
12262 strEqual(suffix, ".post_delay_action") ||
12263 strEqual(suffix, ".init_event_action") ||
12264 strEqual(suffix, ".anim_event_action"))
12266 result = get_anim_action_parameter_value(value_raw);
12268 else if (strEqual(suffix, ".class"))
12270 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12271 get_hash_from_string(value));
12273 else if (strEqual(suffix, ".style"))
12275 result = STYLE_DEFAULT;
12277 if (string_has_parameter(value, "accurate_borders"))
12278 result |= STYLE_ACCURATE_BORDERS;
12280 if (string_has_parameter(value, "inner_corners"))
12281 result |= STYLE_INNER_CORNERS;
12283 if (string_has_parameter(value, "reverse"))
12284 result |= STYLE_REVERSE;
12286 if (string_has_parameter(value, "leftmost_position"))
12287 result |= STYLE_LEFTMOST_POSITION;
12289 if (string_has_parameter(value, "block_clicks"))
12290 result |= STYLE_BLOCK;
12292 if (string_has_parameter(value, "passthrough_clicks"))
12293 result |= STYLE_PASSTHROUGH;
12295 if (string_has_parameter(value, "multiple_actions"))
12296 result |= STYLE_MULTIPLE_ACTIONS;
12298 if (string_has_parameter(value, "consume_ce_event"))
12299 result |= STYLE_CONSUME_CE_EVENT;
12301 else if (strEqual(suffix, ".fade_mode"))
12303 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12304 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12305 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12306 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12307 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12308 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12309 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12310 FADE_MODE_DEFAULT);
12312 else if (strEqual(suffix, ".auto_delay_unit"))
12314 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12315 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12316 AUTO_DELAY_UNIT_DEFAULT);
12318 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12320 result = gfx.get_font_from_token_function(value);
12322 else // generic parameter of type integer or boolean
12324 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12325 type == TYPE_INTEGER ? get_integer_from_string(value) :
12326 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12327 ARG_UNDEFINED_VALUE);
12335 static int get_token_parameter_value(char *token, char *value_raw)
12339 if (token == NULL || value_raw == NULL)
12340 return ARG_UNDEFINED_VALUE;
12342 suffix = strrchr(token, '.');
12343 if (suffix == NULL)
12346 if (strEqual(suffix, ".element"))
12347 return getElementFromToken(value_raw);
12349 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12350 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12353 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12354 boolean ignore_defaults)
12358 for (i = 0; image_config_vars[i].token != NULL; i++)
12360 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12362 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12363 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12367 *image_config_vars[i].value =
12368 get_token_parameter_value(image_config_vars[i].token, value);
12372 void InitMenuDesignSettings_Static(void)
12374 // always start with reliable default values from static default config
12375 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12378 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12382 // the following initializes hierarchical values from static configuration
12384 // special case: initialize "ARG_DEFAULT" values in static default config
12385 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12386 titlescreen_initial_first_default.fade_mode =
12387 title_initial_first_default.fade_mode;
12388 titlescreen_initial_first_default.fade_delay =
12389 title_initial_first_default.fade_delay;
12390 titlescreen_initial_first_default.post_delay =
12391 title_initial_first_default.post_delay;
12392 titlescreen_initial_first_default.auto_delay =
12393 title_initial_first_default.auto_delay;
12394 titlescreen_initial_first_default.auto_delay_unit =
12395 title_initial_first_default.auto_delay_unit;
12396 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12397 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12398 titlescreen_first_default.post_delay = title_first_default.post_delay;
12399 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12400 titlescreen_first_default.auto_delay_unit =
12401 title_first_default.auto_delay_unit;
12402 titlemessage_initial_first_default.fade_mode =
12403 title_initial_first_default.fade_mode;
12404 titlemessage_initial_first_default.fade_delay =
12405 title_initial_first_default.fade_delay;
12406 titlemessage_initial_first_default.post_delay =
12407 title_initial_first_default.post_delay;
12408 titlemessage_initial_first_default.auto_delay =
12409 title_initial_first_default.auto_delay;
12410 titlemessage_initial_first_default.auto_delay_unit =
12411 title_initial_first_default.auto_delay_unit;
12412 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12413 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12414 titlemessage_first_default.post_delay = title_first_default.post_delay;
12415 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12416 titlemessage_first_default.auto_delay_unit =
12417 title_first_default.auto_delay_unit;
12419 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12420 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12421 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12422 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12423 titlescreen_initial_default.auto_delay_unit =
12424 title_initial_default.auto_delay_unit;
12425 titlescreen_default.fade_mode = title_default.fade_mode;
12426 titlescreen_default.fade_delay = title_default.fade_delay;
12427 titlescreen_default.post_delay = title_default.post_delay;
12428 titlescreen_default.auto_delay = title_default.auto_delay;
12429 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12430 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12431 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12432 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12433 titlemessage_initial_default.auto_delay_unit =
12434 title_initial_default.auto_delay_unit;
12435 titlemessage_default.fade_mode = title_default.fade_mode;
12436 titlemessage_default.fade_delay = title_default.fade_delay;
12437 titlemessage_default.post_delay = title_default.post_delay;
12438 titlemessage_default.auto_delay = title_default.auto_delay;
12439 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12441 // special case: initialize "ARG_DEFAULT" values in static default config
12442 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12443 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12445 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12446 titlescreen_first[i] = titlescreen_first_default;
12447 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12448 titlemessage_first[i] = titlemessage_first_default;
12450 titlescreen_initial[i] = titlescreen_initial_default;
12451 titlescreen[i] = titlescreen_default;
12452 titlemessage_initial[i] = titlemessage_initial_default;
12453 titlemessage[i] = titlemessage_default;
12456 // special case: initialize "ARG_DEFAULT" values in static default config
12457 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12458 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12460 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12463 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12464 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12465 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12468 // special case: initialize "ARG_DEFAULT" values in static default config
12469 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12470 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12472 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12473 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12474 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12476 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12479 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12483 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12487 struct XY *dst, *src;
12489 game_buttons_xy[] =
12491 { &game.button.save, &game.button.stop },
12492 { &game.button.pause2, &game.button.pause },
12493 { &game.button.load, &game.button.play },
12494 { &game.button.undo, &game.button.stop },
12495 { &game.button.redo, &game.button.play },
12501 // special case: initialize later added SETUP list size from LEVELS value
12502 if (menu.list_size[GAME_MODE_SETUP] == -1)
12503 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12505 // set default position for snapshot buttons to stop/pause/play buttons
12506 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12507 if ((*game_buttons_xy[i].dst).x == -1 &&
12508 (*game_buttons_xy[i].dst).y == -1)
12509 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12511 // --------------------------------------------------------------------------
12512 // dynamic viewports (including playfield margins, borders and alignments)
12513 // --------------------------------------------------------------------------
12515 // dynamic viewports currently only supported for landscape mode
12516 int display_width = MAX(video.display_width, video.display_height);
12517 int display_height = MIN(video.display_width, video.display_height);
12519 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12521 struct RectWithBorder *vp_window = &viewport.window[i];
12522 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12523 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12524 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12525 boolean dynamic_window_width = (vp_window->min_width != -1);
12526 boolean dynamic_window_height = (vp_window->min_height != -1);
12527 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12528 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12530 // adjust window size if min/max width/height is specified
12532 if (vp_window->min_width != -1)
12534 int window_width = display_width;
12536 // when using static window height, use aspect ratio of display
12537 if (vp_window->min_height == -1)
12538 window_width = vp_window->height * display_width / display_height;
12540 vp_window->width = MAX(vp_window->min_width, window_width);
12543 if (vp_window->min_height != -1)
12545 int window_height = display_height;
12547 // when using static window width, use aspect ratio of display
12548 if (vp_window->min_width == -1)
12549 window_height = vp_window->width * display_height / display_width;
12551 vp_window->height = MAX(vp_window->min_height, window_height);
12554 if (vp_window->max_width != -1)
12555 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12557 if (vp_window->max_height != -1)
12558 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12560 int playfield_width = vp_window->width;
12561 int playfield_height = vp_window->height;
12563 // adjust playfield size and position according to specified margins
12565 playfield_width -= vp_playfield->margin_left;
12566 playfield_width -= vp_playfield->margin_right;
12568 playfield_height -= vp_playfield->margin_top;
12569 playfield_height -= vp_playfield->margin_bottom;
12571 // adjust playfield size if min/max width/height is specified
12573 if (vp_playfield->min_width != -1)
12574 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12576 if (vp_playfield->min_height != -1)
12577 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12579 if (vp_playfield->max_width != -1)
12580 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12582 if (vp_playfield->max_height != -1)
12583 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12585 // adjust playfield position according to specified alignment
12587 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12588 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12589 else if (vp_playfield->align == ALIGN_CENTER)
12590 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12591 else if (vp_playfield->align == ALIGN_RIGHT)
12592 vp_playfield->x += playfield_width - vp_playfield->width;
12594 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12595 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12596 else if (vp_playfield->valign == VALIGN_MIDDLE)
12597 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12598 else if (vp_playfield->valign == VALIGN_BOTTOM)
12599 vp_playfield->y += playfield_height - vp_playfield->height;
12601 vp_playfield->x += vp_playfield->margin_left;
12602 vp_playfield->y += vp_playfield->margin_top;
12604 // adjust individual playfield borders if only default border is specified
12606 if (vp_playfield->border_left == -1)
12607 vp_playfield->border_left = vp_playfield->border_size;
12608 if (vp_playfield->border_right == -1)
12609 vp_playfield->border_right = vp_playfield->border_size;
12610 if (vp_playfield->border_top == -1)
12611 vp_playfield->border_top = vp_playfield->border_size;
12612 if (vp_playfield->border_bottom == -1)
12613 vp_playfield->border_bottom = vp_playfield->border_size;
12615 // set dynamic playfield borders if borders are specified as undefined
12616 // (but only if window size was dynamic and playfield size was static)
12618 if (dynamic_window_width && !dynamic_playfield_width)
12620 if (vp_playfield->border_left == -1)
12622 vp_playfield->border_left = (vp_playfield->x -
12623 vp_playfield->margin_left);
12624 vp_playfield->x -= vp_playfield->border_left;
12625 vp_playfield->width += vp_playfield->border_left;
12628 if (vp_playfield->border_right == -1)
12630 vp_playfield->border_right = (vp_window->width -
12632 vp_playfield->width -
12633 vp_playfield->margin_right);
12634 vp_playfield->width += vp_playfield->border_right;
12638 if (dynamic_window_height && !dynamic_playfield_height)
12640 if (vp_playfield->border_top == -1)
12642 vp_playfield->border_top = (vp_playfield->y -
12643 vp_playfield->margin_top);
12644 vp_playfield->y -= vp_playfield->border_top;
12645 vp_playfield->height += vp_playfield->border_top;
12648 if (vp_playfield->border_bottom == -1)
12650 vp_playfield->border_bottom = (vp_window->height -
12652 vp_playfield->height -
12653 vp_playfield->margin_bottom);
12654 vp_playfield->height += vp_playfield->border_bottom;
12658 // adjust playfield size to be a multiple of a defined alignment tile size
12660 int align_size = vp_playfield->align_size;
12661 int playfield_xtiles = vp_playfield->width / align_size;
12662 int playfield_ytiles = vp_playfield->height / align_size;
12663 int playfield_width_corrected = playfield_xtiles * align_size;
12664 int playfield_height_corrected = playfield_ytiles * align_size;
12665 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12666 i == GFX_SPECIAL_ARG_EDITOR);
12668 if (is_playfield_mode &&
12669 dynamic_playfield_width &&
12670 vp_playfield->width != playfield_width_corrected)
12672 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12674 vp_playfield->width = playfield_width_corrected;
12676 if (vp_playfield->align == ALIGN_LEFT)
12678 vp_playfield->border_left += playfield_xdiff;
12680 else if (vp_playfield->align == ALIGN_RIGHT)
12682 vp_playfield->border_right += playfield_xdiff;
12684 else if (vp_playfield->align == ALIGN_CENTER)
12686 int border_left_diff = playfield_xdiff / 2;
12687 int border_right_diff = playfield_xdiff - border_left_diff;
12689 vp_playfield->border_left += border_left_diff;
12690 vp_playfield->border_right += border_right_diff;
12694 if (is_playfield_mode &&
12695 dynamic_playfield_height &&
12696 vp_playfield->height != playfield_height_corrected)
12698 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12700 vp_playfield->height = playfield_height_corrected;
12702 if (vp_playfield->valign == VALIGN_TOP)
12704 vp_playfield->border_top += playfield_ydiff;
12706 else if (vp_playfield->align == VALIGN_BOTTOM)
12708 vp_playfield->border_right += playfield_ydiff;
12710 else if (vp_playfield->align == VALIGN_MIDDLE)
12712 int border_top_diff = playfield_ydiff / 2;
12713 int border_bottom_diff = playfield_ydiff - border_top_diff;
12715 vp_playfield->border_top += border_top_diff;
12716 vp_playfield->border_bottom += border_bottom_diff;
12720 // adjust door positions according to specified alignment
12722 for (j = 0; j < 2; j++)
12724 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12726 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12727 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12728 else if (vp_door->align == ALIGN_CENTER)
12729 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12730 else if (vp_door->align == ALIGN_RIGHT)
12731 vp_door->x += vp_window->width - vp_door->width;
12733 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12734 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12735 else if (vp_door->valign == VALIGN_MIDDLE)
12736 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12737 else if (vp_door->valign == VALIGN_BOTTOM)
12738 vp_door->y += vp_window->height - vp_door->height;
12743 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12747 struct XYTileSize *dst, *src;
12750 editor_buttons_xy[] =
12753 &editor.button.element_left, &editor.palette.element_left,
12754 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12757 &editor.button.element_middle, &editor.palette.element_middle,
12758 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12761 &editor.button.element_right, &editor.palette.element_right,
12762 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12769 // set default position for element buttons to element graphics
12770 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12772 if ((*editor_buttons_xy[i].dst).x == -1 &&
12773 (*editor_buttons_xy[i].dst).y == -1)
12775 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12777 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12779 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12783 // adjust editor palette rows and columns if specified to be dynamic
12785 if (editor.palette.cols == -1)
12787 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12788 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12789 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12791 editor.palette.cols = (vp_width - sc_width) / bt_width;
12793 if (editor.palette.x == -1)
12795 int palette_width = editor.palette.cols * bt_width + sc_width;
12797 editor.palette.x = (vp_width - palette_width) / 2;
12801 if (editor.palette.rows == -1)
12803 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12804 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12805 int tx_height = getFontHeight(FONT_TEXT_2);
12807 editor.palette.rows = (vp_height - tx_height) / bt_height;
12809 if (editor.palette.y == -1)
12811 int palette_height = editor.palette.rows * bt_height + tx_height;
12813 editor.palette.y = (vp_height - palette_height) / 2;
12818 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12819 boolean initialize)
12821 // special case: check if network and preview player positions are redefined,
12822 // to compare this later against the main menu level preview being redefined
12823 struct TokenIntPtrInfo menu_config_players[] =
12825 { "main.network_players.x", &menu.main.network_players.redefined },
12826 { "main.network_players.y", &menu.main.network_players.redefined },
12827 { "main.preview_players.x", &menu.main.preview_players.redefined },
12828 { "main.preview_players.y", &menu.main.preview_players.redefined },
12829 { "preview.x", &preview.redefined },
12830 { "preview.y", &preview.redefined }
12836 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12837 *menu_config_players[i].value = FALSE;
12841 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12842 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12843 *menu_config_players[i].value = TRUE;
12847 static void InitMenuDesignSettings_PreviewPlayers(void)
12849 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12852 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12854 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12857 static void LoadMenuDesignSettingsFromFilename(char *filename)
12859 static struct TitleFadingInfo tfi;
12860 static struct TitleMessageInfo tmi;
12861 static struct TokenInfo title_tokens[] =
12863 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12864 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12865 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12866 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12867 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12871 static struct TokenInfo titlemessage_tokens[] =
12873 { TYPE_INTEGER, &tmi.x, ".x" },
12874 { TYPE_INTEGER, &tmi.y, ".y" },
12875 { TYPE_INTEGER, &tmi.width, ".width" },
12876 { TYPE_INTEGER, &tmi.height, ".height" },
12877 { TYPE_INTEGER, &tmi.chars, ".chars" },
12878 { TYPE_INTEGER, &tmi.lines, ".lines" },
12879 { TYPE_INTEGER, &tmi.align, ".align" },
12880 { TYPE_INTEGER, &tmi.valign, ".valign" },
12881 { TYPE_INTEGER, &tmi.font, ".font" },
12882 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12883 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12884 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12885 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12886 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12887 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12888 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12889 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12890 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12896 struct TitleFadingInfo *info;
12901 // initialize first titles from "enter screen" definitions, if defined
12902 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12903 { &title_first_default, "menu.enter_screen.TITLE" },
12905 // initialize title screens from "next screen" definitions, if defined
12906 { &title_initial_default, "menu.next_screen.TITLE" },
12907 { &title_default, "menu.next_screen.TITLE" },
12913 struct TitleMessageInfo *array;
12916 titlemessage_arrays[] =
12918 // initialize first titles from "enter screen" definitions, if defined
12919 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12920 { titlescreen_first, "menu.enter_screen.TITLE" },
12921 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12922 { titlemessage_first, "menu.enter_screen.TITLE" },
12924 // initialize titles from "next screen" definitions, if defined
12925 { titlescreen_initial, "menu.next_screen.TITLE" },
12926 { titlescreen, "menu.next_screen.TITLE" },
12927 { titlemessage_initial, "menu.next_screen.TITLE" },
12928 { titlemessage, "menu.next_screen.TITLE" },
12930 // overwrite titles with title definitions, if defined
12931 { titlescreen_initial_first, "[title_initial]" },
12932 { titlescreen_first, "[title]" },
12933 { titlemessage_initial_first, "[title_initial]" },
12934 { titlemessage_first, "[title]" },
12936 { titlescreen_initial, "[title_initial]" },
12937 { titlescreen, "[title]" },
12938 { titlemessage_initial, "[title_initial]" },
12939 { titlemessage, "[title]" },
12941 // overwrite titles with title screen/message definitions, if defined
12942 { titlescreen_initial_first, "[titlescreen_initial]" },
12943 { titlescreen_first, "[titlescreen]" },
12944 { titlemessage_initial_first, "[titlemessage_initial]" },
12945 { titlemessage_first, "[titlemessage]" },
12947 { titlescreen_initial, "[titlescreen_initial]" },
12948 { titlescreen, "[titlescreen]" },
12949 { titlemessage_initial, "[titlemessage_initial]" },
12950 { titlemessage, "[titlemessage]" },
12954 SetupFileHash *setup_file_hash;
12957 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12960 // the following initializes hierarchical values from dynamic configuration
12962 // special case: initialize with default values that may be overwritten
12963 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12964 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12966 struct TokenIntPtrInfo menu_config[] =
12968 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12969 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12970 { "menu.list_size", &menu.list_size[i] }
12973 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12975 char *token = menu_config[j].token;
12976 char *value = getHashEntry(setup_file_hash, token);
12979 *menu_config[j].value = get_integer_from_string(value);
12983 // special case: initialize with default values that may be overwritten
12984 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12985 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12987 struct TokenIntPtrInfo menu_config[] =
12989 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12990 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12991 { "menu.list_size.INFO", &menu.list_size_info[i] },
12992 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12993 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12996 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12998 char *token = menu_config[j].token;
12999 char *value = getHashEntry(setup_file_hash, token);
13002 *menu_config[j].value = get_integer_from_string(value);
13006 // special case: initialize with default values that may be overwritten
13007 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13008 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13010 struct TokenIntPtrInfo menu_config[] =
13012 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13013 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13016 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13018 char *token = menu_config[j].token;
13019 char *value = getHashEntry(setup_file_hash, token);
13022 *menu_config[j].value = get_integer_from_string(value);
13026 // special case: initialize with default values that may be overwritten
13027 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13028 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13030 struct TokenIntPtrInfo menu_config[] =
13032 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13033 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13034 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13035 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13036 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13037 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13038 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13039 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13040 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13041 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13044 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13046 char *token = menu_config[j].token;
13047 char *value = getHashEntry(setup_file_hash, token);
13050 *menu_config[j].value = get_integer_from_string(value);
13054 // special case: initialize with default values that may be overwritten
13055 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13056 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13058 struct TokenIntPtrInfo menu_config[] =
13060 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13061 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13062 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13063 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13064 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13065 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13066 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13067 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13068 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13071 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13073 char *token = menu_config[j].token;
13074 char *value = getHashEntry(setup_file_hash, token);
13077 *menu_config[j].value = get_token_parameter_value(token, value);
13081 // special case: initialize with default values that may be overwritten
13082 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13083 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13087 char *token_prefix;
13088 struct RectWithBorder *struct_ptr;
13092 { "viewport.window", &viewport.window[i] },
13093 { "viewport.playfield", &viewport.playfield[i] },
13094 { "viewport.door_1", &viewport.door_1[i] },
13095 { "viewport.door_2", &viewport.door_2[i] }
13098 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13100 struct TokenIntPtrInfo vp_config[] =
13102 { ".x", &vp_struct[j].struct_ptr->x },
13103 { ".y", &vp_struct[j].struct_ptr->y },
13104 { ".width", &vp_struct[j].struct_ptr->width },
13105 { ".height", &vp_struct[j].struct_ptr->height },
13106 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13107 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13108 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13109 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13110 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13111 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13112 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13113 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13114 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13115 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13116 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13117 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13118 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13119 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13120 { ".align", &vp_struct[j].struct_ptr->align },
13121 { ".valign", &vp_struct[j].struct_ptr->valign }
13124 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13126 char *token = getStringCat2(vp_struct[j].token_prefix,
13127 vp_config[k].token);
13128 char *value = getHashEntry(setup_file_hash, token);
13131 *vp_config[k].value = get_token_parameter_value(token, value);
13138 // special case: initialize with default values that may be overwritten
13139 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13140 for (i = 0; title_info[i].info != NULL; i++)
13142 struct TitleFadingInfo *info = title_info[i].info;
13143 char *base_token = title_info[i].text;
13145 for (j = 0; title_tokens[j].type != -1; j++)
13147 char *token = getStringCat2(base_token, title_tokens[j].text);
13148 char *value = getHashEntry(setup_file_hash, token);
13152 int parameter_value = get_token_parameter_value(token, value);
13156 *(int *)title_tokens[j].value = (int)parameter_value;
13165 // special case: initialize with default values that may be overwritten
13166 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13167 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13169 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13170 char *base_token = titlemessage_arrays[i].text;
13172 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13174 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13175 char *value = getHashEntry(setup_file_hash, token);
13179 int parameter_value = get_token_parameter_value(token, value);
13181 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13185 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13186 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13188 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13198 // read (and overwrite with) values that may be specified in config file
13199 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13201 // special case: check if network and preview player positions are redefined
13202 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13204 freeSetupFileHash(setup_file_hash);
13207 void LoadMenuDesignSettings(void)
13209 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13211 InitMenuDesignSettings_Static();
13212 InitMenuDesignSettings_SpecialPreProcessing();
13213 InitMenuDesignSettings_PreviewPlayers();
13215 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13217 // first look for special settings configured in level series config
13218 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13220 if (fileExists(filename_base))
13221 LoadMenuDesignSettingsFromFilename(filename_base);
13224 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13226 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13227 LoadMenuDesignSettingsFromFilename(filename_local);
13229 InitMenuDesignSettings_SpecialPostProcessing();
13232 void LoadMenuDesignSettings_AfterGraphics(void)
13234 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13237 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13238 boolean ignore_defaults)
13242 for (i = 0; sound_config_vars[i].token != NULL; i++)
13244 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13246 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13247 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13251 *sound_config_vars[i].value =
13252 get_token_parameter_value(sound_config_vars[i].token, value);
13256 void InitSoundSettings_Static(void)
13258 // always start with reliable default values from static default config
13259 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13262 static void LoadSoundSettingsFromFilename(char *filename)
13264 SetupFileHash *setup_file_hash;
13266 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13269 // read (and overwrite with) values that may be specified in config file
13270 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13272 freeSetupFileHash(setup_file_hash);
13275 void LoadSoundSettings(void)
13277 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13279 InitSoundSettings_Static();
13281 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13283 // first look for special settings configured in level series config
13284 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13286 if (fileExists(filename_base))
13287 LoadSoundSettingsFromFilename(filename_base);
13290 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13292 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13293 LoadSoundSettingsFromFilename(filename_local);
13296 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13298 char *filename = getEditorSetupFilename();
13299 SetupFileList *setup_file_list, *list;
13300 SetupFileHash *element_hash;
13301 int num_unknown_tokens = 0;
13304 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13307 element_hash = newSetupFileHash();
13309 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13310 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13312 // determined size may be larger than needed (due to unknown elements)
13314 for (list = setup_file_list; list != NULL; list = list->next)
13317 // add space for up to 3 more elements for padding that may be needed
13318 *num_elements += 3;
13320 // free memory for old list of elements, if needed
13321 checked_free(*elements);
13323 // allocate memory for new list of elements
13324 *elements = checked_malloc(*num_elements * sizeof(int));
13327 for (list = setup_file_list; list != NULL; list = list->next)
13329 char *value = getHashEntry(element_hash, list->token);
13331 if (value == NULL) // try to find obsolete token mapping
13333 char *mapped_token = get_mapped_token(list->token);
13335 if (mapped_token != NULL)
13337 value = getHashEntry(element_hash, mapped_token);
13339 free(mapped_token);
13345 (*elements)[(*num_elements)++] = atoi(value);
13349 if (num_unknown_tokens == 0)
13352 Warn("unknown token(s) found in config file:");
13353 Warn("- config file: '%s'", filename);
13355 num_unknown_tokens++;
13358 Warn("- token: '%s'", list->token);
13362 if (num_unknown_tokens > 0)
13365 while (*num_elements % 4) // pad with empty elements, if needed
13366 (*elements)[(*num_elements)++] = EL_EMPTY;
13368 freeSetupFileList(setup_file_list);
13369 freeSetupFileHash(element_hash);
13372 for (i = 0; i < *num_elements; i++)
13373 Debug("editor", "element '%s' [%d]\n",
13374 element_info[(*elements)[i]].token_name, (*elements)[i]);
13378 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13381 SetupFileHash *setup_file_hash = NULL;
13382 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13383 char *filename_music, *filename_prefix, *filename_info;
13389 token_to_value_ptr[] =
13391 { "title_header", &tmp_music_file_info.title_header },
13392 { "artist_header", &tmp_music_file_info.artist_header },
13393 { "album_header", &tmp_music_file_info.album_header },
13394 { "year_header", &tmp_music_file_info.year_header },
13395 { "played_header", &tmp_music_file_info.played_header },
13397 { "title", &tmp_music_file_info.title },
13398 { "artist", &tmp_music_file_info.artist },
13399 { "album", &tmp_music_file_info.album },
13400 { "year", &tmp_music_file_info.year },
13401 { "played", &tmp_music_file_info.played },
13407 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13408 getCustomMusicFilename(basename));
13410 if (filename_music == NULL)
13413 // ---------- try to replace file extension ----------
13415 filename_prefix = getStringCopy(filename_music);
13416 if (strrchr(filename_prefix, '.') != NULL)
13417 *strrchr(filename_prefix, '.') = '\0';
13418 filename_info = getStringCat2(filename_prefix, ".txt");
13420 if (fileExists(filename_info))
13421 setup_file_hash = loadSetupFileHash(filename_info);
13423 free(filename_prefix);
13424 free(filename_info);
13426 if (setup_file_hash == NULL)
13428 // ---------- try to add file extension ----------
13430 filename_prefix = getStringCopy(filename_music);
13431 filename_info = getStringCat2(filename_prefix, ".txt");
13433 if (fileExists(filename_info))
13434 setup_file_hash = loadSetupFileHash(filename_info);
13436 free(filename_prefix);
13437 free(filename_info);
13440 if (setup_file_hash == NULL)
13443 // ---------- music file info found ----------
13445 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13447 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13449 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13451 *token_to_value_ptr[i].value_ptr =
13452 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13455 tmp_music_file_info.basename = getStringCopy(basename);
13456 tmp_music_file_info.music = music;
13457 tmp_music_file_info.is_sound = is_sound;
13459 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13460 *new_music_file_info = tmp_music_file_info;
13462 return new_music_file_info;
13465 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13467 return get_music_file_info_ext(basename, music, FALSE);
13470 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13472 return get_music_file_info_ext(basename, sound, TRUE);
13475 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13476 char *basename, boolean is_sound)
13478 for (; list != NULL; list = list->next)
13479 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13485 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13487 return music_info_listed_ext(list, basename, FALSE);
13490 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13492 return music_info_listed_ext(list, basename, TRUE);
13495 void LoadMusicInfo(void)
13497 int num_music_noconf = getMusicListSize_NoConf();
13498 int num_music = getMusicListSize();
13499 int num_sounds = getSoundListSize();
13500 struct FileInfo *music, *sound;
13501 struct MusicFileInfo *next, **new;
13505 while (music_file_info != NULL)
13507 next = music_file_info->next;
13509 checked_free(music_file_info->basename);
13511 checked_free(music_file_info->title_header);
13512 checked_free(music_file_info->artist_header);
13513 checked_free(music_file_info->album_header);
13514 checked_free(music_file_info->year_header);
13515 checked_free(music_file_info->played_header);
13517 checked_free(music_file_info->title);
13518 checked_free(music_file_info->artist);
13519 checked_free(music_file_info->album);
13520 checked_free(music_file_info->year);
13521 checked_free(music_file_info->played);
13523 free(music_file_info);
13525 music_file_info = next;
13528 new = &music_file_info;
13530 // get (configured or unconfigured) music file info for all levels
13531 for (i = leveldir_current->first_level;
13532 i <= leveldir_current->last_level; i++)
13536 if (levelset.music[i] != MUS_UNDEFINED)
13538 // get music file info for configured level music
13539 music_nr = levelset.music[i];
13541 else if (num_music_noconf > 0)
13543 // get music file info for unconfigured level music
13544 int level_pos = i - leveldir_current->first_level;
13546 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13553 char *basename = getMusicInfoEntryFilename(music_nr);
13555 if (basename == NULL)
13558 if (!music_info_listed(music_file_info, basename))
13560 *new = get_music_file_info(basename, music_nr);
13563 new = &(*new)->next;
13567 // get music file info for all remaining configured music files
13568 for (i = 0; i < num_music; i++)
13570 music = getMusicListEntry(i);
13572 if (music->filename == NULL)
13575 if (strEqual(music->filename, UNDEFINED_FILENAME))
13578 // a configured file may be not recognized as music
13579 if (!FileIsMusic(music->filename))
13582 if (!music_info_listed(music_file_info, music->filename))
13584 *new = get_music_file_info(music->filename, i);
13587 new = &(*new)->next;
13591 // get sound file info for all configured sound files
13592 for (i = 0; i < num_sounds; i++)
13594 sound = getSoundListEntry(i);
13596 if (sound->filename == NULL)
13599 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13602 // a configured file may be not recognized as sound
13603 if (!FileIsSound(sound->filename))
13606 if (!sound_info_listed(music_file_info, sound->filename))
13608 *new = get_sound_file_info(sound->filename, i);
13610 new = &(*new)->next;
13614 // add pointers to previous list nodes
13616 struct MusicFileInfo *node = music_file_info;
13618 while (node != NULL)
13621 node->next->prev = node;
13627 static void add_helpanim_entry(int element, int action, int direction,
13628 int delay, int *num_list_entries)
13630 struct HelpAnimInfo *new_list_entry;
13631 (*num_list_entries)++;
13634 checked_realloc(helpanim_info,
13635 *num_list_entries * sizeof(struct HelpAnimInfo));
13636 new_list_entry = &helpanim_info[*num_list_entries - 1];
13638 new_list_entry->element = element;
13639 new_list_entry->action = action;
13640 new_list_entry->direction = direction;
13641 new_list_entry->delay = delay;
13644 static void print_unknown_token(char *filename, char *token, int token_nr)
13649 Warn("unknown token(s) found in config file:");
13650 Warn("- config file: '%s'", filename);
13653 Warn("- token: '%s'", token);
13656 static void print_unknown_token_end(int token_nr)
13662 void LoadHelpAnimInfo(void)
13664 char *filename = getHelpAnimFilename();
13665 SetupFileList *setup_file_list = NULL, *list;
13666 SetupFileHash *element_hash, *action_hash, *direction_hash;
13667 int num_list_entries = 0;
13668 int num_unknown_tokens = 0;
13671 if (fileExists(filename))
13672 setup_file_list = loadSetupFileList(filename);
13674 if (setup_file_list == NULL)
13676 // use reliable default values from static configuration
13677 SetupFileList *insert_ptr;
13679 insert_ptr = setup_file_list =
13680 newSetupFileList(helpanim_config[0].token,
13681 helpanim_config[0].value);
13683 for (i = 1; helpanim_config[i].token; i++)
13684 insert_ptr = addListEntry(insert_ptr,
13685 helpanim_config[i].token,
13686 helpanim_config[i].value);
13689 element_hash = newSetupFileHash();
13690 action_hash = newSetupFileHash();
13691 direction_hash = newSetupFileHash();
13693 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13694 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13696 for (i = 0; i < NUM_ACTIONS; i++)
13697 setHashEntry(action_hash, element_action_info[i].suffix,
13698 i_to_a(element_action_info[i].value));
13700 // do not store direction index (bit) here, but direction value!
13701 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13702 setHashEntry(direction_hash, element_direction_info[i].suffix,
13703 i_to_a(1 << element_direction_info[i].value));
13705 for (list = setup_file_list; list != NULL; list = list->next)
13707 char *element_token, *action_token, *direction_token;
13708 char *element_value, *action_value, *direction_value;
13709 int delay = atoi(list->value);
13711 if (strEqual(list->token, "end"))
13713 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13718 /* first try to break element into element/action/direction parts;
13719 if this does not work, also accept combined "element[.act][.dir]"
13720 elements (like "dynamite.active"), which are unique elements */
13722 if (strchr(list->token, '.') == NULL) // token contains no '.'
13724 element_value = getHashEntry(element_hash, list->token);
13725 if (element_value != NULL) // element found
13726 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13727 &num_list_entries);
13730 // no further suffixes found -- this is not an element
13731 print_unknown_token(filename, list->token, num_unknown_tokens++);
13737 // token has format "<prefix>.<something>"
13739 action_token = strchr(list->token, '.'); // suffix may be action ...
13740 direction_token = action_token; // ... or direction
13742 element_token = getStringCopy(list->token);
13743 *strchr(element_token, '.') = '\0';
13745 element_value = getHashEntry(element_hash, element_token);
13747 if (element_value == NULL) // this is no element
13749 element_value = getHashEntry(element_hash, list->token);
13750 if (element_value != NULL) // combined element found
13751 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13752 &num_list_entries);
13754 print_unknown_token(filename, list->token, num_unknown_tokens++);
13756 free(element_token);
13761 action_value = getHashEntry(action_hash, action_token);
13763 if (action_value != NULL) // action found
13765 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13766 &num_list_entries);
13768 free(element_token);
13773 direction_value = getHashEntry(direction_hash, direction_token);
13775 if (direction_value != NULL) // direction found
13777 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13778 &num_list_entries);
13780 free(element_token);
13785 if (strchr(action_token + 1, '.') == NULL)
13787 // no further suffixes found -- this is not an action nor direction
13789 element_value = getHashEntry(element_hash, list->token);
13790 if (element_value != NULL) // combined element found
13791 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13792 &num_list_entries);
13794 print_unknown_token(filename, list->token, num_unknown_tokens++);
13796 free(element_token);
13801 // token has format "<prefix>.<suffix>.<something>"
13803 direction_token = strchr(action_token + 1, '.');
13805 action_token = getStringCopy(action_token);
13806 *strchr(action_token + 1, '.') = '\0';
13808 action_value = getHashEntry(action_hash, action_token);
13810 if (action_value == NULL) // this is no action
13812 element_value = getHashEntry(element_hash, list->token);
13813 if (element_value != NULL) // combined element found
13814 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13815 &num_list_entries);
13817 print_unknown_token(filename, list->token, num_unknown_tokens++);
13819 free(element_token);
13820 free(action_token);
13825 direction_value = getHashEntry(direction_hash, direction_token);
13827 if (direction_value != NULL) // direction found
13829 add_helpanim_entry(atoi(element_value), atoi(action_value),
13830 atoi(direction_value), delay, &num_list_entries);
13832 free(element_token);
13833 free(action_token);
13838 // this is no direction
13840 element_value = getHashEntry(element_hash, list->token);
13841 if (element_value != NULL) // combined element found
13842 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13843 &num_list_entries);
13845 print_unknown_token(filename, list->token, num_unknown_tokens++);
13847 free(element_token);
13848 free(action_token);
13851 print_unknown_token_end(num_unknown_tokens);
13853 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13854 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13856 freeSetupFileList(setup_file_list);
13857 freeSetupFileHash(element_hash);
13858 freeSetupFileHash(action_hash);
13859 freeSetupFileHash(direction_hash);
13862 for (i = 0; i < num_list_entries; i++)
13863 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13864 EL_NAME(helpanim_info[i].element),
13865 helpanim_info[i].element,
13866 helpanim_info[i].action,
13867 helpanim_info[i].direction,
13868 helpanim_info[i].delay);
13872 void LoadHelpTextInfo(void)
13874 char *filename = getHelpTextFilename();
13877 if (helptext_info != NULL)
13879 freeSetupFileHash(helptext_info);
13880 helptext_info = NULL;
13883 if (fileExists(filename))
13884 helptext_info = loadSetupFileHash(filename);
13886 if (helptext_info == NULL)
13888 // use reliable default values from static configuration
13889 helptext_info = newSetupFileHash();
13891 for (i = 0; helptext_config[i].token; i++)
13892 setHashEntry(helptext_info,
13893 helptext_config[i].token,
13894 helptext_config[i].value);
13898 BEGIN_HASH_ITERATION(helptext_info, itr)
13900 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13901 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13903 END_HASH_ITERATION(hash, itr)
13908 // ----------------------------------------------------------------------------
13910 // ----------------------------------------------------------------------------
13912 #define MAX_NUM_CONVERT_LEVELS 1000
13914 void ConvertLevels(void)
13916 static LevelDirTree *convert_leveldir = NULL;
13917 static int convert_level_nr = -1;
13918 static int num_levels_handled = 0;
13919 static int num_levels_converted = 0;
13920 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13923 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13924 global.convert_leveldir);
13926 if (convert_leveldir == NULL)
13927 Fail("no such level identifier: '%s'", global.convert_leveldir);
13929 leveldir_current = convert_leveldir;
13931 if (global.convert_level_nr != -1)
13933 convert_leveldir->first_level = global.convert_level_nr;
13934 convert_leveldir->last_level = global.convert_level_nr;
13937 convert_level_nr = convert_leveldir->first_level;
13939 PrintLine("=", 79);
13940 Print("Converting levels\n");
13941 PrintLine("-", 79);
13942 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13943 Print("Level series name: '%s'\n", convert_leveldir->name);
13944 Print("Level series author: '%s'\n", convert_leveldir->author);
13945 Print("Number of levels: %d\n", convert_leveldir->levels);
13946 PrintLine("=", 79);
13949 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13950 levels_failed[i] = FALSE;
13952 while (convert_level_nr <= convert_leveldir->last_level)
13954 char *level_filename;
13957 level_nr = convert_level_nr++;
13959 Print("Level %03d: ", level_nr);
13961 LoadLevel(level_nr);
13962 if (level.no_level_file || level.no_valid_file)
13964 Print("(no level)\n");
13968 Print("converting level ... ");
13971 // special case: conversion of some EMC levels as requested by ACME
13972 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13975 level_filename = getDefaultLevelFilename(level_nr);
13976 new_level = !fileExists(level_filename);
13980 SaveLevel(level_nr);
13982 num_levels_converted++;
13984 Print("converted.\n");
13988 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13989 levels_failed[level_nr] = TRUE;
13991 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13994 num_levels_handled++;
13998 PrintLine("=", 79);
13999 Print("Number of levels handled: %d\n", num_levels_handled);
14000 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14001 (num_levels_handled ?
14002 num_levels_converted * 100 / num_levels_handled : 0));
14003 PrintLine("-", 79);
14004 Print("Summary (for automatic parsing by scripts):\n");
14005 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14006 convert_leveldir->identifier, num_levels_converted,
14007 num_levels_handled,
14008 (num_levels_handled ?
14009 num_levels_converted * 100 / num_levels_handled : 0));
14011 if (num_levels_handled != num_levels_converted)
14013 Print(", FAILED:");
14014 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14015 if (levels_failed[i])
14020 PrintLine("=", 79);
14022 CloseAllAndExit(0);
14026 // ----------------------------------------------------------------------------
14027 // create and save images for use in level sketches (raw BMP format)
14028 // ----------------------------------------------------------------------------
14030 void CreateLevelSketchImages(void)
14036 InitElementPropertiesGfxElement();
14038 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14039 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14041 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14043 int element = getMappedElement(i);
14044 char basename1[16];
14045 char basename2[16];
14049 sprintf(basename1, "%04d.bmp", i);
14050 sprintf(basename2, "%04ds.bmp", i);
14052 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14053 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14055 DrawSizedElement(0, 0, element, TILESIZE);
14056 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14058 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14059 Fail("cannot save level sketch image file '%s'", filename1);
14061 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14062 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14064 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14065 Fail("cannot save level sketch image file '%s'", filename2);
14070 // create corresponding SQL statements (for normal and small images)
14073 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14074 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14077 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14078 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14080 // optional: create content for forum level sketch demonstration post
14082 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14085 FreeBitmap(bitmap1);
14086 FreeBitmap(bitmap2);
14089 fprintf(stderr, "\n");
14091 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14093 CloseAllAndExit(0);
14097 // ----------------------------------------------------------------------------
14098 // create and save images for element collecting animations (raw BMP format)
14099 // ----------------------------------------------------------------------------
14101 static boolean createCollectImage(int element)
14103 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14106 void CreateCollectElementImages(void)
14110 int anim_frames = num_steps - 1;
14111 int tile_size = TILESIZE;
14112 int anim_width = tile_size * anim_frames;
14113 int anim_height = tile_size;
14114 int num_collect_images = 0;
14115 int pos_collect_images = 0;
14117 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14118 if (createCollectImage(i))
14119 num_collect_images++;
14121 Info("Creating %d element collecting animation images ...",
14122 num_collect_images);
14124 int dst_width = anim_width * 2;
14125 int dst_height = anim_height * num_collect_images / 2;
14126 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14127 char *basename_bmp = "RocksCollect.bmp";
14128 char *basename_png = "RocksCollect.png";
14129 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14130 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14131 int len_filename_bmp = strlen(filename_bmp);
14132 int len_filename_png = strlen(filename_png);
14133 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14134 char cmd_convert[max_command_len];
14136 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14140 // force using RGBA surface for destination bitmap
14141 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14142 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14144 dst_bitmap->surface =
14145 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14147 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14149 if (!createCollectImage(i))
14152 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14153 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14154 int graphic = el2img(i);
14155 char *token_name = element_info[i].token_name;
14156 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14157 Bitmap *src_bitmap;
14160 Info("- creating collecting image for '%s' ...", token_name);
14162 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14164 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14165 tile_size, tile_size, 0, 0);
14167 // force using RGBA surface for temporary bitmap (using transparent black)
14168 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14169 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14171 tmp_bitmap->surface =
14172 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14174 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14176 for (j = 0; j < anim_frames; j++)
14178 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14179 int frame_size = frame_size_final * num_steps;
14180 int offset = (tile_size - frame_size_final) / 2;
14181 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14183 while (frame_size > frame_size_final)
14187 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14189 FreeBitmap(frame_bitmap);
14191 frame_bitmap = half_bitmap;
14194 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14195 frame_size_final, frame_size_final,
14196 dst_x + j * tile_size + offset, dst_y + offset);
14198 FreeBitmap(frame_bitmap);
14201 tmp_bitmap->surface_masked = NULL;
14203 FreeBitmap(tmp_bitmap);
14205 pos_collect_images++;
14208 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14209 Fail("cannot save element collecting image file '%s'", filename_bmp);
14211 FreeBitmap(dst_bitmap);
14213 Info("Converting image file from BMP to PNG ...");
14215 if (system(cmd_convert) != 0)
14216 Fail("converting image file failed");
14218 unlink(filename_bmp);
14222 CloseAllAndExit(0);
14226 // ----------------------------------------------------------------------------
14227 // create and save images for custom and group elements (raw BMP format)
14228 // ----------------------------------------------------------------------------
14230 void CreateCustomElementImages(char *directory)
14232 char *src_basename = "RocksCE-template.ilbm";
14233 char *dst_basename = "RocksCE.bmp";
14234 char *src_filename = getPath2(directory, src_basename);
14235 char *dst_filename = getPath2(directory, dst_basename);
14236 Bitmap *src_bitmap;
14238 int yoffset_ce = 0;
14239 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14242 InitVideoDefaults();
14244 ReCreateBitmap(&backbuffer, video.width, video.height);
14246 src_bitmap = LoadImage(src_filename);
14248 bitmap = CreateBitmap(TILEX * 16 * 2,
14249 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14252 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14259 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14260 TILEX * x, TILEY * y + yoffset_ce);
14262 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14264 TILEX * x + TILEX * 16,
14265 TILEY * y + yoffset_ce);
14267 for (j = 2; j >= 0; j--)
14271 BlitBitmap(src_bitmap, bitmap,
14272 TILEX + c * 7, 0, 6, 10,
14273 TILEX * x + 6 + j * 7,
14274 TILEY * y + 11 + yoffset_ce);
14276 BlitBitmap(src_bitmap, bitmap,
14277 TILEX + c * 8, TILEY, 6, 10,
14278 TILEX * 16 + TILEX * x + 6 + j * 8,
14279 TILEY * y + 10 + yoffset_ce);
14285 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14292 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14293 TILEX * x, TILEY * y + yoffset_ge);
14295 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14297 TILEX * x + TILEX * 16,
14298 TILEY * y + yoffset_ge);
14300 for (j = 1; j >= 0; j--)
14304 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14305 TILEX * x + 6 + j * 10,
14306 TILEY * y + 11 + yoffset_ge);
14308 BlitBitmap(src_bitmap, bitmap,
14309 TILEX + c * 8, TILEY + 12, 6, 10,
14310 TILEX * 16 + TILEX * x + 10 + j * 8,
14311 TILEY * y + 10 + yoffset_ge);
14317 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14318 Fail("cannot save CE graphics file '%s'", dst_filename);
14320 FreeBitmap(bitmap);
14322 CloseAllAndExit(0);