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;
3771 cave->intermission = level->bd_intermission;
3774 cave->level_time[0] = level->time;
3775 cave->level_diamonds[0] = level->gems_needed;
3778 cave->scheduling = level->bd_scheduling_type;
3779 cave->pal_timing = level->bd_pal_timing;
3780 cave->level_speed[0] = level->bd_cycle_delay_ms;
3781 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3782 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3783 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3786 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3787 cave->diamond_value = level->score[SC_EMERALD];
3788 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3790 // compatibility settings
3791 cave->lineshift = level->bd_line_shifting_borders;
3792 cave->wraparound_objects = level->bd_wraparound_objects;
3793 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3794 cave->short_explosions = level->bd_short_explosions;
3795 cave->gravity_affects_all = level->bd_gravity_affects_all;
3797 // player properties
3798 cave->diagonal_movements = level->bd_diagonal_movements;
3799 cave->active_is_first_found = level->bd_topmost_player_active;
3800 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3801 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3802 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3804 // element properties
3805 cave->level_magic_wall_time[0] = level->time_magic_wall;
3808 strncpy(cave->name, level->name, sizeof(GdString));
3809 cave->name[sizeof(GdString) - 1] = '\0';
3811 // playfield elements
3812 for (x = 0; x < cave->w; x++)
3813 for (y = 0; y < cave->h; y++)
3814 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3817 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3819 struct LevelInfo_BD *level_bd = level->native_bd_level;
3820 GdCave *cave = level_bd->cave;
3821 int bd_level_nr = level_bd->level_nr;
3824 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3825 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3828 level->bd_intermission = cave->intermission;
3831 level->time = cave->level_time[bd_level_nr];
3832 level->gems_needed = cave->level_diamonds[bd_level_nr];
3835 level->bd_scheduling_type = cave->scheduling;
3836 level->bd_pal_timing = cave->pal_timing;
3837 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3838 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3839 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3840 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3843 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3844 level->score[SC_EMERALD] = cave->diamond_value;
3845 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3847 // compatibility settings
3848 level->bd_line_shifting_borders = cave->lineshift;
3849 level->bd_wraparound_objects = cave->wraparound_objects;
3850 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
3851 level->bd_short_explosions = cave->short_explosions;
3852 level->bd_gravity_affects_all = cave->gravity_affects_all;
3854 // player properties
3855 level->bd_diagonal_movements = cave->diagonal_movements;
3856 level->bd_topmost_player_active = cave->active_is_first_found;
3857 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
3858 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
3859 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
3861 // element properties
3862 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3865 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3867 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3868 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3870 // playfield elements
3871 for (x = 0; x < level->fieldx; x++)
3872 for (y = 0; y < level->fieldy; y++)
3873 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3875 checked_free(cave_name);
3878 static void setTapeInfoToDefaults(void);
3880 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3882 struct LevelInfo_BD *level_bd = level->native_bd_level;
3883 GdCave *cave = level_bd->cave;
3884 GdReplay *replay = level_bd->replay;
3890 // always start with reliable default values
3891 setTapeInfoToDefaults();
3893 tape.level_nr = level_nr; // (currently not used)
3894 tape.random_seed = replay->seed;
3896 TapeSetDateFromIsoDateString(replay->date);
3899 tape.pos[tape.counter].delay = 0;
3901 tape.bd_replay = TRUE;
3903 // all time calculations only used to display approximate tape time
3904 int cave_speed = cave->speed;
3905 int milliseconds_game = 0;
3906 int milliseconds_elapsed = 20;
3908 for (i = 0; i < replay->movements->len; i++)
3910 int replay_action = replay->movements->data[i];
3911 int tape_action = map_action_BD_to_RND(replay_action);
3912 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3913 boolean success = 0;
3917 success = TapeAddAction(action);
3919 milliseconds_game += milliseconds_elapsed;
3921 if (milliseconds_game >= cave_speed)
3923 milliseconds_game -= cave_speed;
3930 tape.pos[tape.counter].delay = 0;
3931 tape.pos[tape.counter].action[0] = 0;
3935 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3941 TapeHaltRecording();
3945 // ----------------------------------------------------------------------------
3946 // functions for loading EM level
3947 // ----------------------------------------------------------------------------
3949 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3951 static int ball_xy[8][2] =
3962 struct LevelInfo_EM *level_em = level->native_em_level;
3963 struct CAVE *cav = level_em->cav;
3966 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3967 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3969 cav->time_seconds = level->time;
3970 cav->gems_needed = level->gems_needed;
3972 cav->emerald_score = level->score[SC_EMERALD];
3973 cav->diamond_score = level->score[SC_DIAMOND];
3974 cav->alien_score = level->score[SC_ROBOT];
3975 cav->tank_score = level->score[SC_SPACESHIP];
3976 cav->bug_score = level->score[SC_BUG];
3977 cav->eater_score = level->score[SC_YAMYAM];
3978 cav->nut_score = level->score[SC_NUT];
3979 cav->dynamite_score = level->score[SC_DYNAMITE];
3980 cav->key_score = level->score[SC_KEY];
3981 cav->exit_score = level->score[SC_TIME_BONUS];
3983 cav->num_eater_arrays = level->num_yamyam_contents;
3985 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3986 for (y = 0; y < 3; y++)
3987 for (x = 0; x < 3; x++)
3988 cav->eater_array[i][y * 3 + x] =
3989 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3991 cav->amoeba_time = level->amoeba_speed;
3992 cav->wonderwall_time = level->time_magic_wall;
3993 cav->wheel_time = level->time_wheel;
3995 cav->android_move_time = level->android_move_time;
3996 cav->android_clone_time = level->android_clone_time;
3997 cav->ball_random = level->ball_random;
3998 cav->ball_active = level->ball_active_initial;
3999 cav->ball_time = level->ball_time;
4000 cav->num_ball_arrays = level->num_ball_contents;
4002 cav->lenses_score = level->lenses_score;
4003 cav->magnify_score = level->magnify_score;
4004 cav->slurp_score = level->slurp_score;
4006 cav->lenses_time = level->lenses_time;
4007 cav->magnify_time = level->magnify_time;
4009 cav->wind_time = 9999;
4010 cav->wind_direction =
4011 map_direction_RND_to_EM(level->wind_direction_initial);
4013 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4014 for (j = 0; j < 8; j++)
4015 cav->ball_array[i][j] =
4016 map_element_RND_to_EM_cave(level->ball_content[i].
4017 e[ball_xy[j][0]][ball_xy[j][1]]);
4019 map_android_clone_elements_RND_to_EM(level);
4021 // first fill the complete playfield with the empty space element
4022 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4023 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4024 cav->cave[x][y] = Cblank;
4026 // then copy the real level contents from level file into the playfield
4027 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4029 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4031 if (level->field[x][y] == EL_AMOEBA_DEAD)
4032 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4034 cav->cave[x][y] = new_element;
4037 for (i = 0; i < MAX_PLAYERS; i++)
4039 cav->player_x[i] = -1;
4040 cav->player_y[i] = -1;
4043 // initialize player positions and delete players from the playfield
4044 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4046 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4048 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4050 cav->player_x[player_nr] = x;
4051 cav->player_y[player_nr] = y;
4053 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4058 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4060 static int ball_xy[8][2] =
4071 struct LevelInfo_EM *level_em = level->native_em_level;
4072 struct CAVE *cav = level_em->cav;
4075 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4076 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4078 level->time = cav->time_seconds;
4079 level->gems_needed = cav->gems_needed;
4081 sprintf(level->name, "Level %d", level->file_info.nr);
4083 level->score[SC_EMERALD] = cav->emerald_score;
4084 level->score[SC_DIAMOND] = cav->diamond_score;
4085 level->score[SC_ROBOT] = cav->alien_score;
4086 level->score[SC_SPACESHIP] = cav->tank_score;
4087 level->score[SC_BUG] = cav->bug_score;
4088 level->score[SC_YAMYAM] = cav->eater_score;
4089 level->score[SC_NUT] = cav->nut_score;
4090 level->score[SC_DYNAMITE] = cav->dynamite_score;
4091 level->score[SC_KEY] = cav->key_score;
4092 level->score[SC_TIME_BONUS] = cav->exit_score;
4094 level->num_yamyam_contents = cav->num_eater_arrays;
4096 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4097 for (y = 0; y < 3; y++)
4098 for (x = 0; x < 3; x++)
4099 level->yamyam_content[i].e[x][y] =
4100 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4102 level->amoeba_speed = cav->amoeba_time;
4103 level->time_magic_wall = cav->wonderwall_time;
4104 level->time_wheel = cav->wheel_time;
4106 level->android_move_time = cav->android_move_time;
4107 level->android_clone_time = cav->android_clone_time;
4108 level->ball_random = cav->ball_random;
4109 level->ball_active_initial = cav->ball_active;
4110 level->ball_time = cav->ball_time;
4111 level->num_ball_contents = cav->num_ball_arrays;
4113 level->lenses_score = cav->lenses_score;
4114 level->magnify_score = cav->magnify_score;
4115 level->slurp_score = cav->slurp_score;
4117 level->lenses_time = cav->lenses_time;
4118 level->magnify_time = cav->magnify_time;
4120 level->wind_direction_initial =
4121 map_direction_EM_to_RND(cav->wind_direction);
4123 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4124 for (j = 0; j < 8; j++)
4125 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4126 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4128 map_android_clone_elements_EM_to_RND(level);
4130 // convert the playfield (some elements need special treatment)
4131 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4133 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4135 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4136 new_element = EL_AMOEBA_DEAD;
4138 level->field[x][y] = new_element;
4141 for (i = 0; i < MAX_PLAYERS; i++)
4143 // in case of all players set to the same field, use the first player
4144 int nr = MAX_PLAYERS - i - 1;
4145 int jx = cav->player_x[nr];
4146 int jy = cav->player_y[nr];
4148 if (jx != -1 && jy != -1)
4149 level->field[jx][jy] = EL_PLAYER_1 + nr;
4152 // time score is counted for each 10 seconds left in Emerald Mine levels
4153 level->time_score_base = 10;
4157 // ----------------------------------------------------------------------------
4158 // functions for loading SP level
4159 // ----------------------------------------------------------------------------
4161 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4163 struct LevelInfo_SP *level_sp = level->native_sp_level;
4164 LevelInfoType *header = &level_sp->header;
4167 level_sp->width = level->fieldx;
4168 level_sp->height = level->fieldy;
4170 for (x = 0; x < level->fieldx; x++)
4171 for (y = 0; y < level->fieldy; y++)
4172 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4174 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4176 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4177 header->LevelTitle[i] = level->name[i];
4178 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4180 header->InfotronsNeeded = level->gems_needed;
4182 header->SpecialPortCount = 0;
4184 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4186 boolean gravity_port_found = FALSE;
4187 boolean gravity_port_valid = FALSE;
4188 int gravity_port_flag;
4189 int gravity_port_base_element;
4190 int element = level->field[x][y];
4192 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4193 element <= EL_SP_GRAVITY_ON_PORT_UP)
4195 gravity_port_found = TRUE;
4196 gravity_port_valid = TRUE;
4197 gravity_port_flag = 1;
4198 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4200 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4201 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4203 gravity_port_found = TRUE;
4204 gravity_port_valid = TRUE;
4205 gravity_port_flag = 0;
4206 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4208 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4209 element <= EL_SP_GRAVITY_PORT_UP)
4211 // change R'n'D style gravity inverting special port to normal port
4212 // (there are no gravity inverting ports in native Supaplex engine)
4214 gravity_port_found = TRUE;
4215 gravity_port_valid = FALSE;
4216 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4219 if (gravity_port_found)
4221 if (gravity_port_valid &&
4222 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4224 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4226 port->PortLocation = (y * level->fieldx + x) * 2;
4227 port->Gravity = gravity_port_flag;
4229 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4231 header->SpecialPortCount++;
4235 // change special gravity port to normal port
4237 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4240 level_sp->playfield[x][y] = element - EL_SP_START;
4245 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4247 struct LevelInfo_SP *level_sp = level->native_sp_level;
4248 LevelInfoType *header = &level_sp->header;
4249 boolean num_invalid_elements = 0;
4252 level->fieldx = level_sp->width;
4253 level->fieldy = level_sp->height;
4255 for (x = 0; x < level->fieldx; x++)
4257 for (y = 0; y < level->fieldy; y++)
4259 int element_old = level_sp->playfield[x][y];
4260 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4262 if (element_new == EL_UNKNOWN)
4264 num_invalid_elements++;
4266 Debug("level:native:SP", "invalid element %d at position %d, %d",
4270 level->field[x][y] = element_new;
4274 if (num_invalid_elements > 0)
4275 Warn("found %d invalid elements%s", num_invalid_elements,
4276 (!options.debug ? " (use '--debug' for more details)" : ""));
4278 for (i = 0; i < MAX_PLAYERS; i++)
4279 level->initial_player_gravity[i] =
4280 (header->InitialGravity == 1 ? TRUE : FALSE);
4282 // skip leading spaces
4283 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4284 if (header->LevelTitle[i] != ' ')
4288 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4289 level->name[j] = header->LevelTitle[i];
4290 level->name[j] = '\0';
4292 // cut trailing spaces
4294 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4295 level->name[j - 1] = '\0';
4297 level->gems_needed = header->InfotronsNeeded;
4299 for (i = 0; i < header->SpecialPortCount; i++)
4301 SpecialPortType *port = &header->SpecialPort[i];
4302 int port_location = port->PortLocation;
4303 int gravity = port->Gravity;
4304 int port_x, port_y, port_element;
4306 port_x = (port_location / 2) % level->fieldx;
4307 port_y = (port_location / 2) / level->fieldx;
4309 if (port_x < 0 || port_x >= level->fieldx ||
4310 port_y < 0 || port_y >= level->fieldy)
4312 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4317 port_element = level->field[port_x][port_y];
4319 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4320 port_element > EL_SP_GRAVITY_PORT_UP)
4322 Warn("no special port at position (%d, %d)", port_x, port_y);
4327 // change previous (wrong) gravity inverting special port to either
4328 // gravity enabling special port or gravity disabling special port
4329 level->field[port_x][port_y] +=
4330 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4331 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4334 // change special gravity ports without database entries to normal ports
4335 for (x = 0; x < level->fieldx; x++)
4336 for (y = 0; y < level->fieldy; y++)
4337 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4338 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4339 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4341 level->time = 0; // no time limit
4342 level->amoeba_speed = 0;
4343 level->time_magic_wall = 0;
4344 level->time_wheel = 0;
4345 level->amoeba_content = EL_EMPTY;
4347 // original Supaplex does not use score values -- rate by playing time
4348 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4349 level->score[i] = 0;
4351 level->rate_time_over_score = TRUE;
4353 // there are no yamyams in supaplex levels
4354 for (i = 0; i < level->num_yamyam_contents; i++)
4355 for (x = 0; x < 3; x++)
4356 for (y = 0; y < 3; y++)
4357 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4360 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4362 struct LevelInfo_SP *level_sp = level->native_sp_level;
4363 struct DemoInfo_SP *demo = &level_sp->demo;
4366 // always start with reliable default values
4367 demo->is_available = FALSE;
4370 if (TAPE_IS_EMPTY(tape))
4373 demo->level_nr = tape.level_nr; // (currently not used)
4375 level_sp->header.DemoRandomSeed = tape.random_seed;
4379 for (i = 0; i < tape.length; i++)
4381 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4382 int demo_repeat = tape.pos[i].delay;
4383 int demo_entries = (demo_repeat + 15) / 16;
4385 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4387 Warn("tape truncated: size exceeds maximum SP demo size %d",
4393 for (j = 0; j < demo_repeat / 16; j++)
4394 demo->data[demo->length++] = 0xf0 | demo_action;
4396 if (demo_repeat % 16)
4397 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4400 demo->is_available = TRUE;
4403 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4405 struct LevelInfo_SP *level_sp = level->native_sp_level;
4406 struct DemoInfo_SP *demo = &level_sp->demo;
4407 char *filename = level->file_info.filename;
4410 // always start with reliable default values
4411 setTapeInfoToDefaults();
4413 if (!demo->is_available)
4416 tape.level_nr = demo->level_nr; // (currently not used)
4417 tape.random_seed = level_sp->header.DemoRandomSeed;
4419 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4422 tape.pos[tape.counter].delay = 0;
4424 for (i = 0; i < demo->length; i++)
4426 int demo_action = demo->data[i] & 0x0f;
4427 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4428 int tape_action = map_key_SP_to_RND(demo_action);
4429 int tape_repeat = demo_repeat + 1;
4430 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4431 boolean success = 0;
4434 for (j = 0; j < tape_repeat; j++)
4435 success = TapeAddAction(action);
4439 Warn("SP demo truncated: size exceeds maximum tape size %d",
4446 TapeHaltRecording();
4450 // ----------------------------------------------------------------------------
4451 // functions for loading MM level
4452 // ----------------------------------------------------------------------------
4454 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4456 struct LevelInfo_MM *level_mm = level->native_mm_level;
4459 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4460 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4462 level_mm->time = level->time;
4463 level_mm->kettles_needed = level->gems_needed;
4464 level_mm->auto_count_kettles = level->auto_count_gems;
4466 level_mm->mm_laser_red = level->mm_laser_red;
4467 level_mm->mm_laser_green = level->mm_laser_green;
4468 level_mm->mm_laser_blue = level->mm_laser_blue;
4470 level_mm->df_laser_red = level->df_laser_red;
4471 level_mm->df_laser_green = level->df_laser_green;
4472 level_mm->df_laser_blue = level->df_laser_blue;
4474 strcpy(level_mm->name, level->name);
4475 strcpy(level_mm->author, level->author);
4477 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4478 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4479 level_mm->score[SC_KEY] = level->score[SC_KEY];
4480 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4481 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4483 level_mm->amoeba_speed = level->amoeba_speed;
4484 level_mm->time_fuse = level->mm_time_fuse;
4485 level_mm->time_bomb = level->mm_time_bomb;
4486 level_mm->time_ball = level->mm_time_ball;
4487 level_mm->time_block = level->mm_time_block;
4489 level_mm->num_ball_contents = level->num_mm_ball_contents;
4490 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4491 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4492 level_mm->explode_ball = level->explode_mm_ball;
4494 for (i = 0; i < level->num_mm_ball_contents; i++)
4495 level_mm->ball_content[i] =
4496 map_element_RND_to_MM(level->mm_ball_content[i]);
4498 for (x = 0; x < level->fieldx; x++)
4499 for (y = 0; y < level->fieldy; y++)
4501 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4504 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4506 struct LevelInfo_MM *level_mm = level->native_mm_level;
4509 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4510 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4512 level->time = level_mm->time;
4513 level->gems_needed = level_mm->kettles_needed;
4514 level->auto_count_gems = level_mm->auto_count_kettles;
4516 level->mm_laser_red = level_mm->mm_laser_red;
4517 level->mm_laser_green = level_mm->mm_laser_green;
4518 level->mm_laser_blue = level_mm->mm_laser_blue;
4520 level->df_laser_red = level_mm->df_laser_red;
4521 level->df_laser_green = level_mm->df_laser_green;
4522 level->df_laser_blue = level_mm->df_laser_blue;
4524 strcpy(level->name, level_mm->name);
4526 // only overwrite author from 'levelinfo.conf' if author defined in level
4527 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4528 strcpy(level->author, level_mm->author);
4530 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4531 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4532 level->score[SC_KEY] = level_mm->score[SC_KEY];
4533 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4534 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4536 level->amoeba_speed = level_mm->amoeba_speed;
4537 level->mm_time_fuse = level_mm->time_fuse;
4538 level->mm_time_bomb = level_mm->time_bomb;
4539 level->mm_time_ball = level_mm->time_ball;
4540 level->mm_time_block = level_mm->time_block;
4542 level->num_mm_ball_contents = level_mm->num_ball_contents;
4543 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4544 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4545 level->explode_mm_ball = level_mm->explode_ball;
4547 for (i = 0; i < level->num_mm_ball_contents; i++)
4548 level->mm_ball_content[i] =
4549 map_element_MM_to_RND(level_mm->ball_content[i]);
4551 for (x = 0; x < level->fieldx; x++)
4552 for (y = 0; y < level->fieldy; y++)
4553 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4557 // ----------------------------------------------------------------------------
4558 // functions for loading DC level
4559 // ----------------------------------------------------------------------------
4561 #define DC_LEVEL_HEADER_SIZE 344
4563 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4566 static int last_data_encoded;
4570 int diff_hi, diff_lo;
4571 int data_hi, data_lo;
4572 unsigned short data_decoded;
4576 last_data_encoded = 0;
4583 diff = data_encoded - last_data_encoded;
4584 diff_hi = diff & ~0xff;
4585 diff_lo = diff & 0xff;
4589 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4590 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4591 data_hi = data_hi & 0xff00;
4593 data_decoded = data_hi | data_lo;
4595 last_data_encoded = data_encoded;
4597 offset1 = (offset1 + 1) % 31;
4598 offset2 = offset2 & 0xff;
4600 return data_decoded;
4603 static int getMappedElement_DC(int element)
4611 // 0x0117 - 0x036e: (?)
4614 // 0x042d - 0x0684: (?)
4630 element = EL_CRYSTAL;
4633 case 0x0e77: // quicksand (boulder)
4634 element = EL_QUICKSAND_FAST_FULL;
4637 case 0x0e99: // slow quicksand (boulder)
4638 element = EL_QUICKSAND_FULL;
4642 element = EL_EM_EXIT_OPEN;
4646 element = EL_EM_EXIT_CLOSED;
4650 element = EL_EM_STEEL_EXIT_OPEN;
4654 element = EL_EM_STEEL_EXIT_CLOSED;
4657 case 0x0f4f: // dynamite (lit 1)
4658 element = EL_EM_DYNAMITE_ACTIVE;
4661 case 0x0f57: // dynamite (lit 2)
4662 element = EL_EM_DYNAMITE_ACTIVE;
4665 case 0x0f5f: // dynamite (lit 3)
4666 element = EL_EM_DYNAMITE_ACTIVE;
4669 case 0x0f67: // dynamite (lit 4)
4670 element = EL_EM_DYNAMITE_ACTIVE;
4677 element = EL_AMOEBA_WET;
4681 element = EL_AMOEBA_DROP;
4685 element = EL_DC_MAGIC_WALL;
4689 element = EL_SPACESHIP_UP;
4693 element = EL_SPACESHIP_DOWN;
4697 element = EL_SPACESHIP_LEFT;
4701 element = EL_SPACESHIP_RIGHT;
4705 element = EL_BUG_UP;
4709 element = EL_BUG_DOWN;
4713 element = EL_BUG_LEFT;
4717 element = EL_BUG_RIGHT;
4721 element = EL_MOLE_UP;
4725 element = EL_MOLE_DOWN;
4729 element = EL_MOLE_LEFT;
4733 element = EL_MOLE_RIGHT;
4741 element = EL_YAMYAM_UP;
4745 element = EL_SWITCHGATE_OPEN;
4749 element = EL_SWITCHGATE_CLOSED;
4753 element = EL_DC_SWITCHGATE_SWITCH_UP;
4757 element = EL_TIMEGATE_CLOSED;
4760 case 0x144c: // conveyor belt switch (green)
4761 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4764 case 0x144f: // conveyor belt switch (red)
4765 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4768 case 0x1452: // conveyor belt switch (blue)
4769 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4773 element = EL_CONVEYOR_BELT_3_MIDDLE;
4777 element = EL_CONVEYOR_BELT_3_LEFT;
4781 element = EL_CONVEYOR_BELT_3_RIGHT;
4785 element = EL_CONVEYOR_BELT_1_MIDDLE;
4789 element = EL_CONVEYOR_BELT_1_LEFT;
4793 element = EL_CONVEYOR_BELT_1_RIGHT;
4797 element = EL_CONVEYOR_BELT_4_MIDDLE;
4801 element = EL_CONVEYOR_BELT_4_LEFT;
4805 element = EL_CONVEYOR_BELT_4_RIGHT;
4809 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4813 element = EL_EXPANDABLE_WALL_VERTICAL;
4817 element = EL_EXPANDABLE_WALL_ANY;
4820 case 0x14ce: // growing steel wall (left/right)
4821 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4824 case 0x14df: // growing steel wall (up/down)
4825 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4828 case 0x14e8: // growing steel wall (up/down/left/right)
4829 element = EL_EXPANDABLE_STEELWALL_ANY;
4833 element = EL_SHIELD_DEADLY;
4837 element = EL_EXTRA_TIME;
4845 element = EL_EMPTY_SPACE;
4848 case 0x1578: // quicksand (empty)
4849 element = EL_QUICKSAND_FAST_EMPTY;
4852 case 0x1579: // slow quicksand (empty)
4853 element = EL_QUICKSAND_EMPTY;
4863 element = EL_EM_DYNAMITE;
4866 case 0x15a1: // key (red)
4867 element = EL_EM_KEY_1;
4870 case 0x15a2: // key (yellow)
4871 element = EL_EM_KEY_2;
4874 case 0x15a3: // key (blue)
4875 element = EL_EM_KEY_4;
4878 case 0x15a4: // key (green)
4879 element = EL_EM_KEY_3;
4882 case 0x15a5: // key (white)
4883 element = EL_DC_KEY_WHITE;
4887 element = EL_WALL_SLIPPERY;
4894 case 0x15a8: // wall (not round)
4898 case 0x15a9: // (blue)
4899 element = EL_CHAR_A;
4902 case 0x15aa: // (blue)
4903 element = EL_CHAR_B;
4906 case 0x15ab: // (blue)
4907 element = EL_CHAR_C;
4910 case 0x15ac: // (blue)
4911 element = EL_CHAR_D;
4914 case 0x15ad: // (blue)
4915 element = EL_CHAR_E;
4918 case 0x15ae: // (blue)
4919 element = EL_CHAR_F;
4922 case 0x15af: // (blue)
4923 element = EL_CHAR_G;
4926 case 0x15b0: // (blue)
4927 element = EL_CHAR_H;
4930 case 0x15b1: // (blue)
4931 element = EL_CHAR_I;
4934 case 0x15b2: // (blue)
4935 element = EL_CHAR_J;
4938 case 0x15b3: // (blue)
4939 element = EL_CHAR_K;
4942 case 0x15b4: // (blue)
4943 element = EL_CHAR_L;
4946 case 0x15b5: // (blue)
4947 element = EL_CHAR_M;
4950 case 0x15b6: // (blue)
4951 element = EL_CHAR_N;
4954 case 0x15b7: // (blue)
4955 element = EL_CHAR_O;
4958 case 0x15b8: // (blue)
4959 element = EL_CHAR_P;
4962 case 0x15b9: // (blue)
4963 element = EL_CHAR_Q;
4966 case 0x15ba: // (blue)
4967 element = EL_CHAR_R;
4970 case 0x15bb: // (blue)
4971 element = EL_CHAR_S;
4974 case 0x15bc: // (blue)
4975 element = EL_CHAR_T;
4978 case 0x15bd: // (blue)
4979 element = EL_CHAR_U;
4982 case 0x15be: // (blue)
4983 element = EL_CHAR_V;
4986 case 0x15bf: // (blue)
4987 element = EL_CHAR_W;
4990 case 0x15c0: // (blue)
4991 element = EL_CHAR_X;
4994 case 0x15c1: // (blue)
4995 element = EL_CHAR_Y;
4998 case 0x15c2: // (blue)
4999 element = EL_CHAR_Z;
5002 case 0x15c3: // (blue)
5003 element = EL_CHAR_AUMLAUT;
5006 case 0x15c4: // (blue)
5007 element = EL_CHAR_OUMLAUT;
5010 case 0x15c5: // (blue)
5011 element = EL_CHAR_UUMLAUT;
5014 case 0x15c6: // (blue)
5015 element = EL_CHAR_0;
5018 case 0x15c7: // (blue)
5019 element = EL_CHAR_1;
5022 case 0x15c8: // (blue)
5023 element = EL_CHAR_2;
5026 case 0x15c9: // (blue)
5027 element = EL_CHAR_3;
5030 case 0x15ca: // (blue)
5031 element = EL_CHAR_4;
5034 case 0x15cb: // (blue)
5035 element = EL_CHAR_5;
5038 case 0x15cc: // (blue)
5039 element = EL_CHAR_6;
5042 case 0x15cd: // (blue)
5043 element = EL_CHAR_7;
5046 case 0x15ce: // (blue)
5047 element = EL_CHAR_8;
5050 case 0x15cf: // (blue)
5051 element = EL_CHAR_9;
5054 case 0x15d0: // (blue)
5055 element = EL_CHAR_PERIOD;
5058 case 0x15d1: // (blue)
5059 element = EL_CHAR_EXCLAM;
5062 case 0x15d2: // (blue)
5063 element = EL_CHAR_COLON;
5066 case 0x15d3: // (blue)
5067 element = EL_CHAR_LESS;
5070 case 0x15d4: // (blue)
5071 element = EL_CHAR_GREATER;
5074 case 0x15d5: // (blue)
5075 element = EL_CHAR_QUESTION;
5078 case 0x15d6: // (blue)
5079 element = EL_CHAR_COPYRIGHT;
5082 case 0x15d7: // (blue)
5083 element = EL_CHAR_UP;
5086 case 0x15d8: // (blue)
5087 element = EL_CHAR_DOWN;
5090 case 0x15d9: // (blue)
5091 element = EL_CHAR_BUTTON;
5094 case 0x15da: // (blue)
5095 element = EL_CHAR_PLUS;
5098 case 0x15db: // (blue)
5099 element = EL_CHAR_MINUS;
5102 case 0x15dc: // (blue)
5103 element = EL_CHAR_APOSTROPHE;
5106 case 0x15dd: // (blue)
5107 element = EL_CHAR_PARENLEFT;
5110 case 0x15de: // (blue)
5111 element = EL_CHAR_PARENRIGHT;
5114 case 0x15df: // (green)
5115 element = EL_CHAR_A;
5118 case 0x15e0: // (green)
5119 element = EL_CHAR_B;
5122 case 0x15e1: // (green)
5123 element = EL_CHAR_C;
5126 case 0x15e2: // (green)
5127 element = EL_CHAR_D;
5130 case 0x15e3: // (green)
5131 element = EL_CHAR_E;
5134 case 0x15e4: // (green)
5135 element = EL_CHAR_F;
5138 case 0x15e5: // (green)
5139 element = EL_CHAR_G;
5142 case 0x15e6: // (green)
5143 element = EL_CHAR_H;
5146 case 0x15e7: // (green)
5147 element = EL_CHAR_I;
5150 case 0x15e8: // (green)
5151 element = EL_CHAR_J;
5154 case 0x15e9: // (green)
5155 element = EL_CHAR_K;
5158 case 0x15ea: // (green)
5159 element = EL_CHAR_L;
5162 case 0x15eb: // (green)
5163 element = EL_CHAR_M;
5166 case 0x15ec: // (green)
5167 element = EL_CHAR_N;
5170 case 0x15ed: // (green)
5171 element = EL_CHAR_O;
5174 case 0x15ee: // (green)
5175 element = EL_CHAR_P;
5178 case 0x15ef: // (green)
5179 element = EL_CHAR_Q;
5182 case 0x15f0: // (green)
5183 element = EL_CHAR_R;
5186 case 0x15f1: // (green)
5187 element = EL_CHAR_S;
5190 case 0x15f2: // (green)
5191 element = EL_CHAR_T;
5194 case 0x15f3: // (green)
5195 element = EL_CHAR_U;
5198 case 0x15f4: // (green)
5199 element = EL_CHAR_V;
5202 case 0x15f5: // (green)
5203 element = EL_CHAR_W;
5206 case 0x15f6: // (green)
5207 element = EL_CHAR_X;
5210 case 0x15f7: // (green)
5211 element = EL_CHAR_Y;
5214 case 0x15f8: // (green)
5215 element = EL_CHAR_Z;
5218 case 0x15f9: // (green)
5219 element = EL_CHAR_AUMLAUT;
5222 case 0x15fa: // (green)
5223 element = EL_CHAR_OUMLAUT;
5226 case 0x15fb: // (green)
5227 element = EL_CHAR_UUMLAUT;
5230 case 0x15fc: // (green)
5231 element = EL_CHAR_0;
5234 case 0x15fd: // (green)
5235 element = EL_CHAR_1;
5238 case 0x15fe: // (green)
5239 element = EL_CHAR_2;
5242 case 0x15ff: // (green)
5243 element = EL_CHAR_3;
5246 case 0x1600: // (green)
5247 element = EL_CHAR_4;
5250 case 0x1601: // (green)
5251 element = EL_CHAR_5;
5254 case 0x1602: // (green)
5255 element = EL_CHAR_6;
5258 case 0x1603: // (green)
5259 element = EL_CHAR_7;
5262 case 0x1604: // (green)
5263 element = EL_CHAR_8;
5266 case 0x1605: // (green)
5267 element = EL_CHAR_9;
5270 case 0x1606: // (green)
5271 element = EL_CHAR_PERIOD;
5274 case 0x1607: // (green)
5275 element = EL_CHAR_EXCLAM;
5278 case 0x1608: // (green)
5279 element = EL_CHAR_COLON;
5282 case 0x1609: // (green)
5283 element = EL_CHAR_LESS;
5286 case 0x160a: // (green)
5287 element = EL_CHAR_GREATER;
5290 case 0x160b: // (green)
5291 element = EL_CHAR_QUESTION;
5294 case 0x160c: // (green)
5295 element = EL_CHAR_COPYRIGHT;
5298 case 0x160d: // (green)
5299 element = EL_CHAR_UP;
5302 case 0x160e: // (green)
5303 element = EL_CHAR_DOWN;
5306 case 0x160f: // (green)
5307 element = EL_CHAR_BUTTON;
5310 case 0x1610: // (green)
5311 element = EL_CHAR_PLUS;
5314 case 0x1611: // (green)
5315 element = EL_CHAR_MINUS;
5318 case 0x1612: // (green)
5319 element = EL_CHAR_APOSTROPHE;
5322 case 0x1613: // (green)
5323 element = EL_CHAR_PARENLEFT;
5326 case 0x1614: // (green)
5327 element = EL_CHAR_PARENRIGHT;
5330 case 0x1615: // (blue steel)
5331 element = EL_STEEL_CHAR_A;
5334 case 0x1616: // (blue steel)
5335 element = EL_STEEL_CHAR_B;
5338 case 0x1617: // (blue steel)
5339 element = EL_STEEL_CHAR_C;
5342 case 0x1618: // (blue steel)
5343 element = EL_STEEL_CHAR_D;
5346 case 0x1619: // (blue steel)
5347 element = EL_STEEL_CHAR_E;
5350 case 0x161a: // (blue steel)
5351 element = EL_STEEL_CHAR_F;
5354 case 0x161b: // (blue steel)
5355 element = EL_STEEL_CHAR_G;
5358 case 0x161c: // (blue steel)
5359 element = EL_STEEL_CHAR_H;
5362 case 0x161d: // (blue steel)
5363 element = EL_STEEL_CHAR_I;
5366 case 0x161e: // (blue steel)
5367 element = EL_STEEL_CHAR_J;
5370 case 0x161f: // (blue steel)
5371 element = EL_STEEL_CHAR_K;
5374 case 0x1620: // (blue steel)
5375 element = EL_STEEL_CHAR_L;
5378 case 0x1621: // (blue steel)
5379 element = EL_STEEL_CHAR_M;
5382 case 0x1622: // (blue steel)
5383 element = EL_STEEL_CHAR_N;
5386 case 0x1623: // (blue steel)
5387 element = EL_STEEL_CHAR_O;
5390 case 0x1624: // (blue steel)
5391 element = EL_STEEL_CHAR_P;
5394 case 0x1625: // (blue steel)
5395 element = EL_STEEL_CHAR_Q;
5398 case 0x1626: // (blue steel)
5399 element = EL_STEEL_CHAR_R;
5402 case 0x1627: // (blue steel)
5403 element = EL_STEEL_CHAR_S;
5406 case 0x1628: // (blue steel)
5407 element = EL_STEEL_CHAR_T;
5410 case 0x1629: // (blue steel)
5411 element = EL_STEEL_CHAR_U;
5414 case 0x162a: // (blue steel)
5415 element = EL_STEEL_CHAR_V;
5418 case 0x162b: // (blue steel)
5419 element = EL_STEEL_CHAR_W;
5422 case 0x162c: // (blue steel)
5423 element = EL_STEEL_CHAR_X;
5426 case 0x162d: // (blue steel)
5427 element = EL_STEEL_CHAR_Y;
5430 case 0x162e: // (blue steel)
5431 element = EL_STEEL_CHAR_Z;
5434 case 0x162f: // (blue steel)
5435 element = EL_STEEL_CHAR_AUMLAUT;
5438 case 0x1630: // (blue steel)
5439 element = EL_STEEL_CHAR_OUMLAUT;
5442 case 0x1631: // (blue steel)
5443 element = EL_STEEL_CHAR_UUMLAUT;
5446 case 0x1632: // (blue steel)
5447 element = EL_STEEL_CHAR_0;
5450 case 0x1633: // (blue steel)
5451 element = EL_STEEL_CHAR_1;
5454 case 0x1634: // (blue steel)
5455 element = EL_STEEL_CHAR_2;
5458 case 0x1635: // (blue steel)
5459 element = EL_STEEL_CHAR_3;
5462 case 0x1636: // (blue steel)
5463 element = EL_STEEL_CHAR_4;
5466 case 0x1637: // (blue steel)
5467 element = EL_STEEL_CHAR_5;
5470 case 0x1638: // (blue steel)
5471 element = EL_STEEL_CHAR_6;
5474 case 0x1639: // (blue steel)
5475 element = EL_STEEL_CHAR_7;
5478 case 0x163a: // (blue steel)
5479 element = EL_STEEL_CHAR_8;
5482 case 0x163b: // (blue steel)
5483 element = EL_STEEL_CHAR_9;
5486 case 0x163c: // (blue steel)
5487 element = EL_STEEL_CHAR_PERIOD;
5490 case 0x163d: // (blue steel)
5491 element = EL_STEEL_CHAR_EXCLAM;
5494 case 0x163e: // (blue steel)
5495 element = EL_STEEL_CHAR_COLON;
5498 case 0x163f: // (blue steel)
5499 element = EL_STEEL_CHAR_LESS;
5502 case 0x1640: // (blue steel)
5503 element = EL_STEEL_CHAR_GREATER;
5506 case 0x1641: // (blue steel)
5507 element = EL_STEEL_CHAR_QUESTION;
5510 case 0x1642: // (blue steel)
5511 element = EL_STEEL_CHAR_COPYRIGHT;
5514 case 0x1643: // (blue steel)
5515 element = EL_STEEL_CHAR_UP;
5518 case 0x1644: // (blue steel)
5519 element = EL_STEEL_CHAR_DOWN;
5522 case 0x1645: // (blue steel)
5523 element = EL_STEEL_CHAR_BUTTON;
5526 case 0x1646: // (blue steel)
5527 element = EL_STEEL_CHAR_PLUS;
5530 case 0x1647: // (blue steel)
5531 element = EL_STEEL_CHAR_MINUS;
5534 case 0x1648: // (blue steel)
5535 element = EL_STEEL_CHAR_APOSTROPHE;
5538 case 0x1649: // (blue steel)
5539 element = EL_STEEL_CHAR_PARENLEFT;
5542 case 0x164a: // (blue steel)
5543 element = EL_STEEL_CHAR_PARENRIGHT;
5546 case 0x164b: // (green steel)
5547 element = EL_STEEL_CHAR_A;
5550 case 0x164c: // (green steel)
5551 element = EL_STEEL_CHAR_B;
5554 case 0x164d: // (green steel)
5555 element = EL_STEEL_CHAR_C;
5558 case 0x164e: // (green steel)
5559 element = EL_STEEL_CHAR_D;
5562 case 0x164f: // (green steel)
5563 element = EL_STEEL_CHAR_E;
5566 case 0x1650: // (green steel)
5567 element = EL_STEEL_CHAR_F;
5570 case 0x1651: // (green steel)
5571 element = EL_STEEL_CHAR_G;
5574 case 0x1652: // (green steel)
5575 element = EL_STEEL_CHAR_H;
5578 case 0x1653: // (green steel)
5579 element = EL_STEEL_CHAR_I;
5582 case 0x1654: // (green steel)
5583 element = EL_STEEL_CHAR_J;
5586 case 0x1655: // (green steel)
5587 element = EL_STEEL_CHAR_K;
5590 case 0x1656: // (green steel)
5591 element = EL_STEEL_CHAR_L;
5594 case 0x1657: // (green steel)
5595 element = EL_STEEL_CHAR_M;
5598 case 0x1658: // (green steel)
5599 element = EL_STEEL_CHAR_N;
5602 case 0x1659: // (green steel)
5603 element = EL_STEEL_CHAR_O;
5606 case 0x165a: // (green steel)
5607 element = EL_STEEL_CHAR_P;
5610 case 0x165b: // (green steel)
5611 element = EL_STEEL_CHAR_Q;
5614 case 0x165c: // (green steel)
5615 element = EL_STEEL_CHAR_R;
5618 case 0x165d: // (green steel)
5619 element = EL_STEEL_CHAR_S;
5622 case 0x165e: // (green steel)
5623 element = EL_STEEL_CHAR_T;
5626 case 0x165f: // (green steel)
5627 element = EL_STEEL_CHAR_U;
5630 case 0x1660: // (green steel)
5631 element = EL_STEEL_CHAR_V;
5634 case 0x1661: // (green steel)
5635 element = EL_STEEL_CHAR_W;
5638 case 0x1662: // (green steel)
5639 element = EL_STEEL_CHAR_X;
5642 case 0x1663: // (green steel)
5643 element = EL_STEEL_CHAR_Y;
5646 case 0x1664: // (green steel)
5647 element = EL_STEEL_CHAR_Z;
5650 case 0x1665: // (green steel)
5651 element = EL_STEEL_CHAR_AUMLAUT;
5654 case 0x1666: // (green steel)
5655 element = EL_STEEL_CHAR_OUMLAUT;
5658 case 0x1667: // (green steel)
5659 element = EL_STEEL_CHAR_UUMLAUT;
5662 case 0x1668: // (green steel)
5663 element = EL_STEEL_CHAR_0;
5666 case 0x1669: // (green steel)
5667 element = EL_STEEL_CHAR_1;
5670 case 0x166a: // (green steel)
5671 element = EL_STEEL_CHAR_2;
5674 case 0x166b: // (green steel)
5675 element = EL_STEEL_CHAR_3;
5678 case 0x166c: // (green steel)
5679 element = EL_STEEL_CHAR_4;
5682 case 0x166d: // (green steel)
5683 element = EL_STEEL_CHAR_5;
5686 case 0x166e: // (green steel)
5687 element = EL_STEEL_CHAR_6;
5690 case 0x166f: // (green steel)
5691 element = EL_STEEL_CHAR_7;
5694 case 0x1670: // (green steel)
5695 element = EL_STEEL_CHAR_8;
5698 case 0x1671: // (green steel)
5699 element = EL_STEEL_CHAR_9;
5702 case 0x1672: // (green steel)
5703 element = EL_STEEL_CHAR_PERIOD;
5706 case 0x1673: // (green steel)
5707 element = EL_STEEL_CHAR_EXCLAM;
5710 case 0x1674: // (green steel)
5711 element = EL_STEEL_CHAR_COLON;
5714 case 0x1675: // (green steel)
5715 element = EL_STEEL_CHAR_LESS;
5718 case 0x1676: // (green steel)
5719 element = EL_STEEL_CHAR_GREATER;
5722 case 0x1677: // (green steel)
5723 element = EL_STEEL_CHAR_QUESTION;
5726 case 0x1678: // (green steel)
5727 element = EL_STEEL_CHAR_COPYRIGHT;
5730 case 0x1679: // (green steel)
5731 element = EL_STEEL_CHAR_UP;
5734 case 0x167a: // (green steel)
5735 element = EL_STEEL_CHAR_DOWN;
5738 case 0x167b: // (green steel)
5739 element = EL_STEEL_CHAR_BUTTON;
5742 case 0x167c: // (green steel)
5743 element = EL_STEEL_CHAR_PLUS;
5746 case 0x167d: // (green steel)
5747 element = EL_STEEL_CHAR_MINUS;
5750 case 0x167e: // (green steel)
5751 element = EL_STEEL_CHAR_APOSTROPHE;
5754 case 0x167f: // (green steel)
5755 element = EL_STEEL_CHAR_PARENLEFT;
5758 case 0x1680: // (green steel)
5759 element = EL_STEEL_CHAR_PARENRIGHT;
5762 case 0x1681: // gate (red)
5763 element = EL_EM_GATE_1;
5766 case 0x1682: // secret gate (red)
5767 element = EL_EM_GATE_1_GRAY;
5770 case 0x1683: // gate (yellow)
5771 element = EL_EM_GATE_2;
5774 case 0x1684: // secret gate (yellow)
5775 element = EL_EM_GATE_2_GRAY;
5778 case 0x1685: // gate (blue)
5779 element = EL_EM_GATE_4;
5782 case 0x1686: // secret gate (blue)
5783 element = EL_EM_GATE_4_GRAY;
5786 case 0x1687: // gate (green)
5787 element = EL_EM_GATE_3;
5790 case 0x1688: // secret gate (green)
5791 element = EL_EM_GATE_3_GRAY;
5794 case 0x1689: // gate (white)
5795 element = EL_DC_GATE_WHITE;
5798 case 0x168a: // secret gate (white)
5799 element = EL_DC_GATE_WHITE_GRAY;
5802 case 0x168b: // secret gate (no key)
5803 element = EL_DC_GATE_FAKE_GRAY;
5807 element = EL_ROBOT_WHEEL;
5811 element = EL_DC_TIMEGATE_SWITCH;
5815 element = EL_ACID_POOL_BOTTOM;
5819 element = EL_ACID_POOL_TOPLEFT;
5823 element = EL_ACID_POOL_TOPRIGHT;
5827 element = EL_ACID_POOL_BOTTOMLEFT;
5831 element = EL_ACID_POOL_BOTTOMRIGHT;
5835 element = EL_STEELWALL;
5839 element = EL_STEELWALL_SLIPPERY;
5842 case 0x1695: // steel wall (not round)
5843 element = EL_STEELWALL;
5846 case 0x1696: // steel wall (left)
5847 element = EL_DC_STEELWALL_1_LEFT;
5850 case 0x1697: // steel wall (bottom)
5851 element = EL_DC_STEELWALL_1_BOTTOM;
5854 case 0x1698: // steel wall (right)
5855 element = EL_DC_STEELWALL_1_RIGHT;
5858 case 0x1699: // steel wall (top)
5859 element = EL_DC_STEELWALL_1_TOP;
5862 case 0x169a: // steel wall (left/bottom)
5863 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5866 case 0x169b: // steel wall (right/bottom)
5867 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5870 case 0x169c: // steel wall (right/top)
5871 element = EL_DC_STEELWALL_1_TOPRIGHT;
5874 case 0x169d: // steel wall (left/top)
5875 element = EL_DC_STEELWALL_1_TOPLEFT;
5878 case 0x169e: // steel wall (right/bottom small)
5879 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5882 case 0x169f: // steel wall (left/bottom small)
5883 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5886 case 0x16a0: // steel wall (right/top small)
5887 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5890 case 0x16a1: // steel wall (left/top small)
5891 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5894 case 0x16a2: // steel wall (left/right)
5895 element = EL_DC_STEELWALL_1_VERTICAL;
5898 case 0x16a3: // steel wall (top/bottom)
5899 element = EL_DC_STEELWALL_1_HORIZONTAL;
5902 case 0x16a4: // steel wall 2 (left end)
5903 element = EL_DC_STEELWALL_2_LEFT;
5906 case 0x16a5: // steel wall 2 (right end)
5907 element = EL_DC_STEELWALL_2_RIGHT;
5910 case 0x16a6: // steel wall 2 (top end)
5911 element = EL_DC_STEELWALL_2_TOP;
5914 case 0x16a7: // steel wall 2 (bottom end)
5915 element = EL_DC_STEELWALL_2_BOTTOM;
5918 case 0x16a8: // steel wall 2 (left/right)
5919 element = EL_DC_STEELWALL_2_HORIZONTAL;
5922 case 0x16a9: // steel wall 2 (up/down)
5923 element = EL_DC_STEELWALL_2_VERTICAL;
5926 case 0x16aa: // steel wall 2 (mid)
5927 element = EL_DC_STEELWALL_2_MIDDLE;
5931 element = EL_SIGN_EXCLAMATION;
5935 element = EL_SIGN_RADIOACTIVITY;
5939 element = EL_SIGN_STOP;
5943 element = EL_SIGN_WHEELCHAIR;
5947 element = EL_SIGN_PARKING;
5951 element = EL_SIGN_NO_ENTRY;
5955 element = EL_SIGN_HEART;
5959 element = EL_SIGN_GIVE_WAY;
5963 element = EL_SIGN_ENTRY_FORBIDDEN;
5967 element = EL_SIGN_EMERGENCY_EXIT;
5971 element = EL_SIGN_YIN_YANG;
5975 element = EL_WALL_EMERALD;
5979 element = EL_WALL_DIAMOND;
5983 element = EL_WALL_PEARL;
5987 element = EL_WALL_CRYSTAL;
5991 element = EL_INVISIBLE_WALL;
5995 element = EL_INVISIBLE_STEELWALL;
5999 // EL_INVISIBLE_SAND
6002 element = EL_LIGHT_SWITCH;
6006 element = EL_ENVELOPE_1;
6010 if (element >= 0x0117 && element <= 0x036e) // (?)
6011 element = EL_DIAMOND;
6012 else if (element >= 0x042d && element <= 0x0684) // (?)
6013 element = EL_EMERALD;
6014 else if (element >= 0x157c && element <= 0x158b)
6016 else if (element >= 0x1590 && element <= 0x159f)
6017 element = EL_DC_LANDMINE;
6018 else if (element >= 0x16bc && element <= 0x16cb)
6019 element = EL_INVISIBLE_SAND;
6022 Warn("unknown Diamond Caves element 0x%04x", element);
6024 element = EL_UNKNOWN;
6029 return getMappedElement(element);
6032 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6034 byte header[DC_LEVEL_HEADER_SIZE];
6036 int envelope_header_pos = 62;
6037 int envelope_content_pos = 94;
6038 int level_name_pos = 251;
6039 int level_author_pos = 292;
6040 int envelope_header_len;
6041 int envelope_content_len;
6043 int level_author_len;
6045 int num_yamyam_contents;
6048 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6050 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6052 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6054 header[i * 2 + 0] = header_word >> 8;
6055 header[i * 2 + 1] = header_word & 0xff;
6058 // read some values from level header to check level decoding integrity
6059 fieldx = header[6] | (header[7] << 8);
6060 fieldy = header[8] | (header[9] << 8);
6061 num_yamyam_contents = header[60] | (header[61] << 8);
6063 // do some simple sanity checks to ensure that level was correctly decoded
6064 if (fieldx < 1 || fieldx > 256 ||
6065 fieldy < 1 || fieldy > 256 ||
6066 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6068 level->no_valid_file = TRUE;
6070 Warn("cannot decode level from stream -- using empty level");
6075 // maximum envelope header size is 31 bytes
6076 envelope_header_len = header[envelope_header_pos];
6077 // maximum envelope content size is 110 (156?) bytes
6078 envelope_content_len = header[envelope_content_pos];
6080 // maximum level title size is 40 bytes
6081 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6082 // maximum level author size is 30 (51?) bytes
6083 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6087 for (i = 0; i < envelope_header_len; i++)
6088 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6089 level->envelope[0].text[envelope_size++] =
6090 header[envelope_header_pos + 1 + i];
6092 if (envelope_header_len > 0 && envelope_content_len > 0)
6094 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6095 level->envelope[0].text[envelope_size++] = '\n';
6096 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6097 level->envelope[0].text[envelope_size++] = '\n';
6100 for (i = 0; i < envelope_content_len; i++)
6101 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6102 level->envelope[0].text[envelope_size++] =
6103 header[envelope_content_pos + 1 + i];
6105 level->envelope[0].text[envelope_size] = '\0';
6107 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6108 level->envelope[0].ysize = 10;
6109 level->envelope[0].autowrap = TRUE;
6110 level->envelope[0].centered = TRUE;
6112 for (i = 0; i < level_name_len; i++)
6113 level->name[i] = header[level_name_pos + 1 + i];
6114 level->name[level_name_len] = '\0';
6116 for (i = 0; i < level_author_len; i++)
6117 level->author[i] = header[level_author_pos + 1 + i];
6118 level->author[level_author_len] = '\0';
6120 num_yamyam_contents = header[60] | (header[61] << 8);
6121 level->num_yamyam_contents =
6122 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6124 for (i = 0; i < num_yamyam_contents; i++)
6126 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6128 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6129 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6131 if (i < MAX_ELEMENT_CONTENTS)
6132 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6136 fieldx = header[6] | (header[7] << 8);
6137 fieldy = header[8] | (header[9] << 8);
6138 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6139 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6141 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6143 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6144 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6146 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6147 level->field[x][y] = getMappedElement_DC(element_dc);
6150 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6151 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6152 level->field[x][y] = EL_PLAYER_1;
6154 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6155 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6156 level->field[x][y] = EL_PLAYER_2;
6158 level->gems_needed = header[18] | (header[19] << 8);
6160 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6161 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6162 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6163 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6164 level->score[SC_NUT] = header[28] | (header[29] << 8);
6165 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6166 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6167 level->score[SC_BUG] = header[34] | (header[35] << 8);
6168 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6169 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6170 level->score[SC_KEY] = header[40] | (header[41] << 8);
6171 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6173 level->time = header[44] | (header[45] << 8);
6175 level->amoeba_speed = header[46] | (header[47] << 8);
6176 level->time_light = header[48] | (header[49] << 8);
6177 level->time_timegate = header[50] | (header[51] << 8);
6178 level->time_wheel = header[52] | (header[53] << 8);
6179 level->time_magic_wall = header[54] | (header[55] << 8);
6180 level->extra_time = header[56] | (header[57] << 8);
6181 level->shield_normal_time = header[58] | (header[59] << 8);
6183 // shield and extra time elements do not have a score
6184 level->score[SC_SHIELD] = 0;
6185 level->extra_time_score = 0;
6187 // set time for normal and deadly shields to the same value
6188 level->shield_deadly_time = level->shield_normal_time;
6190 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6191 // can slip down from flat walls, like normal walls and steel walls
6192 level->em_slippery_gems = TRUE;
6194 // time score is counted for each 10 seconds left in Diamond Caves levels
6195 level->time_score_base = 10;
6198 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6199 struct LevelFileInfo *level_file_info,
6200 boolean level_info_only)
6202 char *filename = level_file_info->filename;
6204 int num_magic_bytes = 8;
6205 char magic_bytes[num_magic_bytes + 1];
6206 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6208 if (!(file = openFile(filename, MODE_READ)))
6210 level->no_valid_file = TRUE;
6212 if (!level_info_only)
6213 Warn("cannot read level '%s' -- using empty level", filename);
6218 // fseek(file, 0x0000, SEEK_SET);
6220 if (level_file_info->packed)
6222 // read "magic bytes" from start of file
6223 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6224 magic_bytes[0] = '\0';
6226 // check "magic bytes" for correct file format
6227 if (!strPrefix(magic_bytes, "DC2"))
6229 level->no_valid_file = TRUE;
6231 Warn("unknown DC level file '%s' -- using empty level", filename);
6236 if (strPrefix(magic_bytes, "DC2Win95") ||
6237 strPrefix(magic_bytes, "DC2Win98"))
6239 int position_first_level = 0x00fa;
6240 int extra_bytes = 4;
6243 // advance file stream to first level inside the level package
6244 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6246 // each block of level data is followed by block of non-level data
6247 num_levels_to_skip *= 2;
6249 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6250 while (num_levels_to_skip >= 0)
6252 // advance file stream to next level inside the level package
6253 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6255 level->no_valid_file = TRUE;
6257 Warn("cannot fseek in file '%s' -- using empty level", filename);
6262 // skip apparently unused extra bytes following each level
6263 ReadUnusedBytesFromFile(file, extra_bytes);
6265 // read size of next level in level package
6266 skip_bytes = getFile32BitLE(file);
6268 num_levels_to_skip--;
6273 level->no_valid_file = TRUE;
6275 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6281 LoadLevelFromFileStream_DC(file, level);
6287 // ----------------------------------------------------------------------------
6288 // functions for loading SB level
6289 // ----------------------------------------------------------------------------
6291 int getMappedElement_SB(int element_ascii, boolean use_ces)
6299 sb_element_mapping[] =
6301 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6302 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6303 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6304 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6305 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6306 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6307 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6308 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6315 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6316 if (element_ascii == sb_element_mapping[i].ascii)
6317 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6319 return EL_UNDEFINED;
6322 static void SetLevelSettings_SB(struct LevelInfo *level)
6326 level->use_step_counter = TRUE;
6329 level->score[SC_TIME_BONUS] = 0;
6330 level->time_score_base = 1;
6331 level->rate_time_over_score = TRUE;
6334 level->auto_exit_sokoban = TRUE;
6337 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6338 struct LevelFileInfo *level_file_info,
6339 boolean level_info_only)
6341 char *filename = level_file_info->filename;
6342 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6343 char last_comment[MAX_LINE_LEN];
6344 char level_name[MAX_LINE_LEN];
6347 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6348 boolean read_continued_line = FALSE;
6349 boolean reading_playfield = FALSE;
6350 boolean got_valid_playfield_line = FALSE;
6351 boolean invalid_playfield_char = FALSE;
6352 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6353 int file_level_nr = 0;
6354 int x = 0, y = 0; // initialized to make compilers happy
6356 last_comment[0] = '\0';
6357 level_name[0] = '\0';
6359 if (!(file = openFile(filename, MODE_READ)))
6361 level->no_valid_file = TRUE;
6363 if (!level_info_only)
6364 Warn("cannot read level '%s' -- using empty level", filename);
6369 while (!checkEndOfFile(file))
6371 // level successfully read, but next level may follow here
6372 if (!got_valid_playfield_line && reading_playfield)
6374 // read playfield from single level file -- skip remaining file
6375 if (!level_file_info->packed)
6378 if (file_level_nr >= num_levels_to_skip)
6383 last_comment[0] = '\0';
6384 level_name[0] = '\0';
6386 reading_playfield = FALSE;
6389 got_valid_playfield_line = FALSE;
6391 // read next line of input file
6392 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6395 // cut trailing line break (this can be newline and/or carriage return)
6396 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6397 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6400 // copy raw input line for later use (mainly debugging output)
6401 strcpy(line_raw, line);
6403 if (read_continued_line)
6405 // append new line to existing line, if there is enough space
6406 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6407 strcat(previous_line, line_ptr);
6409 strcpy(line, previous_line); // copy storage buffer to line
6411 read_continued_line = FALSE;
6414 // if the last character is '\', continue at next line
6415 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6417 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6418 strcpy(previous_line, line); // copy line to storage buffer
6420 read_continued_line = TRUE;
6426 if (line[0] == '\0')
6429 // extract comment text from comment line
6432 for (line_ptr = line; *line_ptr; line_ptr++)
6433 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6436 strcpy(last_comment, line_ptr);
6441 // extract level title text from line containing level title
6442 if (line[0] == '\'')
6444 strcpy(level_name, &line[1]);
6446 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6447 level_name[strlen(level_name) - 1] = '\0';
6452 // skip lines containing only spaces (or empty lines)
6453 for (line_ptr = line; *line_ptr; line_ptr++)
6454 if (*line_ptr != ' ')
6456 if (*line_ptr == '\0')
6459 // at this point, we have found a line containing part of a playfield
6461 got_valid_playfield_line = TRUE;
6463 if (!reading_playfield)
6465 reading_playfield = TRUE;
6466 invalid_playfield_char = FALSE;
6468 for (x = 0; x < MAX_LEV_FIELDX; x++)
6469 for (y = 0; y < MAX_LEV_FIELDY; y++)
6470 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6475 // start with topmost tile row
6479 // skip playfield line if larger row than allowed
6480 if (y >= MAX_LEV_FIELDY)
6483 // start with leftmost tile column
6486 // read playfield elements from line
6487 for (line_ptr = line; *line_ptr; line_ptr++)
6489 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6491 // stop parsing playfield line if larger column than allowed
6492 if (x >= MAX_LEV_FIELDX)
6495 if (mapped_sb_element == EL_UNDEFINED)
6497 invalid_playfield_char = TRUE;
6502 level->field[x][y] = mapped_sb_element;
6504 // continue with next tile column
6507 level->fieldx = MAX(x, level->fieldx);
6510 if (invalid_playfield_char)
6512 // if first playfield line, treat invalid lines as comment lines
6514 reading_playfield = FALSE;
6519 // continue with next tile row
6527 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6528 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6530 if (!reading_playfield)
6532 level->no_valid_file = TRUE;
6534 Warn("cannot read level '%s' -- using empty level", filename);
6539 if (*level_name != '\0')
6541 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6542 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6544 else if (*last_comment != '\0')
6546 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6547 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6551 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6554 // set all empty fields beyond the border walls to invisible steel wall
6555 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6557 if ((x == 0 || x == level->fieldx - 1 ||
6558 y == 0 || y == level->fieldy - 1) &&
6559 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6560 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6561 level->field, level->fieldx, level->fieldy);
6564 // set special level settings for Sokoban levels
6565 SetLevelSettings_SB(level);
6567 if (load_xsb_to_ces)
6569 // special global settings can now be set in level template
6570 level->use_custom_template = TRUE;
6575 // -------------------------------------------------------------------------
6576 // functions for handling native levels
6577 // -------------------------------------------------------------------------
6579 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6580 struct LevelFileInfo *level_file_info,
6581 boolean level_info_only)
6585 // determine position of requested level inside level package
6586 if (level_file_info->packed)
6587 pos = level_file_info->nr - leveldir_current->first_level;
6589 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6590 level->no_valid_file = TRUE;
6593 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6594 struct LevelFileInfo *level_file_info,
6595 boolean level_info_only)
6597 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6598 level->no_valid_file = TRUE;
6601 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6602 struct LevelFileInfo *level_file_info,
6603 boolean level_info_only)
6607 // determine position of requested level inside level package
6608 if (level_file_info->packed)
6609 pos = level_file_info->nr - leveldir_current->first_level;
6611 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6612 level->no_valid_file = TRUE;
6615 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6616 struct LevelFileInfo *level_file_info,
6617 boolean level_info_only)
6619 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6620 level->no_valid_file = TRUE;
6623 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6625 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6626 CopyNativeLevel_RND_to_BD(level);
6627 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6628 CopyNativeLevel_RND_to_EM(level);
6629 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6630 CopyNativeLevel_RND_to_SP(level);
6631 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6632 CopyNativeLevel_RND_to_MM(level);
6635 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6637 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6638 CopyNativeLevel_BD_to_RND(level);
6639 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6640 CopyNativeLevel_EM_to_RND(level);
6641 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6642 CopyNativeLevel_SP_to_RND(level);
6643 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6644 CopyNativeLevel_MM_to_RND(level);
6647 void SaveNativeLevel(struct LevelInfo *level)
6649 // saving native level files only supported for some game engines
6650 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6651 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6654 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6655 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6656 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6657 char *filename = getLevelFilenameFromBasename(basename);
6659 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6662 boolean success = FALSE;
6664 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6666 CopyNativeLevel_RND_to_BD(level);
6667 // CopyNativeTape_RND_to_BD(level);
6669 success = SaveNativeLevel_BD(filename);
6671 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6673 CopyNativeLevel_RND_to_SP(level);
6674 CopyNativeTape_RND_to_SP(level);
6676 success = SaveNativeLevel_SP(filename);
6680 Request("Native level file saved!", REQ_CONFIRM);
6682 Request("Failed to save native level file!", REQ_CONFIRM);
6686 // ----------------------------------------------------------------------------
6687 // functions for loading generic level
6688 // ----------------------------------------------------------------------------
6690 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6691 struct LevelFileInfo *level_file_info,
6692 boolean level_info_only)
6694 // always start with reliable default values
6695 setLevelInfoToDefaults(level, level_info_only, TRUE);
6697 switch (level_file_info->type)
6699 case LEVEL_FILE_TYPE_RND:
6700 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6703 case LEVEL_FILE_TYPE_BD:
6704 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6705 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6708 case LEVEL_FILE_TYPE_EM:
6709 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6710 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6713 case LEVEL_FILE_TYPE_SP:
6714 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6715 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6718 case LEVEL_FILE_TYPE_MM:
6719 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6720 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6723 case LEVEL_FILE_TYPE_DC:
6724 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6727 case LEVEL_FILE_TYPE_SB:
6728 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6732 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6736 // if level file is invalid, restore level structure to default values
6737 if (level->no_valid_file)
6738 setLevelInfoToDefaults(level, level_info_only, FALSE);
6740 if (check_special_flags("use_native_bd_game_engine"))
6741 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6743 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6744 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6746 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6747 CopyNativeLevel_Native_to_RND(level);
6750 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6752 static struct LevelFileInfo level_file_info;
6754 // always start with reliable default values
6755 setFileInfoToDefaults(&level_file_info);
6757 level_file_info.nr = 0; // unknown level number
6758 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6760 setString(&level_file_info.filename, filename);
6762 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6765 static void LoadLevel_InitVersion(struct LevelInfo *level)
6769 if (leveldir_current == NULL) // only when dumping level
6772 // all engine modifications also valid for levels which use latest engine
6773 if (level->game_version < VERSION_IDENT(3,2,0,5))
6775 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6776 level->time_score_base = 10;
6779 if (leveldir_current->latest_engine)
6781 // ---------- use latest game engine --------------------------------------
6783 /* For all levels which are forced to use the latest game engine version
6784 (normally all but user contributed, private and undefined levels), set
6785 the game engine version to the actual version; this allows for actual
6786 corrections in the game engine to take effect for existing, converted
6787 levels (from "classic" or other existing games) to make the emulation
6788 of the corresponding game more accurate, while (hopefully) not breaking
6789 existing levels created from other players. */
6791 level->game_version = GAME_VERSION_ACTUAL;
6793 /* Set special EM style gems behaviour: EM style gems slip down from
6794 normal, steel and growing wall. As this is a more fundamental change,
6795 it seems better to set the default behaviour to "off" (as it is more
6796 natural) and make it configurable in the level editor (as a property
6797 of gem style elements). Already existing converted levels (neither
6798 private nor contributed levels) are changed to the new behaviour. */
6800 if (level->file_version < FILE_VERSION_2_0)
6801 level->em_slippery_gems = TRUE;
6806 // ---------- use game engine the level was created with --------------------
6808 /* For all levels which are not forced to use the latest game engine
6809 version (normally user contributed, private and undefined levels),
6810 use the version of the game engine the levels were created for.
6812 Since 2.0.1, the game engine version is now directly stored
6813 in the level file (chunk "VERS"), so there is no need anymore
6814 to set the game version from the file version (except for old,
6815 pre-2.0 levels, where the game version is still taken from the
6816 file format version used to store the level -- see above). */
6818 // player was faster than enemies in 1.0.0 and before
6819 if (level->file_version == FILE_VERSION_1_0)
6820 for (i = 0; i < MAX_PLAYERS; i++)
6821 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6823 // default behaviour for EM style gems was "slippery" only in 2.0.1
6824 if (level->game_version == VERSION_IDENT(2,0,1,0))
6825 level->em_slippery_gems = TRUE;
6827 // springs could be pushed over pits before (pre-release version) 2.2.0
6828 if (level->game_version < VERSION_IDENT(2,2,0,0))
6829 level->use_spring_bug = TRUE;
6831 if (level->game_version < VERSION_IDENT(3,2,0,5))
6833 // time orb caused limited time in endless time levels before 3.2.0-5
6834 level->use_time_orb_bug = TRUE;
6836 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6837 level->block_snap_field = FALSE;
6839 // extra time score was same value as time left score before 3.2.0-5
6840 level->extra_time_score = level->score[SC_TIME_BONUS];
6843 if (level->game_version < VERSION_IDENT(3,2,0,7))
6845 // default behaviour for snapping was "not continuous" before 3.2.0-7
6846 level->continuous_snapping = FALSE;
6849 // only few elements were able to actively move into acid before 3.1.0
6850 // trigger settings did not exist before 3.1.0; set to default "any"
6851 if (level->game_version < VERSION_IDENT(3,1,0,0))
6853 // correct "can move into acid" settings (all zero in old levels)
6855 level->can_move_into_acid_bits = 0; // nothing can move into acid
6856 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6858 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6859 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6860 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6861 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6863 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6864 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6866 // correct trigger settings (stored as zero == "none" in old levels)
6868 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6870 int element = EL_CUSTOM_START + i;
6871 struct ElementInfo *ei = &element_info[element];
6873 for (j = 0; j < ei->num_change_pages; j++)
6875 struct ElementChangeInfo *change = &ei->change_page[j];
6877 change->trigger_player = CH_PLAYER_ANY;
6878 change->trigger_page = CH_PAGE_ANY;
6883 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6885 int element = EL_CUSTOM_256;
6886 struct ElementInfo *ei = &element_info[element];
6887 struct ElementChangeInfo *change = &ei->change_page[0];
6889 /* This is needed to fix a problem that was caused by a bugfix in function
6890 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6891 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6892 not replace walkable elements, but instead just placed the player on it,
6893 without placing the Sokoban field under the player). Unfortunately, this
6894 breaks "Snake Bite" style levels when the snake is halfway through a door
6895 that just closes (the snake head is still alive and can be moved in this
6896 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6897 player (without Sokoban element) which then gets killed as designed). */
6899 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6900 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6901 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6902 change->target_element = EL_PLAYER_1;
6905 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6906 if (level->game_version < VERSION_IDENT(3,2,5,0))
6908 /* This is needed to fix a problem that was caused by a bugfix in function
6909 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6910 corrects the behaviour when a custom element changes to another custom
6911 element with a higher element number that has change actions defined.
6912 Normally, only one change per frame is allowed for custom elements.
6913 Therefore, it is checked if a custom element already changed in the
6914 current frame; if it did, subsequent changes are suppressed.
6915 Unfortunately, this is only checked for element changes, but not for
6916 change actions, which are still executed. As the function above loops
6917 through all custom elements from lower to higher, an element change
6918 resulting in a lower CE number won't be checked again, while a target
6919 element with a higher number will also be checked, and potential change
6920 actions will get executed for this CE, too (which is wrong), while
6921 further changes are ignored (which is correct). As this bugfix breaks
6922 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6923 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6924 behaviour for existing levels and tapes that make use of this bug */
6926 level->use_action_after_change_bug = TRUE;
6929 // not centering level after relocating player was default only in 3.2.3
6930 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6931 level->shifted_relocation = TRUE;
6933 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6934 if (level->game_version < VERSION_IDENT(3,2,6,0))
6935 level->em_explodes_by_fire = TRUE;
6937 // levels were solved by the first player entering an exit up to 4.1.0.0
6938 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6939 level->solved_by_one_player = TRUE;
6941 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6942 if (level->game_version < VERSION_IDENT(4,1,1,1))
6943 level->use_life_bugs = TRUE;
6945 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6946 if (level->game_version < VERSION_IDENT(4,1,1,1))
6947 level->sb_objects_needed = FALSE;
6949 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6950 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6951 level->finish_dig_collect = FALSE;
6953 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6954 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6955 level->keep_walkable_ce = TRUE;
6958 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6960 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6963 // check if this level is (not) a Sokoban level
6964 for (y = 0; y < level->fieldy; y++)
6965 for (x = 0; x < level->fieldx; x++)
6966 if (!IS_SB_ELEMENT(Tile[x][y]))
6967 is_sokoban_level = FALSE;
6969 if (is_sokoban_level)
6971 // set special level settings for Sokoban levels
6972 SetLevelSettings_SB(level);
6976 static void LoadLevel_InitSettings(struct LevelInfo *level)
6978 // adjust level settings for (non-native) Sokoban-style levels
6979 LoadLevel_InitSettings_SB(level);
6981 // rename levels with title "nameless level" or if renaming is forced
6982 if (leveldir_current->empty_level_name != NULL &&
6983 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6984 leveldir_current->force_level_name))
6985 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6986 leveldir_current->empty_level_name, level_nr);
6989 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6993 // map elements that have changed in newer versions
6994 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6995 level->game_version);
6996 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6997 for (x = 0; x < 3; x++)
6998 for (y = 0; y < 3; y++)
6999 level->yamyam_content[i].e[x][y] =
7000 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7001 level->game_version);
7005 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7009 // map custom element change events that have changed in newer versions
7010 // (these following values were accidentally changed in version 3.0.1)
7011 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7012 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7014 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7016 int element = EL_CUSTOM_START + i;
7018 // order of checking and copying events to be mapped is important
7019 // (do not change the start and end value -- they are constant)
7020 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7022 if (HAS_CHANGE_EVENT(element, j - 2))
7024 SET_CHANGE_EVENT(element, j - 2, FALSE);
7025 SET_CHANGE_EVENT(element, j, TRUE);
7029 // order of checking and copying events to be mapped is important
7030 // (do not change the start and end value -- they are constant)
7031 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7033 if (HAS_CHANGE_EVENT(element, j - 1))
7035 SET_CHANGE_EVENT(element, j - 1, FALSE);
7036 SET_CHANGE_EVENT(element, j, TRUE);
7042 // initialize "can_change" field for old levels with only one change page
7043 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7045 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7047 int element = EL_CUSTOM_START + i;
7049 if (CAN_CHANGE(element))
7050 element_info[element].change->can_change = TRUE;
7054 // correct custom element values (for old levels without these options)
7055 if (level->game_version < VERSION_IDENT(3,1,1,0))
7057 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7059 int element = EL_CUSTOM_START + i;
7060 struct ElementInfo *ei = &element_info[element];
7062 if (ei->access_direction == MV_NO_DIRECTION)
7063 ei->access_direction = MV_ALL_DIRECTIONS;
7067 // correct custom element values (fix invalid values for all versions)
7070 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7072 int element = EL_CUSTOM_START + i;
7073 struct ElementInfo *ei = &element_info[element];
7075 for (j = 0; j < ei->num_change_pages; j++)
7077 struct ElementChangeInfo *change = &ei->change_page[j];
7079 if (change->trigger_player == CH_PLAYER_NONE)
7080 change->trigger_player = CH_PLAYER_ANY;
7082 if (change->trigger_side == CH_SIDE_NONE)
7083 change->trigger_side = CH_SIDE_ANY;
7088 // initialize "can_explode" field for old levels which did not store this
7089 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7090 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7092 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7094 int element = EL_CUSTOM_START + i;
7096 if (EXPLODES_1X1_OLD(element))
7097 element_info[element].explosion_type = EXPLODES_1X1;
7099 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7100 EXPLODES_SMASHED(element) ||
7101 EXPLODES_IMPACT(element)));
7105 // correct previously hard-coded move delay values for maze runner style
7106 if (level->game_version < VERSION_IDENT(3,1,1,0))
7108 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7110 int element = EL_CUSTOM_START + i;
7112 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7114 // previously hard-coded and therefore ignored
7115 element_info[element].move_delay_fixed = 9;
7116 element_info[element].move_delay_random = 0;
7121 // set some other uninitialized values of custom elements in older levels
7122 if (level->game_version < VERSION_IDENT(3,1,0,0))
7124 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7126 int element = EL_CUSTOM_START + i;
7128 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7130 element_info[element].explosion_delay = 17;
7131 element_info[element].ignition_delay = 8;
7135 // set mouse click change events to work for left/middle/right mouse button
7136 if (level->game_version < VERSION_IDENT(4,2,3,0))
7138 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7140 int element = EL_CUSTOM_START + i;
7141 struct ElementInfo *ei = &element_info[element];
7143 for (j = 0; j < ei->num_change_pages; j++)
7145 struct ElementChangeInfo *change = &ei->change_page[j];
7147 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7148 change->has_event[CE_PRESSED_BY_MOUSE] ||
7149 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7150 change->has_event[CE_MOUSE_PRESSED_ON_X])
7151 change->trigger_side = CH_SIDE_ANY;
7157 static void LoadLevel_InitElements(struct LevelInfo *level)
7159 LoadLevel_InitStandardElements(level);
7161 if (level->file_has_custom_elements)
7162 LoadLevel_InitCustomElements(level);
7164 // initialize element properties for level editor etc.
7165 InitElementPropertiesEngine(level->game_version);
7166 InitElementPropertiesGfxElement();
7169 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7173 // map elements that have changed in newer versions
7174 for (y = 0; y < level->fieldy; y++)
7175 for (x = 0; x < level->fieldx; x++)
7176 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7177 level->game_version);
7179 // clear unused playfield data (nicer if level gets resized in editor)
7180 for (x = 0; x < MAX_LEV_FIELDX; x++)
7181 for (y = 0; y < MAX_LEV_FIELDY; y++)
7182 if (x >= level->fieldx || y >= level->fieldy)
7183 level->field[x][y] = EL_EMPTY;
7185 // copy elements to runtime playfield array
7186 for (x = 0; x < MAX_LEV_FIELDX; x++)
7187 for (y = 0; y < MAX_LEV_FIELDY; y++)
7188 Tile[x][y] = level->field[x][y];
7190 // initialize level size variables for faster access
7191 lev_fieldx = level->fieldx;
7192 lev_fieldy = level->fieldy;
7194 // determine border element for this level
7195 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7196 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7201 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7203 struct LevelFileInfo *level_file_info = &level->file_info;
7205 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7206 CopyNativeLevel_RND_to_Native(level);
7209 static void LoadLevelTemplate_LoadAndInit(void)
7211 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7213 LoadLevel_InitVersion(&level_template);
7214 LoadLevel_InitElements(&level_template);
7215 LoadLevel_InitSettings(&level_template);
7217 ActivateLevelTemplate();
7220 void LoadLevelTemplate(int nr)
7222 if (!fileExists(getGlobalLevelTemplateFilename()))
7224 Warn("no level template found for this level");
7229 setLevelFileInfo(&level_template.file_info, nr);
7231 LoadLevelTemplate_LoadAndInit();
7234 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7236 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7238 LoadLevelTemplate_LoadAndInit();
7241 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7243 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7245 if (level.use_custom_template)
7247 if (network_level != NULL)
7248 LoadNetworkLevelTemplate(network_level);
7250 LoadLevelTemplate(-1);
7253 LoadLevel_InitVersion(&level);
7254 LoadLevel_InitElements(&level);
7255 LoadLevel_InitPlayfield(&level);
7256 LoadLevel_InitSettings(&level);
7258 LoadLevel_InitNativeEngines(&level);
7261 void LoadLevel(int nr)
7263 SetLevelSetInfo(leveldir_current->identifier, nr);
7265 setLevelFileInfo(&level.file_info, nr);
7267 LoadLevel_LoadAndInit(NULL);
7270 void LoadLevelInfoOnly(int nr)
7272 setLevelFileInfo(&level.file_info, nr);
7274 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7277 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7279 SetLevelSetInfo(network_level->leveldir_identifier,
7280 network_level->file_info.nr);
7282 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7284 LoadLevel_LoadAndInit(network_level);
7287 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7291 chunk_size += putFileVersion(file, level->file_version);
7292 chunk_size += putFileVersion(file, level->game_version);
7297 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7301 chunk_size += putFile16BitBE(file, level->creation_date.year);
7302 chunk_size += putFile8Bit(file, level->creation_date.month);
7303 chunk_size += putFile8Bit(file, level->creation_date.day);
7308 #if ENABLE_HISTORIC_CHUNKS
7309 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7313 putFile8Bit(file, level->fieldx);
7314 putFile8Bit(file, level->fieldy);
7316 putFile16BitBE(file, level->time);
7317 putFile16BitBE(file, level->gems_needed);
7319 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7320 putFile8Bit(file, level->name[i]);
7322 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7323 putFile8Bit(file, level->score[i]);
7325 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7326 for (y = 0; y < 3; y++)
7327 for (x = 0; x < 3; x++)
7328 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7329 level->yamyam_content[i].e[x][y]));
7330 putFile8Bit(file, level->amoeba_speed);
7331 putFile8Bit(file, level->time_magic_wall);
7332 putFile8Bit(file, level->time_wheel);
7333 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7334 level->amoeba_content));
7335 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7336 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7337 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7338 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7340 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7342 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7343 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7344 putFile32BitBE(file, level->can_move_into_acid_bits);
7345 putFile8Bit(file, level->dont_collide_with_bits);
7347 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7348 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7350 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7351 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7352 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7354 putFile8Bit(file, level->game_engine_type);
7356 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7360 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7365 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7366 chunk_size += putFile8Bit(file, level->name[i]);
7371 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7376 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7377 chunk_size += putFile8Bit(file, level->author[i]);
7382 #if ENABLE_HISTORIC_CHUNKS
7383 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7388 for (y = 0; y < level->fieldy; y++)
7389 for (x = 0; x < level->fieldx; x++)
7390 if (level->encoding_16bit_field)
7391 chunk_size += putFile16BitBE(file, level->field[x][y]);
7393 chunk_size += putFile8Bit(file, level->field[x][y]);
7399 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7404 for (y = 0; y < level->fieldy; y++)
7405 for (x = 0; x < level->fieldx; x++)
7406 chunk_size += putFile16BitBE(file, level->field[x][y]);
7411 #if ENABLE_HISTORIC_CHUNKS
7412 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7416 putFile8Bit(file, EL_YAMYAM);
7417 putFile8Bit(file, level->num_yamyam_contents);
7418 putFile8Bit(file, 0);
7419 putFile8Bit(file, 0);
7421 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7422 for (y = 0; y < 3; y++)
7423 for (x = 0; x < 3; x++)
7424 if (level->encoding_16bit_field)
7425 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7427 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7431 #if ENABLE_HISTORIC_CHUNKS
7432 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7435 int num_contents, content_xsize, content_ysize;
7436 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7438 if (element == EL_YAMYAM)
7440 num_contents = level->num_yamyam_contents;
7444 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7445 for (y = 0; y < 3; y++)
7446 for (x = 0; x < 3; x++)
7447 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7449 else if (element == EL_BD_AMOEBA)
7455 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7456 for (y = 0; y < 3; y++)
7457 for (x = 0; x < 3; x++)
7458 content_array[i][x][y] = EL_EMPTY;
7459 content_array[0][0][0] = level->amoeba_content;
7463 // chunk header already written -- write empty chunk data
7464 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7466 Warn("cannot save content for element '%d'", element);
7471 putFile16BitBE(file, element);
7472 putFile8Bit(file, num_contents);
7473 putFile8Bit(file, content_xsize);
7474 putFile8Bit(file, content_ysize);
7476 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7478 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7479 for (y = 0; y < 3; y++)
7480 for (x = 0; x < 3; x++)
7481 putFile16BitBE(file, content_array[i][x][y]);
7485 #if ENABLE_HISTORIC_CHUNKS
7486 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7488 int envelope_nr = element - EL_ENVELOPE_1;
7489 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7493 chunk_size += putFile16BitBE(file, element);
7494 chunk_size += putFile16BitBE(file, envelope_len);
7495 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7496 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7498 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7499 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7501 for (i = 0; i < envelope_len; i++)
7502 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7508 #if ENABLE_HISTORIC_CHUNKS
7509 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7510 int num_changed_custom_elements)
7514 putFile16BitBE(file, num_changed_custom_elements);
7516 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7518 int element = EL_CUSTOM_START + i;
7520 struct ElementInfo *ei = &element_info[element];
7522 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7524 if (check < num_changed_custom_elements)
7526 putFile16BitBE(file, element);
7527 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7534 if (check != num_changed_custom_elements) // should not happen
7535 Warn("inconsistent number of custom element properties");
7539 #if ENABLE_HISTORIC_CHUNKS
7540 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7541 int num_changed_custom_elements)
7545 putFile16BitBE(file, num_changed_custom_elements);
7547 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7549 int element = EL_CUSTOM_START + i;
7551 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7553 if (check < num_changed_custom_elements)
7555 putFile16BitBE(file, element);
7556 putFile16BitBE(file, element_info[element].change->target_element);
7563 if (check != num_changed_custom_elements) // should not happen
7564 Warn("inconsistent number of custom target elements");
7568 #if ENABLE_HISTORIC_CHUNKS
7569 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7570 int num_changed_custom_elements)
7572 int i, j, x, y, check = 0;
7574 putFile16BitBE(file, num_changed_custom_elements);
7576 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7578 int element = EL_CUSTOM_START + i;
7579 struct ElementInfo *ei = &element_info[element];
7581 if (ei->modified_settings)
7583 if (check < num_changed_custom_elements)
7585 putFile16BitBE(file, element);
7587 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7588 putFile8Bit(file, ei->description[j]);
7590 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7592 // some free bytes for future properties and padding
7593 WriteUnusedBytesToFile(file, 7);
7595 putFile8Bit(file, ei->use_gfx_element);
7596 putFile16BitBE(file, ei->gfx_element_initial);
7598 putFile8Bit(file, ei->collect_score_initial);
7599 putFile8Bit(file, ei->collect_count_initial);
7601 putFile16BitBE(file, ei->push_delay_fixed);
7602 putFile16BitBE(file, ei->push_delay_random);
7603 putFile16BitBE(file, ei->move_delay_fixed);
7604 putFile16BitBE(file, ei->move_delay_random);
7606 putFile16BitBE(file, ei->move_pattern);
7607 putFile8Bit(file, ei->move_direction_initial);
7608 putFile8Bit(file, ei->move_stepsize);
7610 for (y = 0; y < 3; y++)
7611 for (x = 0; x < 3; x++)
7612 putFile16BitBE(file, ei->content.e[x][y]);
7614 putFile32BitBE(file, ei->change->events);
7616 putFile16BitBE(file, ei->change->target_element);
7618 putFile16BitBE(file, ei->change->delay_fixed);
7619 putFile16BitBE(file, ei->change->delay_random);
7620 putFile16BitBE(file, ei->change->delay_frames);
7622 putFile16BitBE(file, ei->change->initial_trigger_element);
7624 putFile8Bit(file, ei->change->explode);
7625 putFile8Bit(file, ei->change->use_target_content);
7626 putFile8Bit(file, ei->change->only_if_complete);
7627 putFile8Bit(file, ei->change->use_random_replace);
7629 putFile8Bit(file, ei->change->random_percentage);
7630 putFile8Bit(file, ei->change->replace_when);
7632 for (y = 0; y < 3; y++)
7633 for (x = 0; x < 3; x++)
7634 putFile16BitBE(file, ei->change->content.e[x][y]);
7636 putFile8Bit(file, ei->slippery_type);
7638 // some free bytes for future properties and padding
7639 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7646 if (check != num_changed_custom_elements) // should not happen
7647 Warn("inconsistent number of custom element properties");
7651 #if ENABLE_HISTORIC_CHUNKS
7652 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7654 struct ElementInfo *ei = &element_info[element];
7657 // ---------- custom element base property values (96 bytes) ----------------
7659 putFile16BitBE(file, element);
7661 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7662 putFile8Bit(file, ei->description[i]);
7664 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7666 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7668 putFile8Bit(file, ei->num_change_pages);
7670 putFile16BitBE(file, ei->ce_value_fixed_initial);
7671 putFile16BitBE(file, ei->ce_value_random_initial);
7672 putFile8Bit(file, ei->use_last_ce_value);
7674 putFile8Bit(file, ei->use_gfx_element);
7675 putFile16BitBE(file, ei->gfx_element_initial);
7677 putFile8Bit(file, ei->collect_score_initial);
7678 putFile8Bit(file, ei->collect_count_initial);
7680 putFile8Bit(file, ei->drop_delay_fixed);
7681 putFile8Bit(file, ei->push_delay_fixed);
7682 putFile8Bit(file, ei->drop_delay_random);
7683 putFile8Bit(file, ei->push_delay_random);
7684 putFile16BitBE(file, ei->move_delay_fixed);
7685 putFile16BitBE(file, ei->move_delay_random);
7687 // bits 0 - 15 of "move_pattern" ...
7688 putFile16BitBE(file, ei->move_pattern & 0xffff);
7689 putFile8Bit(file, ei->move_direction_initial);
7690 putFile8Bit(file, ei->move_stepsize);
7692 putFile8Bit(file, ei->slippery_type);
7694 for (y = 0; y < 3; y++)
7695 for (x = 0; x < 3; x++)
7696 putFile16BitBE(file, ei->content.e[x][y]);
7698 putFile16BitBE(file, ei->move_enter_element);
7699 putFile16BitBE(file, ei->move_leave_element);
7700 putFile8Bit(file, ei->move_leave_type);
7702 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7703 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7705 putFile8Bit(file, ei->access_direction);
7707 putFile8Bit(file, ei->explosion_delay);
7708 putFile8Bit(file, ei->ignition_delay);
7709 putFile8Bit(file, ei->explosion_type);
7711 // some free bytes for future custom property values and padding
7712 WriteUnusedBytesToFile(file, 1);
7714 // ---------- change page property values (48 bytes) ------------------------
7716 for (i = 0; i < ei->num_change_pages; i++)
7718 struct ElementChangeInfo *change = &ei->change_page[i];
7719 unsigned int event_bits;
7721 // bits 0 - 31 of "has_event[]" ...
7723 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7724 if (change->has_event[j])
7725 event_bits |= (1u << j);
7726 putFile32BitBE(file, event_bits);
7728 putFile16BitBE(file, change->target_element);
7730 putFile16BitBE(file, change->delay_fixed);
7731 putFile16BitBE(file, change->delay_random);
7732 putFile16BitBE(file, change->delay_frames);
7734 putFile16BitBE(file, change->initial_trigger_element);
7736 putFile8Bit(file, change->explode);
7737 putFile8Bit(file, change->use_target_content);
7738 putFile8Bit(file, change->only_if_complete);
7739 putFile8Bit(file, change->use_random_replace);
7741 putFile8Bit(file, change->random_percentage);
7742 putFile8Bit(file, change->replace_when);
7744 for (y = 0; y < 3; y++)
7745 for (x = 0; x < 3; x++)
7746 putFile16BitBE(file, change->target_content.e[x][y]);
7748 putFile8Bit(file, change->can_change);
7750 putFile8Bit(file, change->trigger_side);
7752 putFile8Bit(file, change->trigger_player);
7753 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7754 log_2(change->trigger_page)));
7756 putFile8Bit(file, change->has_action);
7757 putFile8Bit(file, change->action_type);
7758 putFile8Bit(file, change->action_mode);
7759 putFile16BitBE(file, change->action_arg);
7761 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7763 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7764 if (change->has_event[j])
7765 event_bits |= (1u << (j - 32));
7766 putFile8Bit(file, event_bits);
7771 #if ENABLE_HISTORIC_CHUNKS
7772 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7774 struct ElementInfo *ei = &element_info[element];
7775 struct ElementGroupInfo *group = ei->group;
7778 putFile16BitBE(file, element);
7780 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7781 putFile8Bit(file, ei->description[i]);
7783 putFile8Bit(file, group->num_elements);
7785 putFile8Bit(file, ei->use_gfx_element);
7786 putFile16BitBE(file, ei->gfx_element_initial);
7788 putFile8Bit(file, group->choice_mode);
7790 // some free bytes for future values and padding
7791 WriteUnusedBytesToFile(file, 3);
7793 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7794 putFile16BitBE(file, group->element[i]);
7798 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7799 boolean write_element)
7801 int save_type = entry->save_type;
7802 int data_type = entry->data_type;
7803 int conf_type = entry->conf_type;
7804 int byte_mask = conf_type & CONF_MASK_BYTES;
7805 int element = entry->element;
7806 int default_value = entry->default_value;
7808 boolean modified = FALSE;
7810 if (byte_mask != CONF_MASK_MULTI_BYTES)
7812 void *value_ptr = entry->value;
7813 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7816 // check if any settings have been modified before saving them
7817 if (value != default_value)
7820 // do not save if explicitly told or if unmodified default settings
7821 if ((save_type == SAVE_CONF_NEVER) ||
7822 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7826 num_bytes += putFile16BitBE(file, element);
7828 num_bytes += putFile8Bit(file, conf_type);
7829 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7830 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7831 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7834 else if (data_type == TYPE_STRING)
7836 char *default_string = entry->default_string;
7837 char *string = (char *)(entry->value);
7838 int string_length = strlen(string);
7841 // check if any settings have been modified before saving them
7842 if (!strEqual(string, default_string))
7845 // do not save if explicitly told or if unmodified default settings
7846 if ((save_type == SAVE_CONF_NEVER) ||
7847 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7851 num_bytes += putFile16BitBE(file, element);
7853 num_bytes += putFile8Bit(file, conf_type);
7854 num_bytes += putFile16BitBE(file, string_length);
7856 for (i = 0; i < string_length; i++)
7857 num_bytes += putFile8Bit(file, string[i]);
7859 else if (data_type == TYPE_ELEMENT_LIST)
7861 int *element_array = (int *)(entry->value);
7862 int num_elements = *(int *)(entry->num_entities);
7865 // check if any settings have been modified before saving them
7866 for (i = 0; i < num_elements; i++)
7867 if (element_array[i] != default_value)
7870 // do not save if explicitly told or if unmodified default settings
7871 if ((save_type == SAVE_CONF_NEVER) ||
7872 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7876 num_bytes += putFile16BitBE(file, element);
7878 num_bytes += putFile8Bit(file, conf_type);
7879 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7881 for (i = 0; i < num_elements; i++)
7882 num_bytes += putFile16BitBE(file, element_array[i]);
7884 else if (data_type == TYPE_CONTENT_LIST)
7886 struct Content *content = (struct Content *)(entry->value);
7887 int num_contents = *(int *)(entry->num_entities);
7890 // check if any settings have been modified before saving them
7891 for (i = 0; i < num_contents; i++)
7892 for (y = 0; y < 3; y++)
7893 for (x = 0; x < 3; x++)
7894 if (content[i].e[x][y] != default_value)
7897 // do not save if explicitly told or if unmodified default settings
7898 if ((save_type == SAVE_CONF_NEVER) ||
7899 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7903 num_bytes += putFile16BitBE(file, element);
7905 num_bytes += putFile8Bit(file, conf_type);
7906 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7908 for (i = 0; i < num_contents; i++)
7909 for (y = 0; y < 3; y++)
7910 for (x = 0; x < 3; x++)
7911 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7917 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7922 li = *level; // copy level data into temporary buffer
7924 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7925 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7930 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7935 li = *level; // copy level data into temporary buffer
7937 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7938 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7943 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7945 int envelope_nr = element - EL_ENVELOPE_1;
7949 chunk_size += putFile16BitBE(file, element);
7951 // copy envelope data into temporary buffer
7952 xx_envelope = level->envelope[envelope_nr];
7954 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7955 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7960 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7962 struct ElementInfo *ei = &element_info[element];
7966 chunk_size += putFile16BitBE(file, element);
7968 xx_ei = *ei; // copy element data into temporary buffer
7970 // set default description string for this specific element
7971 strcpy(xx_default_description, getDefaultElementDescription(ei));
7973 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7974 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7976 for (i = 0; i < ei->num_change_pages; i++)
7978 struct ElementChangeInfo *change = &ei->change_page[i];
7980 xx_current_change_page = i;
7982 xx_change = *change; // copy change data into temporary buffer
7985 setEventBitsFromEventFlags(change);
7987 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7988 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7995 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7997 struct ElementInfo *ei = &element_info[element];
7998 struct ElementGroupInfo *group = ei->group;
8002 chunk_size += putFile16BitBE(file, element);
8004 xx_ei = *ei; // copy element data into temporary buffer
8005 xx_group = *group; // copy group data into temporary buffer
8007 // set default description string for this specific element
8008 strcpy(xx_default_description, getDefaultElementDescription(ei));
8010 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8011 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8016 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8018 struct ElementInfo *ei = &element_info[element];
8022 chunk_size += putFile16BitBE(file, element);
8024 xx_ei = *ei; // copy element data into temporary buffer
8026 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8027 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8032 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8033 boolean save_as_template)
8039 if (!(file = fopen(filename, MODE_WRITE)))
8041 Warn("cannot save level file '%s'", filename);
8046 level->file_version = FILE_VERSION_ACTUAL;
8047 level->game_version = GAME_VERSION_ACTUAL;
8049 level->creation_date = getCurrentDate();
8051 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8052 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8054 chunk_size = SaveLevel_VERS(NULL, level);
8055 putFileChunkBE(file, "VERS", chunk_size);
8056 SaveLevel_VERS(file, level);
8058 chunk_size = SaveLevel_DATE(NULL, level);
8059 putFileChunkBE(file, "DATE", chunk_size);
8060 SaveLevel_DATE(file, level);
8062 chunk_size = SaveLevel_NAME(NULL, level);
8063 putFileChunkBE(file, "NAME", chunk_size);
8064 SaveLevel_NAME(file, level);
8066 chunk_size = SaveLevel_AUTH(NULL, level);
8067 putFileChunkBE(file, "AUTH", chunk_size);
8068 SaveLevel_AUTH(file, level);
8070 chunk_size = SaveLevel_INFO(NULL, level);
8071 putFileChunkBE(file, "INFO", chunk_size);
8072 SaveLevel_INFO(file, level);
8074 chunk_size = SaveLevel_BODY(NULL, level);
8075 putFileChunkBE(file, "BODY", chunk_size);
8076 SaveLevel_BODY(file, level);
8078 chunk_size = SaveLevel_ELEM(NULL, level);
8079 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8081 putFileChunkBE(file, "ELEM", chunk_size);
8082 SaveLevel_ELEM(file, level);
8085 for (i = 0; i < NUM_ENVELOPES; i++)
8087 int element = EL_ENVELOPE_1 + i;
8089 chunk_size = SaveLevel_NOTE(NULL, level, element);
8090 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8092 putFileChunkBE(file, "NOTE", chunk_size);
8093 SaveLevel_NOTE(file, level, element);
8097 // if not using template level, check for non-default custom/group elements
8098 if (!level->use_custom_template || save_as_template)
8100 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8102 int element = EL_CUSTOM_START + i;
8104 chunk_size = SaveLevel_CUSX(NULL, level, element);
8105 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8107 putFileChunkBE(file, "CUSX", chunk_size);
8108 SaveLevel_CUSX(file, level, element);
8112 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8114 int element = EL_GROUP_START + i;
8116 chunk_size = SaveLevel_GRPX(NULL, level, element);
8117 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8119 putFileChunkBE(file, "GRPX", chunk_size);
8120 SaveLevel_GRPX(file, level, element);
8124 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8126 int element = GET_EMPTY_ELEMENT(i);
8128 chunk_size = SaveLevel_EMPX(NULL, level, element);
8129 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8131 putFileChunkBE(file, "EMPX", chunk_size);
8132 SaveLevel_EMPX(file, level, element);
8139 SetFilePermissions(filename, PERMS_PRIVATE);
8142 void SaveLevel(int nr)
8144 char *filename = getDefaultLevelFilename(nr);
8146 SaveLevelFromFilename(&level, filename, FALSE);
8149 void SaveLevelTemplate(void)
8151 char *filename = getLocalLevelTemplateFilename();
8153 SaveLevelFromFilename(&level, filename, TRUE);
8156 boolean SaveLevelChecked(int nr)
8158 char *filename = getDefaultLevelFilename(nr);
8159 boolean new_level = !fileExists(filename);
8160 boolean level_saved = FALSE;
8162 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8167 Request("Level saved!", REQ_CONFIRM);
8175 void DumpLevel(struct LevelInfo *level)
8177 if (level->no_level_file || level->no_valid_file)
8179 Warn("cannot dump -- no valid level file found");
8185 Print("Level xxx (file version %08d, game version %08d)\n",
8186 level->file_version, level->game_version);
8189 Print("Level author: '%s'\n", level->author);
8190 Print("Level title: '%s'\n", level->name);
8192 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8194 Print("Level time: %d seconds\n", level->time);
8195 Print("Gems needed: %d\n", level->gems_needed);
8197 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8198 Print("Time for wheel: %d seconds\n", level->time_wheel);
8199 Print("Time for light: %d seconds\n", level->time_light);
8200 Print("Time for timegate: %d seconds\n", level->time_timegate);
8202 Print("Amoeba speed: %d\n", level->amoeba_speed);
8205 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8206 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8207 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8208 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8209 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8210 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8216 for (i = 0; i < NUM_ENVELOPES; i++)
8218 char *text = level->envelope[i].text;
8219 int text_len = strlen(text);
8220 boolean has_text = FALSE;
8222 for (j = 0; j < text_len; j++)
8223 if (text[j] != ' ' && text[j] != '\n')
8229 Print("Envelope %d:\n'%s'\n", i + 1, text);
8237 void DumpLevels(void)
8239 static LevelDirTree *dumplevel_leveldir = NULL;
8241 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8242 global.dumplevel_leveldir);
8244 if (dumplevel_leveldir == NULL)
8245 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8247 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8248 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8249 Fail("no such level number: %d", global.dumplevel_level_nr);
8251 leveldir_current = dumplevel_leveldir;
8253 LoadLevel(global.dumplevel_level_nr);
8260 // ============================================================================
8261 // tape file functions
8262 // ============================================================================
8264 static void setTapeInfoToDefaults(void)
8268 // always start with reliable default values (empty tape)
8271 // default values (also for pre-1.2 tapes) with only the first player
8272 tape.player_participates[0] = TRUE;
8273 for (i = 1; i < MAX_PLAYERS; i++)
8274 tape.player_participates[i] = FALSE;
8276 // at least one (default: the first) player participates in every tape
8277 tape.num_participating_players = 1;
8279 tape.property_bits = TAPE_PROPERTY_NONE;
8281 tape.level_nr = level_nr;
8283 tape.changed = FALSE;
8284 tape.solved = FALSE;
8286 tape.recording = FALSE;
8287 tape.playing = FALSE;
8288 tape.pausing = FALSE;
8290 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8291 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8293 tape.no_info_chunk = TRUE;
8294 tape.no_valid_file = FALSE;
8297 static int getTapePosSize(struct TapeInfo *tape)
8299 int tape_pos_size = 0;
8301 if (tape->use_key_actions)
8302 tape_pos_size += tape->num_participating_players;
8304 if (tape->use_mouse_actions)
8305 tape_pos_size += 3; // x and y position and mouse button mask
8307 tape_pos_size += 1; // tape action delay value
8309 return tape_pos_size;
8312 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8314 tape->use_key_actions = FALSE;
8315 tape->use_mouse_actions = FALSE;
8317 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8318 tape->use_key_actions = TRUE;
8320 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8321 tape->use_mouse_actions = TRUE;
8324 static int getTapeActionValue(struct TapeInfo *tape)
8326 return (tape->use_key_actions &&
8327 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8328 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8329 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8330 TAPE_ACTIONS_DEFAULT);
8333 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8335 tape->file_version = getFileVersion(file);
8336 tape->game_version = getFileVersion(file);
8341 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8345 tape->random_seed = getFile32BitBE(file);
8346 tape->date = getFile32BitBE(file);
8347 tape->length = getFile32BitBE(file);
8349 // read header fields that are new since version 1.2
8350 if (tape->file_version >= FILE_VERSION_1_2)
8352 byte store_participating_players = getFile8Bit(file);
8355 // since version 1.2, tapes store which players participate in the tape
8356 tape->num_participating_players = 0;
8357 for (i = 0; i < MAX_PLAYERS; i++)
8359 tape->player_participates[i] = FALSE;
8361 if (store_participating_players & (1 << i))
8363 tape->player_participates[i] = TRUE;
8364 tape->num_participating_players++;
8368 setTapeActionFlags(tape, getFile8Bit(file));
8370 tape->property_bits = getFile8Bit(file);
8371 tape->solved = getFile8Bit(file);
8373 engine_version = getFileVersion(file);
8374 if (engine_version > 0)
8375 tape->engine_version = engine_version;
8377 tape->engine_version = tape->game_version;
8383 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8385 tape->scr_fieldx = getFile8Bit(file);
8386 tape->scr_fieldy = getFile8Bit(file);
8391 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8393 char *level_identifier = NULL;
8394 int level_identifier_size;
8397 tape->no_info_chunk = FALSE;
8399 level_identifier_size = getFile16BitBE(file);
8401 level_identifier = checked_malloc(level_identifier_size);
8403 for (i = 0; i < level_identifier_size; i++)
8404 level_identifier[i] = getFile8Bit(file);
8406 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8407 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8409 checked_free(level_identifier);
8411 tape->level_nr = getFile16BitBE(file);
8413 chunk_size = 2 + level_identifier_size + 2;
8418 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8421 int tape_pos_size = getTapePosSize(tape);
8422 int chunk_size_expected = tape_pos_size * tape->length;
8424 if (chunk_size_expected != chunk_size)
8426 ReadUnusedBytesFromFile(file, chunk_size);
8427 return chunk_size_expected;
8430 for (i = 0; i < tape->length; i++)
8432 if (i >= MAX_TAPE_LEN)
8434 Warn("tape truncated -- size exceeds maximum tape size %d",
8437 // tape too large; read and ignore remaining tape data from this chunk
8438 for (;i < tape->length; i++)
8439 ReadUnusedBytesFromFile(file, tape_pos_size);
8444 if (tape->use_key_actions)
8446 for (j = 0; j < MAX_PLAYERS; j++)
8448 tape->pos[i].action[j] = MV_NONE;
8450 if (tape->player_participates[j])
8451 tape->pos[i].action[j] = getFile8Bit(file);
8455 if (tape->use_mouse_actions)
8457 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8458 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8459 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8462 tape->pos[i].delay = getFile8Bit(file);
8464 if (tape->file_version == FILE_VERSION_1_0)
8466 // eliminate possible diagonal moves in old tapes
8467 // this is only for backward compatibility
8469 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8470 byte action = tape->pos[i].action[0];
8471 int k, num_moves = 0;
8473 for (k = 0; k < 4; k++)
8475 if (action & joy_dir[k])
8477 tape->pos[i + num_moves].action[0] = joy_dir[k];
8479 tape->pos[i + num_moves].delay = 0;
8488 tape->length += num_moves;
8491 else if (tape->file_version < FILE_VERSION_2_0)
8493 // convert pre-2.0 tapes to new tape format
8495 if (tape->pos[i].delay > 1)
8498 tape->pos[i + 1] = tape->pos[i];
8499 tape->pos[i + 1].delay = 1;
8502 for (j = 0; j < MAX_PLAYERS; j++)
8503 tape->pos[i].action[j] = MV_NONE;
8504 tape->pos[i].delay--;
8511 if (checkEndOfFile(file))
8515 if (i != tape->length)
8516 chunk_size = tape_pos_size * i;
8521 static void LoadTape_SokobanSolution(char *filename)
8524 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8526 if (!(file = openFile(filename, MODE_READ)))
8528 tape.no_valid_file = TRUE;
8533 while (!checkEndOfFile(file))
8535 unsigned char c = getByteFromFile(file);
8537 if (checkEndOfFile(file))
8544 tape.pos[tape.length].action[0] = MV_UP;
8545 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8551 tape.pos[tape.length].action[0] = MV_DOWN;
8552 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8558 tape.pos[tape.length].action[0] = MV_LEFT;
8559 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8565 tape.pos[tape.length].action[0] = MV_RIGHT;
8566 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8574 // ignore white-space characters
8578 tape.no_valid_file = TRUE;
8580 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8588 if (tape.no_valid_file)
8591 tape.length_frames = GetTapeLengthFrames();
8592 tape.length_seconds = GetTapeLengthSeconds();
8595 void LoadTapeFromFilename(char *filename)
8597 char cookie[MAX_LINE_LEN];
8598 char chunk_name[CHUNK_ID_LEN + 1];
8602 // always start with reliable default values
8603 setTapeInfoToDefaults();
8605 if (strSuffix(filename, ".sln"))
8607 LoadTape_SokobanSolution(filename);
8612 if (!(file = openFile(filename, MODE_READ)))
8614 tape.no_valid_file = TRUE;
8619 getFileChunkBE(file, chunk_name, NULL);
8620 if (strEqual(chunk_name, "RND1"))
8622 getFile32BitBE(file); // not used
8624 getFileChunkBE(file, chunk_name, NULL);
8625 if (!strEqual(chunk_name, "TAPE"))
8627 tape.no_valid_file = TRUE;
8629 Warn("unknown format of tape file '%s'", filename);
8636 else // check for pre-2.0 file format with cookie string
8638 strcpy(cookie, chunk_name);
8639 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8641 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8642 cookie[strlen(cookie) - 1] = '\0';
8644 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8646 tape.no_valid_file = TRUE;
8648 Warn("unknown format of tape file '%s'", filename);
8655 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8657 tape.no_valid_file = TRUE;
8659 Warn("unsupported version of tape file '%s'", filename);
8666 // pre-2.0 tape files have no game version, so use file version here
8667 tape.game_version = tape.file_version;
8670 if (tape.file_version < FILE_VERSION_1_2)
8672 // tape files from versions before 1.2.0 without chunk structure
8673 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8674 LoadTape_BODY(file, 2 * tape.length, &tape);
8682 int (*loader)(File *, int, struct TapeInfo *);
8686 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8687 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8688 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8689 { "INFO", -1, LoadTape_INFO },
8690 { "BODY", -1, LoadTape_BODY },
8694 while (getFileChunkBE(file, chunk_name, &chunk_size))
8698 while (chunk_info[i].name != NULL &&
8699 !strEqual(chunk_name, chunk_info[i].name))
8702 if (chunk_info[i].name == NULL)
8704 Warn("unknown chunk '%s' in tape file '%s'",
8705 chunk_name, filename);
8707 ReadUnusedBytesFromFile(file, chunk_size);
8709 else if (chunk_info[i].size != -1 &&
8710 chunk_info[i].size != chunk_size)
8712 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8713 chunk_size, chunk_name, filename);
8715 ReadUnusedBytesFromFile(file, chunk_size);
8719 // call function to load this tape chunk
8720 int chunk_size_expected =
8721 (chunk_info[i].loader)(file, chunk_size, &tape);
8723 // the size of some chunks cannot be checked before reading other
8724 // chunks first (like "HEAD" and "BODY") that contain some header
8725 // information, so check them here
8726 if (chunk_size_expected != chunk_size)
8728 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8729 chunk_size, chunk_name, filename);
8737 tape.length_frames = GetTapeLengthFrames();
8738 tape.length_seconds = GetTapeLengthSeconds();
8741 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8743 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8745 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8746 tape.engine_version);
8750 void LoadTape(int nr)
8752 char *filename = getTapeFilename(nr);
8754 LoadTapeFromFilename(filename);
8757 void LoadSolutionTape(int nr)
8759 char *filename = getSolutionTapeFilename(nr);
8761 LoadTapeFromFilename(filename);
8763 if (TAPE_IS_EMPTY(tape))
8765 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8766 level.native_bd_level->replay != NULL)
8767 CopyNativeTape_BD_to_RND(&level);
8768 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8769 level.native_sp_level->demo.is_available)
8770 CopyNativeTape_SP_to_RND(&level);
8774 void LoadScoreTape(char *score_tape_basename, int nr)
8776 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8778 LoadTapeFromFilename(filename);
8781 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8783 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8785 LoadTapeFromFilename(filename);
8788 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8790 // chunk required for team mode tapes with non-default screen size
8791 return (tape->num_participating_players > 1 &&
8792 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8793 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8796 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8798 putFileVersion(file, tape->file_version);
8799 putFileVersion(file, tape->game_version);
8802 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8805 byte store_participating_players = 0;
8807 // set bits for participating players for compact storage
8808 for (i = 0; i < MAX_PLAYERS; i++)
8809 if (tape->player_participates[i])
8810 store_participating_players |= (1 << i);
8812 putFile32BitBE(file, tape->random_seed);
8813 putFile32BitBE(file, tape->date);
8814 putFile32BitBE(file, tape->length);
8816 putFile8Bit(file, store_participating_players);
8818 putFile8Bit(file, getTapeActionValue(tape));
8820 putFile8Bit(file, tape->property_bits);
8821 putFile8Bit(file, tape->solved);
8823 putFileVersion(file, tape->engine_version);
8826 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8828 putFile8Bit(file, tape->scr_fieldx);
8829 putFile8Bit(file, tape->scr_fieldy);
8832 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8834 int level_identifier_size = strlen(tape->level_identifier) + 1;
8837 putFile16BitBE(file, level_identifier_size);
8839 for (i = 0; i < level_identifier_size; i++)
8840 putFile8Bit(file, tape->level_identifier[i]);
8842 putFile16BitBE(file, tape->level_nr);
8845 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8849 for (i = 0; i < tape->length; i++)
8851 if (tape->use_key_actions)
8853 for (j = 0; j < MAX_PLAYERS; j++)
8854 if (tape->player_participates[j])
8855 putFile8Bit(file, tape->pos[i].action[j]);
8858 if (tape->use_mouse_actions)
8860 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8861 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8862 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8865 putFile8Bit(file, tape->pos[i].delay);
8869 void SaveTapeToFilename(char *filename)
8873 int info_chunk_size;
8874 int body_chunk_size;
8876 if (!(file = fopen(filename, MODE_WRITE)))
8878 Warn("cannot save level recording file '%s'", filename);
8883 tape_pos_size = getTapePosSize(&tape);
8885 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8886 body_chunk_size = tape_pos_size * tape.length;
8888 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8889 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8891 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8892 SaveTape_VERS(file, &tape);
8894 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8895 SaveTape_HEAD(file, &tape);
8897 if (checkSaveTape_SCRN(&tape))
8899 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8900 SaveTape_SCRN(file, &tape);
8903 putFileChunkBE(file, "INFO", info_chunk_size);
8904 SaveTape_INFO(file, &tape);
8906 putFileChunkBE(file, "BODY", body_chunk_size);
8907 SaveTape_BODY(file, &tape);
8911 SetFilePermissions(filename, PERMS_PRIVATE);
8914 static void SaveTapeExt(char *filename)
8918 tape.file_version = FILE_VERSION_ACTUAL;
8919 tape.game_version = GAME_VERSION_ACTUAL;
8921 tape.num_participating_players = 0;
8923 // count number of participating players
8924 for (i = 0; i < MAX_PLAYERS; i++)
8925 if (tape.player_participates[i])
8926 tape.num_participating_players++;
8928 SaveTapeToFilename(filename);
8930 tape.changed = FALSE;
8933 void SaveTape(int nr)
8935 char *filename = getTapeFilename(nr);
8937 InitTapeDirectory(leveldir_current->subdir);
8939 SaveTapeExt(filename);
8942 void SaveScoreTape(int nr)
8944 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8946 // used instead of "leveldir_current->subdir" (for network games)
8947 InitScoreTapeDirectory(levelset.identifier, nr);
8949 SaveTapeExt(filename);
8952 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8953 unsigned int req_state_added)
8955 char *filename = getTapeFilename(nr);
8956 boolean new_tape = !fileExists(filename);
8957 boolean tape_saved = FALSE;
8959 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8964 Request(msg_saved, REQ_CONFIRM | req_state_added);
8972 boolean SaveTapeChecked(int nr)
8974 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8977 boolean SaveTapeChecked_LevelSolved(int nr)
8979 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8980 "Level solved! Tape saved!", REQ_STAY_OPEN);
8983 void DumpTape(struct TapeInfo *tape)
8985 int tape_frame_counter;
8988 if (tape->no_valid_file)
8990 Warn("cannot dump -- no valid tape file found");
8997 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8998 tape->level_nr, tape->file_version, tape->game_version);
8999 Print(" (effective engine version %08d)\n",
9000 tape->engine_version);
9001 Print("Level series identifier: '%s'\n", tape->level_identifier);
9003 Print("Solution tape: %s\n",
9004 tape->solved ? "yes" :
9005 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9007 Print("Special tape properties: ");
9008 if (tape->property_bits == TAPE_PROPERTY_NONE)
9010 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9011 Print("[em_random_bug]");
9012 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9013 Print("[game_speed]");
9014 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9016 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9017 Print("[single_step]");
9018 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9019 Print("[snapshot]");
9020 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9021 Print("[replayed]");
9022 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9023 Print("[tas_keys]");
9024 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9025 Print("[small_graphics]");
9028 int year2 = tape->date / 10000;
9029 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9030 int month_index_raw = (tape->date / 100) % 100;
9031 int month_index = month_index_raw % 12; // prevent invalid index
9032 int month = month_index + 1;
9033 int day = tape->date % 100;
9035 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9039 tape_frame_counter = 0;
9041 for (i = 0; i < tape->length; i++)
9043 if (i >= MAX_TAPE_LEN)
9048 for (j = 0; j < MAX_PLAYERS; j++)
9050 if (tape->player_participates[j])
9052 int action = tape->pos[i].action[j];
9054 Print("%d:%02x ", j, action);
9055 Print("[%c%c%c%c|%c%c] - ",
9056 (action & JOY_LEFT ? '<' : ' '),
9057 (action & JOY_RIGHT ? '>' : ' '),
9058 (action & JOY_UP ? '^' : ' '),
9059 (action & JOY_DOWN ? 'v' : ' '),
9060 (action & JOY_BUTTON_1 ? '1' : ' '),
9061 (action & JOY_BUTTON_2 ? '2' : ' '));
9065 Print("(%03d) ", tape->pos[i].delay);
9066 Print("[%05d]\n", tape_frame_counter);
9068 tape_frame_counter += tape->pos[i].delay;
9074 void DumpTapes(void)
9076 static LevelDirTree *dumptape_leveldir = NULL;
9078 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9079 global.dumptape_leveldir);
9081 if (dumptape_leveldir == NULL)
9082 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9084 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9085 global.dumptape_level_nr > dumptape_leveldir->last_level)
9086 Fail("no such level number: %d", global.dumptape_level_nr);
9088 leveldir_current = dumptape_leveldir;
9090 if (options.mytapes)
9091 LoadTape(global.dumptape_level_nr);
9093 LoadSolutionTape(global.dumptape_level_nr);
9101 // ============================================================================
9102 // score file functions
9103 // ============================================================================
9105 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9109 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9111 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9112 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9113 scores->entry[i].score = 0;
9114 scores->entry[i].time = 0;
9116 scores->entry[i].id = -1;
9117 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9118 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9119 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9120 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9121 strcpy(scores->entry[i].country_code, "??");
9124 scores->num_entries = 0;
9125 scores->last_added = -1;
9126 scores->last_added_local = -1;
9128 scores->updated = FALSE;
9129 scores->uploaded = FALSE;
9130 scores->tape_downloaded = FALSE;
9131 scores->force_last_added = FALSE;
9133 // The following values are intentionally not reset here:
9137 // - continue_playing
9138 // - continue_on_return
9141 static void setScoreInfoToDefaults(void)
9143 setScoreInfoToDefaultsExt(&scores);
9146 static void setServerScoreInfoToDefaults(void)
9148 setScoreInfoToDefaultsExt(&server_scores);
9151 static void LoadScore_OLD(int nr)
9154 char *filename = getScoreFilename(nr);
9155 char cookie[MAX_LINE_LEN];
9156 char line[MAX_LINE_LEN];
9160 if (!(file = fopen(filename, MODE_READ)))
9163 // check file identifier
9164 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9166 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9167 cookie[strlen(cookie) - 1] = '\0';
9169 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9171 Warn("unknown format of score file '%s'", filename);
9178 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9180 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9181 Warn("fscanf() failed; %s", strerror(errno));
9183 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9186 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9187 line[strlen(line) - 1] = '\0';
9189 for (line_ptr = line; *line_ptr; line_ptr++)
9191 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9193 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9194 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9203 static void ConvertScore_OLD(void)
9205 // only convert score to time for levels that rate playing time over score
9206 if (!level.rate_time_over_score)
9209 // convert old score to playing time for score-less levels (like Supaplex)
9210 int time_final_max = 999;
9213 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9215 int score = scores.entry[i].score;
9217 if (score > 0 && score < time_final_max)
9218 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9222 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9224 scores->file_version = getFileVersion(file);
9225 scores->game_version = getFileVersion(file);
9230 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9232 char *level_identifier = NULL;
9233 int level_identifier_size;
9236 level_identifier_size = getFile16BitBE(file);
9238 level_identifier = checked_malloc(level_identifier_size);
9240 for (i = 0; i < level_identifier_size; i++)
9241 level_identifier[i] = getFile8Bit(file);
9243 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9244 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9246 checked_free(level_identifier);
9248 scores->level_nr = getFile16BitBE(file);
9249 scores->num_entries = getFile16BitBE(file);
9251 chunk_size = 2 + level_identifier_size + 2 + 2;
9256 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9260 for (i = 0; i < scores->num_entries; i++)
9262 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9263 scores->entry[i].name[j] = getFile8Bit(file);
9265 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9268 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9273 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9277 for (i = 0; i < scores->num_entries; i++)
9278 scores->entry[i].score = getFile16BitBE(file);
9280 chunk_size = scores->num_entries * 2;
9285 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9289 for (i = 0; i < scores->num_entries; i++)
9290 scores->entry[i].score = getFile32BitBE(file);
9292 chunk_size = scores->num_entries * 4;
9297 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9301 for (i = 0; i < scores->num_entries; i++)
9302 scores->entry[i].time = getFile32BitBE(file);
9304 chunk_size = scores->num_entries * 4;
9309 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9313 for (i = 0; i < scores->num_entries; i++)
9315 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9316 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9318 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9321 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9326 void LoadScore(int nr)
9328 char *filename = getScoreFilename(nr);
9329 char cookie[MAX_LINE_LEN];
9330 char chunk_name[CHUNK_ID_LEN + 1];
9332 boolean old_score_file_format = FALSE;
9335 // always start with reliable default values
9336 setScoreInfoToDefaults();
9338 if (!(file = openFile(filename, MODE_READ)))
9341 getFileChunkBE(file, chunk_name, NULL);
9342 if (strEqual(chunk_name, "RND1"))
9344 getFile32BitBE(file); // not used
9346 getFileChunkBE(file, chunk_name, NULL);
9347 if (!strEqual(chunk_name, "SCOR"))
9349 Warn("unknown format of score file '%s'", filename);
9356 else // check for old file format with cookie string
9358 strcpy(cookie, chunk_name);
9359 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9361 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9362 cookie[strlen(cookie) - 1] = '\0';
9364 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9366 Warn("unknown format of score file '%s'", filename);
9373 old_score_file_format = TRUE;
9376 if (old_score_file_format)
9378 // score files from versions before 4.2.4.0 without chunk structure
9381 // convert score to time, if possible (mainly for Supaplex levels)
9390 int (*loader)(File *, int, struct ScoreInfo *);
9394 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9395 { "INFO", -1, LoadScore_INFO },
9396 { "NAME", -1, LoadScore_NAME },
9397 { "SCOR", -1, LoadScore_SCOR },
9398 { "SC4R", -1, LoadScore_SC4R },
9399 { "TIME", -1, LoadScore_TIME },
9400 { "TAPE", -1, LoadScore_TAPE },
9405 while (getFileChunkBE(file, chunk_name, &chunk_size))
9409 while (chunk_info[i].name != NULL &&
9410 !strEqual(chunk_name, chunk_info[i].name))
9413 if (chunk_info[i].name == NULL)
9415 Warn("unknown chunk '%s' in score file '%s'",
9416 chunk_name, filename);
9418 ReadUnusedBytesFromFile(file, chunk_size);
9420 else if (chunk_info[i].size != -1 &&
9421 chunk_info[i].size != chunk_size)
9423 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9424 chunk_size, chunk_name, filename);
9426 ReadUnusedBytesFromFile(file, chunk_size);
9430 // call function to load this score chunk
9431 int chunk_size_expected =
9432 (chunk_info[i].loader)(file, chunk_size, &scores);
9434 // the size of some chunks cannot be checked before reading other
9435 // chunks first (like "HEAD" and "BODY") that contain some header
9436 // information, so check them here
9437 if (chunk_size_expected != chunk_size)
9439 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9440 chunk_size, chunk_name, filename);
9449 #if ENABLE_HISTORIC_CHUNKS
9450 void SaveScore_OLD(int nr)
9453 char *filename = getScoreFilename(nr);
9456 // used instead of "leveldir_current->subdir" (for network games)
9457 InitScoreDirectory(levelset.identifier);
9459 if (!(file = fopen(filename, MODE_WRITE)))
9461 Warn("cannot save score for level %d", nr);
9466 fprintf(file, "%s\n\n", SCORE_COOKIE);
9468 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9469 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9473 SetFilePermissions(filename, PERMS_PRIVATE);
9477 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9479 putFileVersion(file, scores->file_version);
9480 putFileVersion(file, scores->game_version);
9483 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9485 int level_identifier_size = strlen(scores->level_identifier) + 1;
9488 putFile16BitBE(file, level_identifier_size);
9490 for (i = 0; i < level_identifier_size; i++)
9491 putFile8Bit(file, scores->level_identifier[i]);
9493 putFile16BitBE(file, scores->level_nr);
9494 putFile16BitBE(file, scores->num_entries);
9497 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9501 for (i = 0; i < scores->num_entries; i++)
9503 int name_size = strlen(scores->entry[i].name);
9505 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9506 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9510 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9514 for (i = 0; i < scores->num_entries; i++)
9515 putFile16BitBE(file, scores->entry[i].score);
9518 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9522 for (i = 0; i < scores->num_entries; i++)
9523 putFile32BitBE(file, scores->entry[i].score);
9526 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9530 for (i = 0; i < scores->num_entries; i++)
9531 putFile32BitBE(file, scores->entry[i].time);
9534 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9538 for (i = 0; i < scores->num_entries; i++)
9540 int size = strlen(scores->entry[i].tape_basename);
9542 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9543 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9547 static void SaveScoreToFilename(char *filename)
9550 int info_chunk_size;
9551 int name_chunk_size;
9552 int scor_chunk_size;
9553 int sc4r_chunk_size;
9554 int time_chunk_size;
9555 int tape_chunk_size;
9556 boolean has_large_score_values;
9559 if (!(file = fopen(filename, MODE_WRITE)))
9561 Warn("cannot save score file '%s'", filename);
9566 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9567 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9568 scor_chunk_size = scores.num_entries * 2;
9569 sc4r_chunk_size = scores.num_entries * 4;
9570 time_chunk_size = scores.num_entries * 4;
9571 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9573 has_large_score_values = FALSE;
9574 for (i = 0; i < scores.num_entries; i++)
9575 if (scores.entry[i].score > 0xffff)
9576 has_large_score_values = TRUE;
9578 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9579 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9581 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9582 SaveScore_VERS(file, &scores);
9584 putFileChunkBE(file, "INFO", info_chunk_size);
9585 SaveScore_INFO(file, &scores);
9587 putFileChunkBE(file, "NAME", name_chunk_size);
9588 SaveScore_NAME(file, &scores);
9590 if (has_large_score_values)
9592 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9593 SaveScore_SC4R(file, &scores);
9597 putFileChunkBE(file, "SCOR", scor_chunk_size);
9598 SaveScore_SCOR(file, &scores);
9601 putFileChunkBE(file, "TIME", time_chunk_size);
9602 SaveScore_TIME(file, &scores);
9604 putFileChunkBE(file, "TAPE", tape_chunk_size);
9605 SaveScore_TAPE(file, &scores);
9609 SetFilePermissions(filename, PERMS_PRIVATE);
9612 void SaveScore(int nr)
9614 char *filename = getScoreFilename(nr);
9617 // used instead of "leveldir_current->subdir" (for network games)
9618 InitScoreDirectory(levelset.identifier);
9620 scores.file_version = FILE_VERSION_ACTUAL;
9621 scores.game_version = GAME_VERSION_ACTUAL;
9623 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9624 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9625 scores.level_nr = level_nr;
9627 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9628 if (scores.entry[i].score == 0 &&
9629 scores.entry[i].time == 0 &&
9630 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9633 scores.num_entries = i;
9635 if (scores.num_entries == 0)
9638 SaveScoreToFilename(filename);
9641 static void LoadServerScoreFromCache(int nr)
9643 struct ScoreEntry score_entry;
9652 { &score_entry.score, FALSE, 0 },
9653 { &score_entry.time, FALSE, 0 },
9654 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9655 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9656 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9657 { &score_entry.id, FALSE, 0 },
9658 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9659 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9660 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9661 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9665 char *filename = getScoreCacheFilename(nr);
9666 SetupFileHash *score_hash = loadSetupFileHash(filename);
9669 server_scores.num_entries = 0;
9671 if (score_hash == NULL)
9674 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9676 score_entry = server_scores.entry[i];
9678 for (j = 0; score_mapping[j].value != NULL; j++)
9682 sprintf(token, "%02d.%d", i, j);
9684 char *value = getHashEntry(score_hash, token);
9689 if (score_mapping[j].is_string)
9691 char *score_value = (char *)score_mapping[j].value;
9692 int value_size = score_mapping[j].string_size;
9694 strncpy(score_value, value, value_size);
9695 score_value[value_size] = '\0';
9699 int *score_value = (int *)score_mapping[j].value;
9701 *score_value = atoi(value);
9704 server_scores.num_entries = i + 1;
9707 server_scores.entry[i] = score_entry;
9710 freeSetupFileHash(score_hash);
9713 void LoadServerScore(int nr, boolean download_score)
9715 if (!setup.use_api_server)
9718 // always start with reliable default values
9719 setServerScoreInfoToDefaults();
9721 // 1st step: load server scores from cache file (which may not exist)
9722 // (this should prevent reading it while the thread is writing to it)
9723 LoadServerScoreFromCache(nr);
9725 if (download_score && runtime.use_api_server)
9727 // 2nd step: download server scores from score server to cache file
9728 // (as thread, as it might time out if the server is not reachable)
9729 ApiGetScoreAsThread(nr);
9733 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9735 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9737 // if score tape not uploaded, ask for uploading missing tapes later
9738 if (!setup.has_remaining_tapes)
9739 setup.ask_for_remaining_tapes = TRUE;
9741 setup.provide_uploading_tapes = TRUE;
9742 setup.has_remaining_tapes = TRUE;
9744 SaveSetup_ServerSetup();
9747 void SaveServerScore(int nr, boolean tape_saved)
9749 if (!runtime.use_api_server)
9751 PrepareScoreTapesForUpload(leveldir_current->subdir);
9756 ApiAddScoreAsThread(nr, tape_saved, NULL);
9759 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9760 char *score_tape_filename)
9762 if (!runtime.use_api_server)
9765 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9768 void LoadLocalAndServerScore(int nr, boolean download_score)
9770 int last_added_local = scores.last_added_local;
9771 boolean force_last_added = scores.force_last_added;
9773 // needed if only showing server scores
9774 setScoreInfoToDefaults();
9776 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9779 // restore last added local score entry (before merging server scores)
9780 scores.last_added = scores.last_added_local = last_added_local;
9782 if (setup.use_api_server &&
9783 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9785 // load server scores from cache file and trigger update from server
9786 LoadServerScore(nr, download_score);
9788 // merge local scores with scores from server
9792 if (force_last_added)
9793 scores.force_last_added = force_last_added;
9797 // ============================================================================
9798 // setup file functions
9799 // ============================================================================
9801 #define TOKEN_STR_PLAYER_PREFIX "player_"
9804 static struct TokenInfo global_setup_tokens[] =
9808 &setup.player_name, "player_name"
9812 &setup.multiple_users, "multiple_users"
9816 &setup.sound, "sound"
9820 &setup.sound_loops, "repeating_sound_loops"
9824 &setup.sound_music, "background_music"
9828 &setup.sound_simple, "simple_sound_effects"
9832 &setup.toons, "toons"
9836 &setup.global_animations, "global_animations"
9840 &setup.scroll_delay, "scroll_delay"
9844 &setup.forced_scroll_delay, "forced_scroll_delay"
9848 &setup.scroll_delay_value, "scroll_delay_value"
9852 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9856 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9860 &setup.fade_screens, "fade_screens"
9864 &setup.autorecord, "automatic_tape_recording"
9868 &setup.autorecord_after_replay, "autorecord_after_replay"
9872 &setup.auto_pause_on_start, "auto_pause_on_start"
9876 &setup.show_titlescreen, "show_titlescreen"
9880 &setup.quick_doors, "quick_doors"
9884 &setup.team_mode, "team_mode"
9888 &setup.handicap, "handicap"
9892 &setup.skip_levels, "skip_levels"
9896 &setup.increment_levels, "increment_levels"
9900 &setup.auto_play_next_level, "auto_play_next_level"
9904 &setup.count_score_after_game, "count_score_after_game"
9908 &setup.show_scores_after_game, "show_scores_after_game"
9912 &setup.time_limit, "time_limit"
9916 &setup.fullscreen, "fullscreen"
9920 &setup.window_scaling_percent, "window_scaling_percent"
9924 &setup.window_scaling_quality, "window_scaling_quality"
9928 &setup.screen_rendering_mode, "screen_rendering_mode"
9932 &setup.vsync_mode, "vsync_mode"
9936 &setup.ask_on_escape, "ask_on_escape"
9940 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9944 &setup.ask_on_game_over, "ask_on_game_over"
9948 &setup.ask_on_quit_game, "ask_on_quit_game"
9952 &setup.ask_on_quit_program, "ask_on_quit_program"
9956 &setup.quick_switch, "quick_player_switch"
9960 &setup.input_on_focus, "input_on_focus"
9964 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9968 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9972 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9976 &setup.game_speed_extended, "game_speed_extended"
9980 &setup.game_frame_delay, "game_frame_delay"
9984 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9988 &setup.bd_skip_hatching, "bd_skip_hatching"
9992 &setup.bd_scroll_delay, "bd_scroll_delay"
9996 &setup.bd_smooth_movements, "bd_smooth_movements"
10000 &setup.sp_show_border_elements, "sp_show_border_elements"
10004 &setup.small_game_graphics, "small_game_graphics"
10008 &setup.show_load_save_buttons, "show_load_save_buttons"
10012 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10016 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10020 &setup.graphics_set, "graphics_set"
10024 &setup.sounds_set, "sounds_set"
10028 &setup.music_set, "music_set"
10032 &setup.override_level_graphics, "override_level_graphics"
10036 &setup.override_level_sounds, "override_level_sounds"
10040 &setup.override_level_music, "override_level_music"
10044 &setup.volume_simple, "volume_simple"
10048 &setup.volume_loops, "volume_loops"
10052 &setup.volume_music, "volume_music"
10056 &setup.network_mode, "network_mode"
10060 &setup.network_player_nr, "network_player"
10064 &setup.network_server_hostname, "network_server_hostname"
10068 &setup.touch.control_type, "touch.control_type"
10072 &setup.touch.move_distance, "touch.move_distance"
10076 &setup.touch.drop_distance, "touch.drop_distance"
10080 &setup.touch.transparency, "touch.transparency"
10084 &setup.touch.draw_outlined, "touch.draw_outlined"
10088 &setup.touch.draw_pressed, "touch.draw_pressed"
10092 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10096 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10100 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10104 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10108 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10112 static struct TokenInfo auto_setup_tokens[] =
10116 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10120 static struct TokenInfo server_setup_tokens[] =
10124 &setup.player_uuid, "player_uuid"
10128 &setup.player_version, "player_version"
10132 &setup.use_api_server, TEST_PREFIX "use_api_server"
10136 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10140 &setup.api_server_password, TEST_PREFIX "api_server_password"
10144 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10148 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10152 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10156 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10160 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10164 static struct TokenInfo editor_setup_tokens[] =
10168 &setup.editor.el_classic, "editor.el_classic"
10172 &setup.editor.el_custom, "editor.el_custom"
10176 &setup.editor.el_user_defined, "editor.el_user_defined"
10180 &setup.editor.el_dynamic, "editor.el_dynamic"
10184 &setup.editor.el_headlines, "editor.el_headlines"
10188 &setup.editor.show_element_token, "editor.show_element_token"
10192 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10196 static struct TokenInfo editor_cascade_setup_tokens[] =
10200 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10204 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10208 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10212 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10216 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10220 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10224 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10228 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10232 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10236 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10240 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10244 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10248 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10252 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10256 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10260 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10264 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10268 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10272 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10276 static struct TokenInfo shortcut_setup_tokens[] =
10280 &setup.shortcut.save_game, "shortcut.save_game"
10284 &setup.shortcut.load_game, "shortcut.load_game"
10288 &setup.shortcut.restart_game, "shortcut.restart_game"
10292 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10296 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10300 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10304 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10308 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10312 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10316 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10320 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10324 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10328 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10332 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10336 &setup.shortcut.tape_record, "shortcut.tape_record"
10340 &setup.shortcut.tape_play, "shortcut.tape_play"
10344 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10348 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10352 &setup.shortcut.sound_music, "shortcut.sound_music"
10356 &setup.shortcut.snap_left, "shortcut.snap_left"
10360 &setup.shortcut.snap_right, "shortcut.snap_right"
10364 &setup.shortcut.snap_up, "shortcut.snap_up"
10368 &setup.shortcut.snap_down, "shortcut.snap_down"
10372 static struct SetupInputInfo setup_input;
10373 static struct TokenInfo player_setup_tokens[] =
10377 &setup_input.use_joystick, ".use_joystick"
10381 &setup_input.joy.device_name, ".joy.device_name"
10385 &setup_input.joy.xleft, ".joy.xleft"
10389 &setup_input.joy.xmiddle, ".joy.xmiddle"
10393 &setup_input.joy.xright, ".joy.xright"
10397 &setup_input.joy.yupper, ".joy.yupper"
10401 &setup_input.joy.ymiddle, ".joy.ymiddle"
10405 &setup_input.joy.ylower, ".joy.ylower"
10409 &setup_input.joy.snap, ".joy.snap_field"
10413 &setup_input.joy.drop, ".joy.place_bomb"
10417 &setup_input.key.left, ".key.move_left"
10421 &setup_input.key.right, ".key.move_right"
10425 &setup_input.key.up, ".key.move_up"
10429 &setup_input.key.down, ".key.move_down"
10433 &setup_input.key.snap, ".key.snap_field"
10437 &setup_input.key.drop, ".key.place_bomb"
10441 static struct TokenInfo system_setup_tokens[] =
10445 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10449 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10453 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10457 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10461 static struct TokenInfo internal_setup_tokens[] =
10465 &setup.internal.program_title, "program_title"
10469 &setup.internal.program_version, "program_version"
10473 &setup.internal.program_author, "program_author"
10477 &setup.internal.program_email, "program_email"
10481 &setup.internal.program_website, "program_website"
10485 &setup.internal.program_copyright, "program_copyright"
10489 &setup.internal.program_company, "program_company"
10493 &setup.internal.program_icon_file, "program_icon_file"
10497 &setup.internal.default_graphics_set, "default_graphics_set"
10501 &setup.internal.default_sounds_set, "default_sounds_set"
10505 &setup.internal.default_music_set, "default_music_set"
10509 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10513 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10517 &setup.internal.fallback_music_file, "fallback_music_file"
10521 &setup.internal.default_level_series, "default_level_series"
10525 &setup.internal.default_window_width, "default_window_width"
10529 &setup.internal.default_window_height, "default_window_height"
10533 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10537 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10541 &setup.internal.create_user_levelset, "create_user_levelset"
10545 &setup.internal.info_screens_from_main, "info_screens_from_main"
10549 &setup.internal.menu_game, "menu_game"
10553 &setup.internal.menu_engines, "menu_engines"
10557 &setup.internal.menu_editor, "menu_editor"
10561 &setup.internal.menu_graphics, "menu_graphics"
10565 &setup.internal.menu_sound, "menu_sound"
10569 &setup.internal.menu_artwork, "menu_artwork"
10573 &setup.internal.menu_input, "menu_input"
10577 &setup.internal.menu_touch, "menu_touch"
10581 &setup.internal.menu_shortcuts, "menu_shortcuts"
10585 &setup.internal.menu_exit, "menu_exit"
10589 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10593 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10597 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10601 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10605 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10609 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10613 &setup.internal.info_title, "info_title"
10617 &setup.internal.info_elements, "info_elements"
10621 &setup.internal.info_music, "info_music"
10625 &setup.internal.info_credits, "info_credits"
10629 &setup.internal.info_program, "info_program"
10633 &setup.internal.info_version, "info_version"
10637 &setup.internal.info_levelset, "info_levelset"
10641 &setup.internal.info_exit, "info_exit"
10645 static struct TokenInfo debug_setup_tokens[] =
10649 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10653 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10657 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10661 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10665 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10669 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10673 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10677 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10681 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10685 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10689 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10693 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10697 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10701 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10705 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10709 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10713 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10717 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10721 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10725 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10729 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10732 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10736 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10740 &setup.debug.xsn_mode, "debug.xsn_mode"
10744 &setup.debug.xsn_percent, "debug.xsn_percent"
10748 static struct TokenInfo options_setup_tokens[] =
10752 &setup.options.verbose, "options.verbose"
10756 &setup.options.debug, "options.debug"
10760 &setup.options.debug_mode, "options.debug_mode"
10764 static void setSetupInfoToDefaults(struct SetupInfo *si)
10768 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10770 si->multiple_users = TRUE;
10773 si->sound_loops = TRUE;
10774 si->sound_music = TRUE;
10775 si->sound_simple = TRUE;
10777 si->global_animations = TRUE;
10778 si->scroll_delay = TRUE;
10779 si->forced_scroll_delay = FALSE;
10780 si->scroll_delay_value = STD_SCROLL_DELAY;
10781 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10782 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10783 si->fade_screens = TRUE;
10784 si->autorecord = TRUE;
10785 si->autorecord_after_replay = TRUE;
10786 si->auto_pause_on_start = FALSE;
10787 si->show_titlescreen = TRUE;
10788 si->quick_doors = FALSE;
10789 si->team_mode = FALSE;
10790 si->handicap = TRUE;
10791 si->skip_levels = TRUE;
10792 si->increment_levels = TRUE;
10793 si->auto_play_next_level = TRUE;
10794 si->count_score_after_game = TRUE;
10795 si->show_scores_after_game = TRUE;
10796 si->time_limit = TRUE;
10797 si->fullscreen = FALSE;
10798 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10799 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10800 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10801 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10802 si->ask_on_escape = TRUE;
10803 si->ask_on_escape_editor = TRUE;
10804 si->ask_on_game_over = TRUE;
10805 si->ask_on_quit_game = TRUE;
10806 si->ask_on_quit_program = TRUE;
10807 si->quick_switch = FALSE;
10808 si->input_on_focus = FALSE;
10809 si->prefer_aga_graphics = TRUE;
10810 si->prefer_lowpass_sounds = FALSE;
10811 si->prefer_extra_panel_items = TRUE;
10812 si->game_speed_extended = FALSE;
10813 si->game_frame_delay = GAME_FRAME_DELAY;
10814 si->bd_skip_uncovering = FALSE;
10815 si->bd_skip_hatching = FALSE;
10816 si->bd_scroll_delay = TRUE;
10817 si->bd_smooth_movements = AUTO;
10818 si->sp_show_border_elements = FALSE;
10819 si->small_game_graphics = FALSE;
10820 si->show_load_save_buttons = FALSE;
10821 si->show_undo_redo_buttons = FALSE;
10822 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10824 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10825 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10826 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10828 si->override_level_graphics = FALSE;
10829 si->override_level_sounds = FALSE;
10830 si->override_level_music = FALSE;
10832 si->volume_simple = 100; // percent
10833 si->volume_loops = 100; // percent
10834 si->volume_music = 100; // percent
10836 si->network_mode = FALSE;
10837 si->network_player_nr = 0; // first player
10838 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10840 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10841 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10842 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10843 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10844 si->touch.draw_outlined = TRUE;
10845 si->touch.draw_pressed = TRUE;
10847 for (i = 0; i < 2; i++)
10849 char *default_grid_button[6][2] =
10855 { "111222", " vv " },
10856 { "111222", " vv " }
10858 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10859 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10860 int min_xsize = MIN(6, grid_xsize);
10861 int min_ysize = MIN(6, grid_ysize);
10862 int startx = grid_xsize - min_xsize;
10863 int starty = grid_ysize - min_ysize;
10866 // virtual buttons grid can only be set to defaults if video is initialized
10867 // (this will be repeated if virtual buttons are not loaded from setup file)
10868 if (video.initialized)
10870 si->touch.grid_xsize[i] = grid_xsize;
10871 si->touch.grid_ysize[i] = grid_ysize;
10875 si->touch.grid_xsize[i] = -1;
10876 si->touch.grid_ysize[i] = -1;
10879 for (x = 0; x < MAX_GRID_XSIZE; x++)
10880 for (y = 0; y < MAX_GRID_YSIZE; y++)
10881 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10883 for (x = 0; x < min_xsize; x++)
10884 for (y = 0; y < min_ysize; y++)
10885 si->touch.grid_button[i][x][starty + y] =
10886 default_grid_button[y][0][x];
10888 for (x = 0; x < min_xsize; x++)
10889 for (y = 0; y < min_ysize; y++)
10890 si->touch.grid_button[i][startx + x][starty + y] =
10891 default_grid_button[y][1][x];
10894 si->touch.grid_initialized = video.initialized;
10896 si->touch.overlay_buttons = FALSE;
10898 si->editor.el_boulderdash = TRUE;
10899 si->editor.el_boulderdash_native = TRUE;
10900 si->editor.el_emerald_mine = TRUE;
10901 si->editor.el_emerald_mine_club = TRUE;
10902 si->editor.el_more = TRUE;
10903 si->editor.el_sokoban = TRUE;
10904 si->editor.el_supaplex = TRUE;
10905 si->editor.el_diamond_caves = TRUE;
10906 si->editor.el_dx_boulderdash = TRUE;
10908 si->editor.el_mirror_magic = TRUE;
10909 si->editor.el_deflektor = TRUE;
10911 si->editor.el_chars = TRUE;
10912 si->editor.el_steel_chars = TRUE;
10914 si->editor.el_classic = TRUE;
10915 si->editor.el_custom = TRUE;
10917 si->editor.el_user_defined = FALSE;
10918 si->editor.el_dynamic = TRUE;
10920 si->editor.el_headlines = TRUE;
10922 si->editor.show_element_token = FALSE;
10924 si->editor.show_read_only_warning = TRUE;
10926 si->editor.use_template_for_new_levels = TRUE;
10928 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10929 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10930 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10931 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10932 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10934 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10935 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10936 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10937 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10938 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10940 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10941 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10942 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10943 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10944 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10945 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10947 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10948 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10949 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10951 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10952 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10953 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10954 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10956 for (i = 0; i < MAX_PLAYERS; i++)
10958 si->input[i].use_joystick = FALSE;
10959 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10960 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10961 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10962 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10963 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10964 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10965 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10966 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10967 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10968 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10969 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10970 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10971 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10972 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10973 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10976 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10977 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10978 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10979 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10981 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10982 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10983 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10984 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10985 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10986 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10987 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10989 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10991 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10992 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10993 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10995 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10996 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10997 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10999 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11000 si->internal.choose_from_top_leveldir = FALSE;
11001 si->internal.show_scaling_in_title = TRUE;
11002 si->internal.create_user_levelset = TRUE;
11003 si->internal.info_screens_from_main = FALSE;
11005 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11006 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11008 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11009 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11010 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11011 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11012 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11013 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11014 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11015 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11016 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11017 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11019 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11020 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11021 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11022 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11023 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11024 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11025 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11026 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11027 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11028 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11030 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11031 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11033 si->debug.show_frames_per_second = FALSE;
11035 si->debug.xsn_mode = AUTO;
11036 si->debug.xsn_percent = 0;
11038 si->options.verbose = FALSE;
11039 si->options.debug = FALSE;
11040 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11042 #if defined(PLATFORM_ANDROID)
11043 si->fullscreen = TRUE;
11044 si->touch.overlay_buttons = TRUE;
11047 setHideSetupEntry(&setup.debug.xsn_mode);
11050 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11052 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11055 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11057 si->player_uuid = NULL; // (will be set later)
11058 si->player_version = 1; // (will be set later)
11060 si->use_api_server = TRUE;
11061 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11062 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11063 si->ask_for_uploading_tapes = TRUE;
11064 si->ask_for_remaining_tapes = FALSE;
11065 si->provide_uploading_tapes = TRUE;
11066 si->ask_for_using_api_server = TRUE;
11067 si->has_remaining_tapes = FALSE;
11070 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11072 si->editor_cascade.el_bd = TRUE;
11073 si->editor_cascade.el_bd_native = TRUE;
11074 si->editor_cascade.el_em = TRUE;
11075 si->editor_cascade.el_emc = TRUE;
11076 si->editor_cascade.el_rnd = TRUE;
11077 si->editor_cascade.el_sb = TRUE;
11078 si->editor_cascade.el_sp = TRUE;
11079 si->editor_cascade.el_dc = TRUE;
11080 si->editor_cascade.el_dx = TRUE;
11082 si->editor_cascade.el_mm = TRUE;
11083 si->editor_cascade.el_df = TRUE;
11085 si->editor_cascade.el_chars = FALSE;
11086 si->editor_cascade.el_steel_chars = FALSE;
11087 si->editor_cascade.el_ce = FALSE;
11088 si->editor_cascade.el_ge = FALSE;
11089 si->editor_cascade.el_es = FALSE;
11090 si->editor_cascade.el_ref = FALSE;
11091 si->editor_cascade.el_user = FALSE;
11092 si->editor_cascade.el_dynamic = FALSE;
11095 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11097 static char *getHideSetupToken(void *setup_value)
11099 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11101 if (setup_value != NULL)
11102 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11104 return hide_setup_token;
11107 void setHideSetupEntry(void *setup_value)
11109 char *hide_setup_token = getHideSetupToken(setup_value);
11111 if (hide_setup_hash == NULL)
11112 hide_setup_hash = newSetupFileHash();
11114 if (setup_value != NULL)
11115 setHashEntry(hide_setup_hash, hide_setup_token, "");
11118 void removeHideSetupEntry(void *setup_value)
11120 char *hide_setup_token = getHideSetupToken(setup_value);
11122 if (setup_value != NULL)
11123 removeHashEntry(hide_setup_hash, hide_setup_token);
11126 boolean hideSetupEntry(void *setup_value)
11128 char *hide_setup_token = getHideSetupToken(setup_value);
11130 return (setup_value != NULL &&
11131 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11134 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11135 struct TokenInfo *token_info,
11136 int token_nr, char *token_text)
11138 char *token_hide_text = getStringCat2(token_text, ".hide");
11139 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11141 // set the value of this setup option in the setup option structure
11142 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11144 // check if this setup option should be hidden in the setup menu
11145 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11146 setHideSetupEntry(token_info[token_nr].value);
11148 free(token_hide_text);
11151 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11152 struct TokenInfo *token_info,
11155 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11156 token_info[token_nr].text);
11159 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11163 if (!setup_file_hash)
11166 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11167 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11169 setup.touch.grid_initialized = TRUE;
11170 for (i = 0; i < 2; i++)
11172 int grid_xsize = setup.touch.grid_xsize[i];
11173 int grid_ysize = setup.touch.grid_ysize[i];
11176 // if virtual buttons are not loaded from setup file, repeat initializing
11177 // virtual buttons grid with default values later when video is initialized
11178 if (grid_xsize == -1 ||
11181 setup.touch.grid_initialized = FALSE;
11186 for (y = 0; y < grid_ysize; y++)
11188 char token_string[MAX_LINE_LEN];
11190 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11192 char *value_string = getHashEntry(setup_file_hash, token_string);
11194 if (value_string == NULL)
11197 for (x = 0; x < grid_xsize; x++)
11199 char c = value_string[x];
11201 setup.touch.grid_button[i][x][y] =
11202 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11207 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11208 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11210 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11211 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11213 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11217 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11219 setup_input = setup.input[pnr];
11220 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11222 char full_token[100];
11224 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11225 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11228 setup.input[pnr] = setup_input;
11231 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11232 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11234 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11235 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11237 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11238 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11240 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11241 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11243 setHideRelatedSetupEntries();
11246 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11250 if (!setup_file_hash)
11253 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11254 setSetupInfo(auto_setup_tokens, i,
11255 getHashEntry(setup_file_hash,
11256 auto_setup_tokens[i].text));
11259 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11263 if (!setup_file_hash)
11266 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11267 setSetupInfo(server_setup_tokens, i,
11268 getHashEntry(setup_file_hash,
11269 server_setup_tokens[i].text));
11272 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11276 if (!setup_file_hash)
11279 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11280 setSetupInfo(editor_cascade_setup_tokens, i,
11281 getHashEntry(setup_file_hash,
11282 editor_cascade_setup_tokens[i].text));
11285 void LoadUserNames(void)
11287 int last_user_nr = user.nr;
11290 if (global.user_names != NULL)
11292 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11293 checked_free(global.user_names[i]);
11295 checked_free(global.user_names);
11298 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11300 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11304 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11306 if (setup_file_hash)
11308 char *player_name = getHashEntry(setup_file_hash, "player_name");
11310 global.user_names[i] = getFixedUserName(player_name);
11312 freeSetupFileHash(setup_file_hash);
11315 if (global.user_names[i] == NULL)
11316 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11319 user.nr = last_user_nr;
11322 void LoadSetupFromFilename(char *filename)
11324 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11326 if (setup_file_hash)
11328 decodeSetupFileHash_Default(setup_file_hash);
11330 freeSetupFileHash(setup_file_hash);
11334 Debug("setup", "using default setup values");
11338 static void LoadSetup_SpecialPostProcessing(void)
11340 char *player_name_new;
11342 // needed to work around problems with fixed length strings
11343 player_name_new = getFixedUserName(setup.player_name);
11344 free(setup.player_name);
11345 setup.player_name = player_name_new;
11347 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11348 if (setup.scroll_delay == FALSE)
11350 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11351 setup.scroll_delay = TRUE; // now always "on"
11354 // make sure that scroll delay value stays inside valid range
11355 setup.scroll_delay_value =
11356 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11359 void LoadSetup_Default(void)
11363 // always start with reliable default values
11364 setSetupInfoToDefaults(&setup);
11366 // try to load setup values from default setup file
11367 filename = getDefaultSetupFilename();
11369 if (fileExists(filename))
11370 LoadSetupFromFilename(filename);
11372 // try to load setup values from platform setup file
11373 filename = getPlatformSetupFilename();
11375 if (fileExists(filename))
11376 LoadSetupFromFilename(filename);
11378 // try to load setup values from user setup file
11379 filename = getSetupFilename();
11381 LoadSetupFromFilename(filename);
11383 LoadSetup_SpecialPostProcessing();
11386 void LoadSetup_AutoSetup(void)
11388 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11389 SetupFileHash *setup_file_hash = NULL;
11391 // always start with reliable default values
11392 setSetupInfoToDefaults_AutoSetup(&setup);
11394 setup_file_hash = loadSetupFileHash(filename);
11396 if (setup_file_hash)
11398 decodeSetupFileHash_AutoSetup(setup_file_hash);
11400 freeSetupFileHash(setup_file_hash);
11406 void LoadSetup_ServerSetup(void)
11408 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11409 SetupFileHash *setup_file_hash = NULL;
11411 // always start with reliable default values
11412 setSetupInfoToDefaults_ServerSetup(&setup);
11414 setup_file_hash = loadSetupFileHash(filename);
11416 if (setup_file_hash)
11418 decodeSetupFileHash_ServerSetup(setup_file_hash);
11420 freeSetupFileHash(setup_file_hash);
11425 if (setup.player_uuid == NULL)
11427 // player UUID does not yet exist in setup file
11428 setup.player_uuid = getStringCopy(getUUID());
11429 setup.player_version = 2;
11431 SaveSetup_ServerSetup();
11435 void LoadSetup_EditorCascade(void)
11437 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11438 SetupFileHash *setup_file_hash = NULL;
11440 // always start with reliable default values
11441 setSetupInfoToDefaults_EditorCascade(&setup);
11443 setup_file_hash = loadSetupFileHash(filename);
11445 if (setup_file_hash)
11447 decodeSetupFileHash_EditorCascade(setup_file_hash);
11449 freeSetupFileHash(setup_file_hash);
11455 void LoadSetup(void)
11457 LoadSetup_Default();
11458 LoadSetup_AutoSetup();
11459 LoadSetup_ServerSetup();
11460 LoadSetup_EditorCascade();
11463 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11464 char *mapping_line)
11466 char mapping_guid[MAX_LINE_LEN];
11467 char *mapping_start, *mapping_end;
11469 // get GUID from game controller mapping line: copy complete line
11470 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11471 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11473 // get GUID from game controller mapping line: cut after GUID part
11474 mapping_start = strchr(mapping_guid, ',');
11475 if (mapping_start != NULL)
11476 *mapping_start = '\0';
11478 // cut newline from game controller mapping line
11479 mapping_end = strchr(mapping_line, '\n');
11480 if (mapping_end != NULL)
11481 *mapping_end = '\0';
11483 // add mapping entry to game controller mappings hash
11484 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11487 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11492 if (!(file = fopen(filename, MODE_READ)))
11494 Warn("cannot read game controller mappings file '%s'", filename);
11499 while (!feof(file))
11501 char line[MAX_LINE_LEN];
11503 if (!fgets(line, MAX_LINE_LEN, file))
11506 addGameControllerMappingToHash(mappings_hash, line);
11512 void SaveSetup_Default(void)
11514 char *filename = getSetupFilename();
11518 InitUserDataDirectory();
11520 if (!(file = fopen(filename, MODE_WRITE)))
11522 Warn("cannot write setup file '%s'", filename);
11527 fprintFileHeader(file, SETUP_FILENAME);
11529 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11531 // just to make things nicer :)
11532 if (global_setup_tokens[i].value == &setup.multiple_users ||
11533 global_setup_tokens[i].value == &setup.sound ||
11534 global_setup_tokens[i].value == &setup.graphics_set ||
11535 global_setup_tokens[i].value == &setup.volume_simple ||
11536 global_setup_tokens[i].value == &setup.network_mode ||
11537 global_setup_tokens[i].value == &setup.touch.control_type ||
11538 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11539 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11540 fprintf(file, "\n");
11542 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11545 for (i = 0; i < 2; i++)
11547 int grid_xsize = setup.touch.grid_xsize[i];
11548 int grid_ysize = setup.touch.grid_ysize[i];
11551 fprintf(file, "\n");
11553 for (y = 0; y < grid_ysize; y++)
11555 char token_string[MAX_LINE_LEN];
11556 char value_string[MAX_LINE_LEN];
11558 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11560 for (x = 0; x < grid_xsize; x++)
11562 char c = setup.touch.grid_button[i][x][y];
11564 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11567 value_string[grid_xsize] = '\0';
11569 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11573 fprintf(file, "\n");
11574 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11575 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11577 fprintf(file, "\n");
11578 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11579 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11581 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11585 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11586 fprintf(file, "\n");
11588 setup_input = setup.input[pnr];
11589 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11590 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11593 fprintf(file, "\n");
11594 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11595 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11597 // (internal setup values not saved to user setup file)
11599 fprintf(file, "\n");
11600 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11601 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11602 setup.debug.xsn_mode != AUTO)
11603 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11605 fprintf(file, "\n");
11606 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11607 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11611 SetFilePermissions(filename, PERMS_PRIVATE);
11614 void SaveSetup_AutoSetup(void)
11616 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11620 InitUserDataDirectory();
11622 if (!(file = fopen(filename, MODE_WRITE)))
11624 Warn("cannot write auto setup file '%s'", filename);
11631 fprintFileHeader(file, AUTOSETUP_FILENAME);
11633 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11634 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11638 SetFilePermissions(filename, PERMS_PRIVATE);
11643 void SaveSetup_ServerSetup(void)
11645 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11649 InitUserDataDirectory();
11651 if (!(file = fopen(filename, MODE_WRITE)))
11653 Warn("cannot write server setup file '%s'", filename);
11660 fprintFileHeader(file, SERVERSETUP_FILENAME);
11662 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11664 // just to make things nicer :)
11665 if (server_setup_tokens[i].value == &setup.use_api_server)
11666 fprintf(file, "\n");
11668 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11673 SetFilePermissions(filename, PERMS_PRIVATE);
11678 void SaveSetup_EditorCascade(void)
11680 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11684 InitUserDataDirectory();
11686 if (!(file = fopen(filename, MODE_WRITE)))
11688 Warn("cannot write editor cascade state file '%s'", filename);
11695 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11697 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11698 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11702 SetFilePermissions(filename, PERMS_PRIVATE);
11707 void SaveSetup(void)
11709 SaveSetup_Default();
11710 SaveSetup_AutoSetup();
11711 SaveSetup_ServerSetup();
11712 SaveSetup_EditorCascade();
11715 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11720 if (!(file = fopen(filename, MODE_WRITE)))
11722 Warn("cannot write game controller mappings file '%s'", filename);
11727 BEGIN_HASH_ITERATION(mappings_hash, itr)
11729 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11731 END_HASH_ITERATION(mappings_hash, itr)
11736 void SaveSetup_AddGameControllerMapping(char *mapping)
11738 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11739 SetupFileHash *mappings_hash = newSetupFileHash();
11741 InitUserDataDirectory();
11743 // load existing personal game controller mappings
11744 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11746 // add new mapping to personal game controller mappings
11747 addGameControllerMappingToHash(mappings_hash, mapping);
11749 // save updated personal game controller mappings
11750 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11752 freeSetupFileHash(mappings_hash);
11756 void LoadCustomElementDescriptions(void)
11758 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11759 SetupFileHash *setup_file_hash;
11762 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11764 if (element_info[i].custom_description != NULL)
11766 free(element_info[i].custom_description);
11767 element_info[i].custom_description = NULL;
11771 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11774 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11776 char *token = getStringCat2(element_info[i].token_name, ".name");
11777 char *value = getHashEntry(setup_file_hash, token);
11780 element_info[i].custom_description = getStringCopy(value);
11785 freeSetupFileHash(setup_file_hash);
11788 static int getElementFromToken(char *token)
11790 char *value = getHashEntry(element_token_hash, token);
11793 return atoi(value);
11795 Warn("unknown element token '%s'", token);
11797 return EL_UNDEFINED;
11800 void FreeGlobalAnimEventInfo(void)
11802 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11804 if (gaei->event_list == NULL)
11809 for (i = 0; i < gaei->num_event_lists; i++)
11811 checked_free(gaei->event_list[i]->event_value);
11812 checked_free(gaei->event_list[i]);
11815 checked_free(gaei->event_list);
11817 gaei->event_list = NULL;
11818 gaei->num_event_lists = 0;
11821 static int AddGlobalAnimEventList(void)
11823 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11824 int list_pos = gaei->num_event_lists++;
11826 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11827 sizeof(struct GlobalAnimEventListInfo *));
11829 gaei->event_list[list_pos] =
11830 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11832 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11834 gaeli->event_value = NULL;
11835 gaeli->num_event_values = 0;
11840 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11842 // do not add empty global animation events
11843 if (event_value == ANIM_EVENT_NONE)
11846 // if list position is undefined, create new list
11847 if (list_pos == ANIM_EVENT_UNDEFINED)
11848 list_pos = AddGlobalAnimEventList();
11850 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11851 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11852 int value_pos = gaeli->num_event_values++;
11854 gaeli->event_value = checked_realloc(gaeli->event_value,
11855 gaeli->num_event_values * sizeof(int *));
11857 gaeli->event_value[value_pos] = event_value;
11862 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11864 if (list_pos == ANIM_EVENT_UNDEFINED)
11865 return ANIM_EVENT_NONE;
11867 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11868 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11870 return gaeli->event_value[value_pos];
11873 int GetGlobalAnimEventValueCount(int list_pos)
11875 if (list_pos == ANIM_EVENT_UNDEFINED)
11878 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11879 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11881 return gaeli->num_event_values;
11884 // This function checks if a string <s> of the format "string1, string2, ..."
11885 // exactly contains a string <s_contained>.
11887 static boolean string_has_parameter(char *s, char *s_contained)
11891 if (s == NULL || s_contained == NULL)
11894 if (strlen(s_contained) > strlen(s))
11897 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11899 char next_char = s[strlen(s_contained)];
11901 // check if next character is delimiter or whitespace
11902 if (next_char == ',' || next_char == '\0' ||
11903 next_char == ' ' || next_char == '\t')
11907 // check if string contains another parameter string after a comma
11908 substring = strchr(s, ',');
11909 if (substring == NULL) // string does not contain a comma
11912 // advance string pointer to next character after the comma
11915 // skip potential whitespaces after the comma
11916 while (*substring == ' ' || *substring == '\t')
11919 return string_has_parameter(substring, s_contained);
11922 static int get_anim_parameter_value_ce(char *s)
11925 char *pattern_1 = "ce_change:custom_";
11926 char *pattern_2 = ".page_";
11927 int pattern_1_len = strlen(pattern_1);
11928 char *matching_char = strstr(s_ptr, pattern_1);
11929 int result = ANIM_EVENT_NONE;
11931 if (matching_char == NULL)
11932 return ANIM_EVENT_NONE;
11934 result = ANIM_EVENT_CE_CHANGE;
11936 s_ptr = matching_char + pattern_1_len;
11938 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11939 if (*s_ptr >= '0' && *s_ptr <= '9')
11941 int gic_ce_nr = (*s_ptr++ - '0');
11943 if (*s_ptr >= '0' && *s_ptr <= '9')
11945 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11947 if (*s_ptr >= '0' && *s_ptr <= '9')
11948 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11951 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11952 return ANIM_EVENT_NONE;
11954 // custom element stored as 0 to 255
11957 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11961 // invalid custom element number specified
11963 return ANIM_EVENT_NONE;
11966 // check for change page number ("page_X" or "page_XX") (optional)
11967 if (strPrefix(s_ptr, pattern_2))
11969 s_ptr += strlen(pattern_2);
11971 if (*s_ptr >= '0' && *s_ptr <= '9')
11973 int gic_page_nr = (*s_ptr++ - '0');
11975 if (*s_ptr >= '0' && *s_ptr <= '9')
11976 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11978 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11979 return ANIM_EVENT_NONE;
11981 // change page stored as 1 to 32 (0 means "all change pages")
11983 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11987 // invalid animation part number specified
11989 return ANIM_EVENT_NONE;
11993 // discard result if next character is neither delimiter nor whitespace
11994 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11995 *s_ptr == ' ' || *s_ptr == '\t'))
11996 return ANIM_EVENT_NONE;
12001 static int get_anim_parameter_value(char *s)
12003 int event_value[] =
12011 char *pattern_1[] =
12019 char *pattern_2 = ".part_";
12020 char *matching_char = NULL;
12022 int pattern_1_len = 0;
12023 int result = ANIM_EVENT_NONE;
12026 result = get_anim_parameter_value_ce(s);
12028 if (result != ANIM_EVENT_NONE)
12031 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12033 matching_char = strstr(s_ptr, pattern_1[i]);
12034 pattern_1_len = strlen(pattern_1[i]);
12035 result = event_value[i];
12037 if (matching_char != NULL)
12041 if (matching_char == NULL)
12042 return ANIM_EVENT_NONE;
12044 s_ptr = matching_char + pattern_1_len;
12046 // check for main animation number ("anim_X" or "anim_XX")
12047 if (*s_ptr >= '0' && *s_ptr <= '9')
12049 int gic_anim_nr = (*s_ptr++ - '0');
12051 if (*s_ptr >= '0' && *s_ptr <= '9')
12052 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12054 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12055 return ANIM_EVENT_NONE;
12057 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12061 // invalid main animation number specified
12063 return ANIM_EVENT_NONE;
12066 // check for animation part number ("part_X" or "part_XX") (optional)
12067 if (strPrefix(s_ptr, pattern_2))
12069 s_ptr += strlen(pattern_2);
12071 if (*s_ptr >= '0' && *s_ptr <= '9')
12073 int gic_part_nr = (*s_ptr++ - '0');
12075 if (*s_ptr >= '0' && *s_ptr <= '9')
12076 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12078 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12079 return ANIM_EVENT_NONE;
12081 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12085 // invalid animation part number specified
12087 return ANIM_EVENT_NONE;
12091 // discard result if next character is neither delimiter nor whitespace
12092 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12093 *s_ptr == ' ' || *s_ptr == '\t'))
12094 return ANIM_EVENT_NONE;
12099 static int get_anim_parameter_values(char *s)
12101 int list_pos = ANIM_EVENT_UNDEFINED;
12102 int event_value = ANIM_EVENT_DEFAULT;
12104 if (string_has_parameter(s, "any"))
12105 event_value |= ANIM_EVENT_ANY;
12107 if (string_has_parameter(s, "click:self") ||
12108 string_has_parameter(s, "click") ||
12109 string_has_parameter(s, "self"))
12110 event_value |= ANIM_EVENT_SELF;
12112 if (string_has_parameter(s, "unclick:any"))
12113 event_value |= ANIM_EVENT_UNCLICK_ANY;
12115 // if animation event found, add it to global animation event list
12116 if (event_value != ANIM_EVENT_NONE)
12117 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12121 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12122 event_value = get_anim_parameter_value(s);
12124 // if animation event found, add it to global animation event list
12125 if (event_value != ANIM_EVENT_NONE)
12126 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12128 // continue with next part of the string, starting with next comma
12129 s = strchr(s + 1, ',');
12135 static int get_anim_action_parameter_value(char *token)
12137 // check most common default case first to massively speed things up
12138 if (strEqual(token, ARG_UNDEFINED))
12139 return ANIM_EVENT_ACTION_NONE;
12141 int result = getImageIDFromToken(token);
12145 char *gfx_token = getStringCat2("gfx.", token);
12147 result = getImageIDFromToken(gfx_token);
12149 checked_free(gfx_token);
12154 Key key = getKeyFromX11KeyName(token);
12156 if (key != KSYM_UNDEFINED)
12157 result = -(int)key;
12164 result = get_hash_from_string(token); // unsigned int => int
12165 result = ABS(result); // may be negative now
12166 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12168 setHashEntry(anim_url_hash, int2str(result, 0), token);
12173 result = ANIM_EVENT_ACTION_NONE;
12178 int get_parameter_value(char *value_raw, char *suffix, int type)
12180 char *value = getStringToLower(value_raw);
12181 int result = 0; // probably a save default value
12183 if (strEqual(suffix, ".direction"))
12185 result = (strEqual(value, "left") ? MV_LEFT :
12186 strEqual(value, "right") ? MV_RIGHT :
12187 strEqual(value, "up") ? MV_UP :
12188 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12190 else if (strEqual(suffix, ".position"))
12192 result = (strEqual(value, "left") ? POS_LEFT :
12193 strEqual(value, "right") ? POS_RIGHT :
12194 strEqual(value, "top") ? POS_TOP :
12195 strEqual(value, "upper") ? POS_UPPER :
12196 strEqual(value, "middle") ? POS_MIDDLE :
12197 strEqual(value, "lower") ? POS_LOWER :
12198 strEqual(value, "bottom") ? POS_BOTTOM :
12199 strEqual(value, "any") ? POS_ANY :
12200 strEqual(value, "ce") ? POS_CE :
12201 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12202 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12204 else if (strEqual(suffix, ".align"))
12206 result = (strEqual(value, "left") ? ALIGN_LEFT :
12207 strEqual(value, "right") ? ALIGN_RIGHT :
12208 strEqual(value, "center") ? ALIGN_CENTER :
12209 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12211 else if (strEqual(suffix, ".valign"))
12213 result = (strEqual(value, "top") ? VALIGN_TOP :
12214 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12215 strEqual(value, "middle") ? VALIGN_MIDDLE :
12216 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12218 else if (strEqual(suffix, ".anim_mode"))
12220 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12221 string_has_parameter(value, "loop") ? ANIM_LOOP :
12222 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12223 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12224 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12225 string_has_parameter(value, "random") ? ANIM_RANDOM :
12226 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12227 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12228 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12229 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12230 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12231 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12232 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12233 string_has_parameter(value, "all") ? ANIM_ALL :
12234 string_has_parameter(value, "tiled") ? ANIM_TILED :
12235 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12238 if (string_has_parameter(value, "once"))
12239 result |= ANIM_ONCE;
12241 if (string_has_parameter(value, "reverse"))
12242 result |= ANIM_REVERSE;
12244 if (string_has_parameter(value, "opaque_player"))
12245 result |= ANIM_OPAQUE_PLAYER;
12247 if (string_has_parameter(value, "static_panel"))
12248 result |= ANIM_STATIC_PANEL;
12250 else if (strEqual(suffix, ".init_event") ||
12251 strEqual(suffix, ".anim_event"))
12253 result = get_anim_parameter_values(value);
12255 else if (strEqual(suffix, ".init_delay_action") ||
12256 strEqual(suffix, ".anim_delay_action") ||
12257 strEqual(suffix, ".post_delay_action") ||
12258 strEqual(suffix, ".init_event_action") ||
12259 strEqual(suffix, ".anim_event_action"))
12261 result = get_anim_action_parameter_value(value_raw);
12263 else if (strEqual(suffix, ".class"))
12265 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12266 get_hash_from_string(value));
12268 else if (strEqual(suffix, ".style"))
12270 result = STYLE_DEFAULT;
12272 if (string_has_parameter(value, "accurate_borders"))
12273 result |= STYLE_ACCURATE_BORDERS;
12275 if (string_has_parameter(value, "inner_corners"))
12276 result |= STYLE_INNER_CORNERS;
12278 if (string_has_parameter(value, "reverse"))
12279 result |= STYLE_REVERSE;
12281 if (string_has_parameter(value, "leftmost_position"))
12282 result |= STYLE_LEFTMOST_POSITION;
12284 if (string_has_parameter(value, "block_clicks"))
12285 result |= STYLE_BLOCK;
12287 if (string_has_parameter(value, "passthrough_clicks"))
12288 result |= STYLE_PASSTHROUGH;
12290 if (string_has_parameter(value, "multiple_actions"))
12291 result |= STYLE_MULTIPLE_ACTIONS;
12293 if (string_has_parameter(value, "consume_ce_event"))
12294 result |= STYLE_CONSUME_CE_EVENT;
12296 else if (strEqual(suffix, ".fade_mode"))
12298 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12299 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12300 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12301 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12302 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12303 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12304 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12305 FADE_MODE_DEFAULT);
12307 else if (strEqual(suffix, ".auto_delay_unit"))
12309 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12310 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12311 AUTO_DELAY_UNIT_DEFAULT);
12313 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12315 result = gfx.get_font_from_token_function(value);
12317 else // generic parameter of type integer or boolean
12319 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12320 type == TYPE_INTEGER ? get_integer_from_string(value) :
12321 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12322 ARG_UNDEFINED_VALUE);
12330 static int get_token_parameter_value(char *token, char *value_raw)
12334 if (token == NULL || value_raw == NULL)
12335 return ARG_UNDEFINED_VALUE;
12337 suffix = strrchr(token, '.');
12338 if (suffix == NULL)
12341 if (strEqual(suffix, ".element"))
12342 return getElementFromToken(value_raw);
12344 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12345 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12348 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12349 boolean ignore_defaults)
12353 for (i = 0; image_config_vars[i].token != NULL; i++)
12355 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12357 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12358 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12362 *image_config_vars[i].value =
12363 get_token_parameter_value(image_config_vars[i].token, value);
12367 void InitMenuDesignSettings_Static(void)
12369 // always start with reliable default values from static default config
12370 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12373 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12377 // the following initializes hierarchical values from static configuration
12379 // special case: initialize "ARG_DEFAULT" values in static default config
12380 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12381 titlescreen_initial_first_default.fade_mode =
12382 title_initial_first_default.fade_mode;
12383 titlescreen_initial_first_default.fade_delay =
12384 title_initial_first_default.fade_delay;
12385 titlescreen_initial_first_default.post_delay =
12386 title_initial_first_default.post_delay;
12387 titlescreen_initial_first_default.auto_delay =
12388 title_initial_first_default.auto_delay;
12389 titlescreen_initial_first_default.auto_delay_unit =
12390 title_initial_first_default.auto_delay_unit;
12391 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12392 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12393 titlescreen_first_default.post_delay = title_first_default.post_delay;
12394 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12395 titlescreen_first_default.auto_delay_unit =
12396 title_first_default.auto_delay_unit;
12397 titlemessage_initial_first_default.fade_mode =
12398 title_initial_first_default.fade_mode;
12399 titlemessage_initial_first_default.fade_delay =
12400 title_initial_first_default.fade_delay;
12401 titlemessage_initial_first_default.post_delay =
12402 title_initial_first_default.post_delay;
12403 titlemessage_initial_first_default.auto_delay =
12404 title_initial_first_default.auto_delay;
12405 titlemessage_initial_first_default.auto_delay_unit =
12406 title_initial_first_default.auto_delay_unit;
12407 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12408 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12409 titlemessage_first_default.post_delay = title_first_default.post_delay;
12410 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12411 titlemessage_first_default.auto_delay_unit =
12412 title_first_default.auto_delay_unit;
12414 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12415 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12416 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12417 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12418 titlescreen_initial_default.auto_delay_unit =
12419 title_initial_default.auto_delay_unit;
12420 titlescreen_default.fade_mode = title_default.fade_mode;
12421 titlescreen_default.fade_delay = title_default.fade_delay;
12422 titlescreen_default.post_delay = title_default.post_delay;
12423 titlescreen_default.auto_delay = title_default.auto_delay;
12424 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12425 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12426 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12427 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12428 titlemessage_initial_default.auto_delay_unit =
12429 title_initial_default.auto_delay_unit;
12430 titlemessage_default.fade_mode = title_default.fade_mode;
12431 titlemessage_default.fade_delay = title_default.fade_delay;
12432 titlemessage_default.post_delay = title_default.post_delay;
12433 titlemessage_default.auto_delay = title_default.auto_delay;
12434 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12436 // special case: initialize "ARG_DEFAULT" values in static default config
12437 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12438 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12440 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12441 titlescreen_first[i] = titlescreen_first_default;
12442 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12443 titlemessage_first[i] = titlemessage_first_default;
12445 titlescreen_initial[i] = titlescreen_initial_default;
12446 titlescreen[i] = titlescreen_default;
12447 titlemessage_initial[i] = titlemessage_initial_default;
12448 titlemessage[i] = titlemessage_default;
12451 // special case: initialize "ARG_DEFAULT" values in static default config
12452 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12453 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12455 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12458 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12459 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12460 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12463 // special case: initialize "ARG_DEFAULT" values in static default config
12464 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12465 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12467 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12468 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12469 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12471 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12474 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12478 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12482 struct XY *dst, *src;
12484 game_buttons_xy[] =
12486 { &game.button.save, &game.button.stop },
12487 { &game.button.pause2, &game.button.pause },
12488 { &game.button.load, &game.button.play },
12489 { &game.button.undo, &game.button.stop },
12490 { &game.button.redo, &game.button.play },
12496 // special case: initialize later added SETUP list size from LEVELS value
12497 if (menu.list_size[GAME_MODE_SETUP] == -1)
12498 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12500 // set default position for snapshot buttons to stop/pause/play buttons
12501 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12502 if ((*game_buttons_xy[i].dst).x == -1 &&
12503 (*game_buttons_xy[i].dst).y == -1)
12504 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12506 // --------------------------------------------------------------------------
12507 // dynamic viewports (including playfield margins, borders and alignments)
12508 // --------------------------------------------------------------------------
12510 // dynamic viewports currently only supported for landscape mode
12511 int display_width = MAX(video.display_width, video.display_height);
12512 int display_height = MIN(video.display_width, video.display_height);
12514 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12516 struct RectWithBorder *vp_window = &viewport.window[i];
12517 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12518 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12519 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12520 boolean dynamic_window_width = (vp_window->min_width != -1);
12521 boolean dynamic_window_height = (vp_window->min_height != -1);
12522 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12523 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12525 // adjust window size if min/max width/height is specified
12527 if (vp_window->min_width != -1)
12529 int window_width = display_width;
12531 // when using static window height, use aspect ratio of display
12532 if (vp_window->min_height == -1)
12533 window_width = vp_window->height * display_width / display_height;
12535 vp_window->width = MAX(vp_window->min_width, window_width);
12538 if (vp_window->min_height != -1)
12540 int window_height = display_height;
12542 // when using static window width, use aspect ratio of display
12543 if (vp_window->min_width == -1)
12544 window_height = vp_window->width * display_height / display_width;
12546 vp_window->height = MAX(vp_window->min_height, window_height);
12549 if (vp_window->max_width != -1)
12550 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12552 if (vp_window->max_height != -1)
12553 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12555 int playfield_width = vp_window->width;
12556 int playfield_height = vp_window->height;
12558 // adjust playfield size and position according to specified margins
12560 playfield_width -= vp_playfield->margin_left;
12561 playfield_width -= vp_playfield->margin_right;
12563 playfield_height -= vp_playfield->margin_top;
12564 playfield_height -= vp_playfield->margin_bottom;
12566 // adjust playfield size if min/max width/height is specified
12568 if (vp_playfield->min_width != -1)
12569 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12571 if (vp_playfield->min_height != -1)
12572 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12574 if (vp_playfield->max_width != -1)
12575 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12577 if (vp_playfield->max_height != -1)
12578 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12580 // adjust playfield position according to specified alignment
12582 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12583 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12584 else if (vp_playfield->align == ALIGN_CENTER)
12585 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12586 else if (vp_playfield->align == ALIGN_RIGHT)
12587 vp_playfield->x += playfield_width - vp_playfield->width;
12589 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12590 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12591 else if (vp_playfield->valign == VALIGN_MIDDLE)
12592 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12593 else if (vp_playfield->valign == VALIGN_BOTTOM)
12594 vp_playfield->y += playfield_height - vp_playfield->height;
12596 vp_playfield->x += vp_playfield->margin_left;
12597 vp_playfield->y += vp_playfield->margin_top;
12599 // adjust individual playfield borders if only default border is specified
12601 if (vp_playfield->border_left == -1)
12602 vp_playfield->border_left = vp_playfield->border_size;
12603 if (vp_playfield->border_right == -1)
12604 vp_playfield->border_right = vp_playfield->border_size;
12605 if (vp_playfield->border_top == -1)
12606 vp_playfield->border_top = vp_playfield->border_size;
12607 if (vp_playfield->border_bottom == -1)
12608 vp_playfield->border_bottom = vp_playfield->border_size;
12610 // set dynamic playfield borders if borders are specified as undefined
12611 // (but only if window size was dynamic and playfield size was static)
12613 if (dynamic_window_width && !dynamic_playfield_width)
12615 if (vp_playfield->border_left == -1)
12617 vp_playfield->border_left = (vp_playfield->x -
12618 vp_playfield->margin_left);
12619 vp_playfield->x -= vp_playfield->border_left;
12620 vp_playfield->width += vp_playfield->border_left;
12623 if (vp_playfield->border_right == -1)
12625 vp_playfield->border_right = (vp_window->width -
12627 vp_playfield->width -
12628 vp_playfield->margin_right);
12629 vp_playfield->width += vp_playfield->border_right;
12633 if (dynamic_window_height && !dynamic_playfield_height)
12635 if (vp_playfield->border_top == -1)
12637 vp_playfield->border_top = (vp_playfield->y -
12638 vp_playfield->margin_top);
12639 vp_playfield->y -= vp_playfield->border_top;
12640 vp_playfield->height += vp_playfield->border_top;
12643 if (vp_playfield->border_bottom == -1)
12645 vp_playfield->border_bottom = (vp_window->height -
12647 vp_playfield->height -
12648 vp_playfield->margin_bottom);
12649 vp_playfield->height += vp_playfield->border_bottom;
12653 // adjust playfield size to be a multiple of a defined alignment tile size
12655 int align_size = vp_playfield->align_size;
12656 int playfield_xtiles = vp_playfield->width / align_size;
12657 int playfield_ytiles = vp_playfield->height / align_size;
12658 int playfield_width_corrected = playfield_xtiles * align_size;
12659 int playfield_height_corrected = playfield_ytiles * align_size;
12660 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12661 i == GFX_SPECIAL_ARG_EDITOR);
12663 if (is_playfield_mode &&
12664 dynamic_playfield_width &&
12665 vp_playfield->width != playfield_width_corrected)
12667 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12669 vp_playfield->width = playfield_width_corrected;
12671 if (vp_playfield->align == ALIGN_LEFT)
12673 vp_playfield->border_left += playfield_xdiff;
12675 else if (vp_playfield->align == ALIGN_RIGHT)
12677 vp_playfield->border_right += playfield_xdiff;
12679 else if (vp_playfield->align == ALIGN_CENTER)
12681 int border_left_diff = playfield_xdiff / 2;
12682 int border_right_diff = playfield_xdiff - border_left_diff;
12684 vp_playfield->border_left += border_left_diff;
12685 vp_playfield->border_right += border_right_diff;
12689 if (is_playfield_mode &&
12690 dynamic_playfield_height &&
12691 vp_playfield->height != playfield_height_corrected)
12693 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12695 vp_playfield->height = playfield_height_corrected;
12697 if (vp_playfield->valign == VALIGN_TOP)
12699 vp_playfield->border_top += playfield_ydiff;
12701 else if (vp_playfield->align == VALIGN_BOTTOM)
12703 vp_playfield->border_right += playfield_ydiff;
12705 else if (vp_playfield->align == VALIGN_MIDDLE)
12707 int border_top_diff = playfield_ydiff / 2;
12708 int border_bottom_diff = playfield_ydiff - border_top_diff;
12710 vp_playfield->border_top += border_top_diff;
12711 vp_playfield->border_bottom += border_bottom_diff;
12715 // adjust door positions according to specified alignment
12717 for (j = 0; j < 2; j++)
12719 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12721 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12722 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12723 else if (vp_door->align == ALIGN_CENTER)
12724 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12725 else if (vp_door->align == ALIGN_RIGHT)
12726 vp_door->x += vp_window->width - vp_door->width;
12728 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12729 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12730 else if (vp_door->valign == VALIGN_MIDDLE)
12731 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12732 else if (vp_door->valign == VALIGN_BOTTOM)
12733 vp_door->y += vp_window->height - vp_door->height;
12738 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12742 struct XYTileSize *dst, *src;
12745 editor_buttons_xy[] =
12748 &editor.button.element_left, &editor.palette.element_left,
12749 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12752 &editor.button.element_middle, &editor.palette.element_middle,
12753 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12756 &editor.button.element_right, &editor.palette.element_right,
12757 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12764 // set default position for element buttons to element graphics
12765 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12767 if ((*editor_buttons_xy[i].dst).x == -1 &&
12768 (*editor_buttons_xy[i].dst).y == -1)
12770 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12772 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12774 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12778 // adjust editor palette rows and columns if specified to be dynamic
12780 if (editor.palette.cols == -1)
12782 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12783 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12784 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12786 editor.palette.cols = (vp_width - sc_width) / bt_width;
12788 if (editor.palette.x == -1)
12790 int palette_width = editor.palette.cols * bt_width + sc_width;
12792 editor.palette.x = (vp_width - palette_width) / 2;
12796 if (editor.palette.rows == -1)
12798 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12799 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12800 int tx_height = getFontHeight(FONT_TEXT_2);
12802 editor.palette.rows = (vp_height - tx_height) / bt_height;
12804 if (editor.palette.y == -1)
12806 int palette_height = editor.palette.rows * bt_height + tx_height;
12808 editor.palette.y = (vp_height - palette_height) / 2;
12813 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12814 boolean initialize)
12816 // special case: check if network and preview player positions are redefined,
12817 // to compare this later against the main menu level preview being redefined
12818 struct TokenIntPtrInfo menu_config_players[] =
12820 { "main.network_players.x", &menu.main.network_players.redefined },
12821 { "main.network_players.y", &menu.main.network_players.redefined },
12822 { "main.preview_players.x", &menu.main.preview_players.redefined },
12823 { "main.preview_players.y", &menu.main.preview_players.redefined },
12824 { "preview.x", &preview.redefined },
12825 { "preview.y", &preview.redefined }
12831 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12832 *menu_config_players[i].value = FALSE;
12836 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12837 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12838 *menu_config_players[i].value = TRUE;
12842 static void InitMenuDesignSettings_PreviewPlayers(void)
12844 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12847 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12849 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12852 static void LoadMenuDesignSettingsFromFilename(char *filename)
12854 static struct TitleFadingInfo tfi;
12855 static struct TitleMessageInfo tmi;
12856 static struct TokenInfo title_tokens[] =
12858 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12859 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12860 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12861 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12862 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12866 static struct TokenInfo titlemessage_tokens[] =
12868 { TYPE_INTEGER, &tmi.x, ".x" },
12869 { TYPE_INTEGER, &tmi.y, ".y" },
12870 { TYPE_INTEGER, &tmi.width, ".width" },
12871 { TYPE_INTEGER, &tmi.height, ".height" },
12872 { TYPE_INTEGER, &tmi.chars, ".chars" },
12873 { TYPE_INTEGER, &tmi.lines, ".lines" },
12874 { TYPE_INTEGER, &tmi.align, ".align" },
12875 { TYPE_INTEGER, &tmi.valign, ".valign" },
12876 { TYPE_INTEGER, &tmi.font, ".font" },
12877 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12878 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12879 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12880 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12881 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12882 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12883 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12884 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12885 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12891 struct TitleFadingInfo *info;
12896 // initialize first titles from "enter screen" definitions, if defined
12897 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12898 { &title_first_default, "menu.enter_screen.TITLE" },
12900 // initialize title screens from "next screen" definitions, if defined
12901 { &title_initial_default, "menu.next_screen.TITLE" },
12902 { &title_default, "menu.next_screen.TITLE" },
12908 struct TitleMessageInfo *array;
12911 titlemessage_arrays[] =
12913 // initialize first titles from "enter screen" definitions, if defined
12914 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12915 { titlescreen_first, "menu.enter_screen.TITLE" },
12916 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12917 { titlemessage_first, "menu.enter_screen.TITLE" },
12919 // initialize titles from "next screen" definitions, if defined
12920 { titlescreen_initial, "menu.next_screen.TITLE" },
12921 { titlescreen, "menu.next_screen.TITLE" },
12922 { titlemessage_initial, "menu.next_screen.TITLE" },
12923 { titlemessage, "menu.next_screen.TITLE" },
12925 // overwrite titles with title definitions, if defined
12926 { titlescreen_initial_first, "[title_initial]" },
12927 { titlescreen_first, "[title]" },
12928 { titlemessage_initial_first, "[title_initial]" },
12929 { titlemessage_first, "[title]" },
12931 { titlescreen_initial, "[title_initial]" },
12932 { titlescreen, "[title]" },
12933 { titlemessage_initial, "[title_initial]" },
12934 { titlemessage, "[title]" },
12936 // overwrite titles with title screen/message definitions, if defined
12937 { titlescreen_initial_first, "[titlescreen_initial]" },
12938 { titlescreen_first, "[titlescreen]" },
12939 { titlemessage_initial_first, "[titlemessage_initial]" },
12940 { titlemessage_first, "[titlemessage]" },
12942 { titlescreen_initial, "[titlescreen_initial]" },
12943 { titlescreen, "[titlescreen]" },
12944 { titlemessage_initial, "[titlemessage_initial]" },
12945 { titlemessage, "[titlemessage]" },
12949 SetupFileHash *setup_file_hash;
12952 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12955 // the following initializes hierarchical values from dynamic configuration
12957 // special case: initialize with default values that may be overwritten
12958 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12959 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12961 struct TokenIntPtrInfo menu_config[] =
12963 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12964 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12965 { "menu.list_size", &menu.list_size[i] }
12968 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12970 char *token = menu_config[j].token;
12971 char *value = getHashEntry(setup_file_hash, token);
12974 *menu_config[j].value = get_integer_from_string(value);
12978 // special case: initialize with default values that may be overwritten
12979 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12980 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12982 struct TokenIntPtrInfo menu_config[] =
12984 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12985 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12986 { "menu.list_size.INFO", &menu.list_size_info[i] },
12987 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12988 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12991 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12993 char *token = menu_config[j].token;
12994 char *value = getHashEntry(setup_file_hash, token);
12997 *menu_config[j].value = get_integer_from_string(value);
13001 // special case: initialize with default values that may be overwritten
13002 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13003 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13005 struct TokenIntPtrInfo menu_config[] =
13007 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13008 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13011 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13013 char *token = menu_config[j].token;
13014 char *value = getHashEntry(setup_file_hash, token);
13017 *menu_config[j].value = get_integer_from_string(value);
13021 // special case: initialize with default values that may be overwritten
13022 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13023 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13025 struct TokenIntPtrInfo menu_config[] =
13027 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13028 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13029 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13030 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13031 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13032 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13033 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13034 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13035 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13036 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13039 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13041 char *token = menu_config[j].token;
13042 char *value = getHashEntry(setup_file_hash, token);
13045 *menu_config[j].value = get_integer_from_string(value);
13049 // special case: initialize with default values that may be overwritten
13050 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13051 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13053 struct TokenIntPtrInfo menu_config[] =
13055 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13056 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13057 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13058 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13059 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13060 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13061 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13062 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13063 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13066 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13068 char *token = menu_config[j].token;
13069 char *value = getHashEntry(setup_file_hash, token);
13072 *menu_config[j].value = get_token_parameter_value(token, value);
13076 // special case: initialize with default values that may be overwritten
13077 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13078 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13082 char *token_prefix;
13083 struct RectWithBorder *struct_ptr;
13087 { "viewport.window", &viewport.window[i] },
13088 { "viewport.playfield", &viewport.playfield[i] },
13089 { "viewport.door_1", &viewport.door_1[i] },
13090 { "viewport.door_2", &viewport.door_2[i] }
13093 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13095 struct TokenIntPtrInfo vp_config[] =
13097 { ".x", &vp_struct[j].struct_ptr->x },
13098 { ".y", &vp_struct[j].struct_ptr->y },
13099 { ".width", &vp_struct[j].struct_ptr->width },
13100 { ".height", &vp_struct[j].struct_ptr->height },
13101 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13102 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13103 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13104 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13105 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13106 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13107 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13108 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13109 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13110 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13111 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13112 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13113 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13114 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13115 { ".align", &vp_struct[j].struct_ptr->align },
13116 { ".valign", &vp_struct[j].struct_ptr->valign }
13119 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13121 char *token = getStringCat2(vp_struct[j].token_prefix,
13122 vp_config[k].token);
13123 char *value = getHashEntry(setup_file_hash, token);
13126 *vp_config[k].value = get_token_parameter_value(token, value);
13133 // special case: initialize with default values that may be overwritten
13134 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13135 for (i = 0; title_info[i].info != NULL; i++)
13137 struct TitleFadingInfo *info = title_info[i].info;
13138 char *base_token = title_info[i].text;
13140 for (j = 0; title_tokens[j].type != -1; j++)
13142 char *token = getStringCat2(base_token, title_tokens[j].text);
13143 char *value = getHashEntry(setup_file_hash, token);
13147 int parameter_value = get_token_parameter_value(token, value);
13151 *(int *)title_tokens[j].value = (int)parameter_value;
13160 // special case: initialize with default values that may be overwritten
13161 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13162 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13164 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13165 char *base_token = titlemessage_arrays[i].text;
13167 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13169 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13170 char *value = getHashEntry(setup_file_hash, token);
13174 int parameter_value = get_token_parameter_value(token, value);
13176 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13180 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13181 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13183 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13193 // read (and overwrite with) values that may be specified in config file
13194 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13196 // special case: check if network and preview player positions are redefined
13197 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13199 freeSetupFileHash(setup_file_hash);
13202 void LoadMenuDesignSettings(void)
13204 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13206 InitMenuDesignSettings_Static();
13207 InitMenuDesignSettings_SpecialPreProcessing();
13208 InitMenuDesignSettings_PreviewPlayers();
13210 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13212 // first look for special settings configured in level series config
13213 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13215 if (fileExists(filename_base))
13216 LoadMenuDesignSettingsFromFilename(filename_base);
13219 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13221 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13222 LoadMenuDesignSettingsFromFilename(filename_local);
13224 InitMenuDesignSettings_SpecialPostProcessing();
13227 void LoadMenuDesignSettings_AfterGraphics(void)
13229 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13232 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13233 boolean ignore_defaults)
13237 for (i = 0; sound_config_vars[i].token != NULL; i++)
13239 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13241 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13242 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13246 *sound_config_vars[i].value =
13247 get_token_parameter_value(sound_config_vars[i].token, value);
13251 void InitSoundSettings_Static(void)
13253 // always start with reliable default values from static default config
13254 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13257 static void LoadSoundSettingsFromFilename(char *filename)
13259 SetupFileHash *setup_file_hash;
13261 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13264 // read (and overwrite with) values that may be specified in config file
13265 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13267 freeSetupFileHash(setup_file_hash);
13270 void LoadSoundSettings(void)
13272 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13274 InitSoundSettings_Static();
13276 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13278 // first look for special settings configured in level series config
13279 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13281 if (fileExists(filename_base))
13282 LoadSoundSettingsFromFilename(filename_base);
13285 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13287 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13288 LoadSoundSettingsFromFilename(filename_local);
13291 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13293 char *filename = getEditorSetupFilename();
13294 SetupFileList *setup_file_list, *list;
13295 SetupFileHash *element_hash;
13296 int num_unknown_tokens = 0;
13299 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13302 element_hash = newSetupFileHash();
13304 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13305 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13307 // determined size may be larger than needed (due to unknown elements)
13309 for (list = setup_file_list; list != NULL; list = list->next)
13312 // add space for up to 3 more elements for padding that may be needed
13313 *num_elements += 3;
13315 // free memory for old list of elements, if needed
13316 checked_free(*elements);
13318 // allocate memory for new list of elements
13319 *elements = checked_malloc(*num_elements * sizeof(int));
13322 for (list = setup_file_list; list != NULL; list = list->next)
13324 char *value = getHashEntry(element_hash, list->token);
13326 if (value == NULL) // try to find obsolete token mapping
13328 char *mapped_token = get_mapped_token(list->token);
13330 if (mapped_token != NULL)
13332 value = getHashEntry(element_hash, mapped_token);
13334 free(mapped_token);
13340 (*elements)[(*num_elements)++] = atoi(value);
13344 if (num_unknown_tokens == 0)
13347 Warn("unknown token(s) found in config file:");
13348 Warn("- config file: '%s'", filename);
13350 num_unknown_tokens++;
13353 Warn("- token: '%s'", list->token);
13357 if (num_unknown_tokens > 0)
13360 while (*num_elements % 4) // pad with empty elements, if needed
13361 (*elements)[(*num_elements)++] = EL_EMPTY;
13363 freeSetupFileList(setup_file_list);
13364 freeSetupFileHash(element_hash);
13367 for (i = 0; i < *num_elements; i++)
13368 Debug("editor", "element '%s' [%d]\n",
13369 element_info[(*elements)[i]].token_name, (*elements)[i]);
13373 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13376 SetupFileHash *setup_file_hash = NULL;
13377 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13378 char *filename_music, *filename_prefix, *filename_info;
13384 token_to_value_ptr[] =
13386 { "title_header", &tmp_music_file_info.title_header },
13387 { "artist_header", &tmp_music_file_info.artist_header },
13388 { "album_header", &tmp_music_file_info.album_header },
13389 { "year_header", &tmp_music_file_info.year_header },
13390 { "played_header", &tmp_music_file_info.played_header },
13392 { "title", &tmp_music_file_info.title },
13393 { "artist", &tmp_music_file_info.artist },
13394 { "album", &tmp_music_file_info.album },
13395 { "year", &tmp_music_file_info.year },
13396 { "played", &tmp_music_file_info.played },
13402 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13403 getCustomMusicFilename(basename));
13405 if (filename_music == NULL)
13408 // ---------- try to replace file extension ----------
13410 filename_prefix = getStringCopy(filename_music);
13411 if (strrchr(filename_prefix, '.') != NULL)
13412 *strrchr(filename_prefix, '.') = '\0';
13413 filename_info = getStringCat2(filename_prefix, ".txt");
13415 if (fileExists(filename_info))
13416 setup_file_hash = loadSetupFileHash(filename_info);
13418 free(filename_prefix);
13419 free(filename_info);
13421 if (setup_file_hash == NULL)
13423 // ---------- try to add file extension ----------
13425 filename_prefix = getStringCopy(filename_music);
13426 filename_info = getStringCat2(filename_prefix, ".txt");
13428 if (fileExists(filename_info))
13429 setup_file_hash = loadSetupFileHash(filename_info);
13431 free(filename_prefix);
13432 free(filename_info);
13435 if (setup_file_hash == NULL)
13438 // ---------- music file info found ----------
13440 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13442 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13444 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13446 *token_to_value_ptr[i].value_ptr =
13447 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13450 tmp_music_file_info.basename = getStringCopy(basename);
13451 tmp_music_file_info.music = music;
13452 tmp_music_file_info.is_sound = is_sound;
13454 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13455 *new_music_file_info = tmp_music_file_info;
13457 return new_music_file_info;
13460 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13462 return get_music_file_info_ext(basename, music, FALSE);
13465 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13467 return get_music_file_info_ext(basename, sound, TRUE);
13470 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13471 char *basename, boolean is_sound)
13473 for (; list != NULL; list = list->next)
13474 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13480 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13482 return music_info_listed_ext(list, basename, FALSE);
13485 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13487 return music_info_listed_ext(list, basename, TRUE);
13490 void LoadMusicInfo(void)
13492 int num_music_noconf = getMusicListSize_NoConf();
13493 int num_music = getMusicListSize();
13494 int num_sounds = getSoundListSize();
13495 struct FileInfo *music, *sound;
13496 struct MusicFileInfo *next, **new;
13500 while (music_file_info != NULL)
13502 next = music_file_info->next;
13504 checked_free(music_file_info->basename);
13506 checked_free(music_file_info->title_header);
13507 checked_free(music_file_info->artist_header);
13508 checked_free(music_file_info->album_header);
13509 checked_free(music_file_info->year_header);
13510 checked_free(music_file_info->played_header);
13512 checked_free(music_file_info->title);
13513 checked_free(music_file_info->artist);
13514 checked_free(music_file_info->album);
13515 checked_free(music_file_info->year);
13516 checked_free(music_file_info->played);
13518 free(music_file_info);
13520 music_file_info = next;
13523 new = &music_file_info;
13525 // get (configured or unconfigured) music file info for all levels
13526 for (i = leveldir_current->first_level;
13527 i <= leveldir_current->last_level; i++)
13531 if (levelset.music[i] != MUS_UNDEFINED)
13533 // get music file info for configured level music
13534 music_nr = levelset.music[i];
13536 else if (num_music_noconf > 0)
13538 // get music file info for unconfigured level music
13539 int level_pos = i - leveldir_current->first_level;
13541 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13548 char *basename = getMusicInfoEntryFilename(music_nr);
13550 if (basename == NULL)
13553 if (!music_info_listed(music_file_info, basename))
13555 *new = get_music_file_info(basename, music_nr);
13558 new = &(*new)->next;
13562 // get music file info for all remaining configured music files
13563 for (i = 0; i < num_music; i++)
13565 music = getMusicListEntry(i);
13567 if (music->filename == NULL)
13570 if (strEqual(music->filename, UNDEFINED_FILENAME))
13573 // a configured file may be not recognized as music
13574 if (!FileIsMusic(music->filename))
13577 if (!music_info_listed(music_file_info, music->filename))
13579 *new = get_music_file_info(music->filename, i);
13582 new = &(*new)->next;
13586 // get sound file info for all configured sound files
13587 for (i = 0; i < num_sounds; i++)
13589 sound = getSoundListEntry(i);
13591 if (sound->filename == NULL)
13594 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13597 // a configured file may be not recognized as sound
13598 if (!FileIsSound(sound->filename))
13601 if (!sound_info_listed(music_file_info, sound->filename))
13603 *new = get_sound_file_info(sound->filename, i);
13605 new = &(*new)->next;
13609 // add pointers to previous list nodes
13611 struct MusicFileInfo *node = music_file_info;
13613 while (node != NULL)
13616 node->next->prev = node;
13622 static void add_helpanim_entry(int element, int action, int direction,
13623 int delay, int *num_list_entries)
13625 struct HelpAnimInfo *new_list_entry;
13626 (*num_list_entries)++;
13629 checked_realloc(helpanim_info,
13630 *num_list_entries * sizeof(struct HelpAnimInfo));
13631 new_list_entry = &helpanim_info[*num_list_entries - 1];
13633 new_list_entry->element = element;
13634 new_list_entry->action = action;
13635 new_list_entry->direction = direction;
13636 new_list_entry->delay = delay;
13639 static void print_unknown_token(char *filename, char *token, int token_nr)
13644 Warn("unknown token(s) found in config file:");
13645 Warn("- config file: '%s'", filename);
13648 Warn("- token: '%s'", token);
13651 static void print_unknown_token_end(int token_nr)
13657 void LoadHelpAnimInfo(void)
13659 char *filename = getHelpAnimFilename();
13660 SetupFileList *setup_file_list = NULL, *list;
13661 SetupFileHash *element_hash, *action_hash, *direction_hash;
13662 int num_list_entries = 0;
13663 int num_unknown_tokens = 0;
13666 if (fileExists(filename))
13667 setup_file_list = loadSetupFileList(filename);
13669 if (setup_file_list == NULL)
13671 // use reliable default values from static configuration
13672 SetupFileList *insert_ptr;
13674 insert_ptr = setup_file_list =
13675 newSetupFileList(helpanim_config[0].token,
13676 helpanim_config[0].value);
13678 for (i = 1; helpanim_config[i].token; i++)
13679 insert_ptr = addListEntry(insert_ptr,
13680 helpanim_config[i].token,
13681 helpanim_config[i].value);
13684 element_hash = newSetupFileHash();
13685 action_hash = newSetupFileHash();
13686 direction_hash = newSetupFileHash();
13688 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13689 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13691 for (i = 0; i < NUM_ACTIONS; i++)
13692 setHashEntry(action_hash, element_action_info[i].suffix,
13693 i_to_a(element_action_info[i].value));
13695 // do not store direction index (bit) here, but direction value!
13696 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13697 setHashEntry(direction_hash, element_direction_info[i].suffix,
13698 i_to_a(1 << element_direction_info[i].value));
13700 for (list = setup_file_list; list != NULL; list = list->next)
13702 char *element_token, *action_token, *direction_token;
13703 char *element_value, *action_value, *direction_value;
13704 int delay = atoi(list->value);
13706 if (strEqual(list->token, "end"))
13708 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13713 /* first try to break element into element/action/direction parts;
13714 if this does not work, also accept combined "element[.act][.dir]"
13715 elements (like "dynamite.active"), which are unique elements */
13717 if (strchr(list->token, '.') == NULL) // token contains no '.'
13719 element_value = getHashEntry(element_hash, list->token);
13720 if (element_value != NULL) // element found
13721 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13722 &num_list_entries);
13725 // no further suffixes found -- this is not an element
13726 print_unknown_token(filename, list->token, num_unknown_tokens++);
13732 // token has format "<prefix>.<something>"
13734 action_token = strchr(list->token, '.'); // suffix may be action ...
13735 direction_token = action_token; // ... or direction
13737 element_token = getStringCopy(list->token);
13738 *strchr(element_token, '.') = '\0';
13740 element_value = getHashEntry(element_hash, element_token);
13742 if (element_value == NULL) // this is no element
13744 element_value = getHashEntry(element_hash, list->token);
13745 if (element_value != NULL) // combined element found
13746 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13747 &num_list_entries);
13749 print_unknown_token(filename, list->token, num_unknown_tokens++);
13751 free(element_token);
13756 action_value = getHashEntry(action_hash, action_token);
13758 if (action_value != NULL) // action found
13760 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13761 &num_list_entries);
13763 free(element_token);
13768 direction_value = getHashEntry(direction_hash, direction_token);
13770 if (direction_value != NULL) // direction found
13772 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13773 &num_list_entries);
13775 free(element_token);
13780 if (strchr(action_token + 1, '.') == NULL)
13782 // no further suffixes found -- this is not an action nor direction
13784 element_value = getHashEntry(element_hash, list->token);
13785 if (element_value != NULL) // combined element found
13786 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13787 &num_list_entries);
13789 print_unknown_token(filename, list->token, num_unknown_tokens++);
13791 free(element_token);
13796 // token has format "<prefix>.<suffix>.<something>"
13798 direction_token = strchr(action_token + 1, '.');
13800 action_token = getStringCopy(action_token);
13801 *strchr(action_token + 1, '.') = '\0';
13803 action_value = getHashEntry(action_hash, action_token);
13805 if (action_value == NULL) // this is no action
13807 element_value = getHashEntry(element_hash, list->token);
13808 if (element_value != NULL) // combined element found
13809 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13810 &num_list_entries);
13812 print_unknown_token(filename, list->token, num_unknown_tokens++);
13814 free(element_token);
13815 free(action_token);
13820 direction_value = getHashEntry(direction_hash, direction_token);
13822 if (direction_value != NULL) // direction found
13824 add_helpanim_entry(atoi(element_value), atoi(action_value),
13825 atoi(direction_value), delay, &num_list_entries);
13827 free(element_token);
13828 free(action_token);
13833 // this is no direction
13835 element_value = getHashEntry(element_hash, list->token);
13836 if (element_value != NULL) // combined element found
13837 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13838 &num_list_entries);
13840 print_unknown_token(filename, list->token, num_unknown_tokens++);
13842 free(element_token);
13843 free(action_token);
13846 print_unknown_token_end(num_unknown_tokens);
13848 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13849 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13851 freeSetupFileList(setup_file_list);
13852 freeSetupFileHash(element_hash);
13853 freeSetupFileHash(action_hash);
13854 freeSetupFileHash(direction_hash);
13857 for (i = 0; i < num_list_entries; i++)
13858 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13859 EL_NAME(helpanim_info[i].element),
13860 helpanim_info[i].element,
13861 helpanim_info[i].action,
13862 helpanim_info[i].direction,
13863 helpanim_info[i].delay);
13867 void LoadHelpTextInfo(void)
13869 char *filename = getHelpTextFilename();
13872 if (helptext_info != NULL)
13874 freeSetupFileHash(helptext_info);
13875 helptext_info = NULL;
13878 if (fileExists(filename))
13879 helptext_info = loadSetupFileHash(filename);
13881 if (helptext_info == NULL)
13883 // use reliable default values from static configuration
13884 helptext_info = newSetupFileHash();
13886 for (i = 0; helptext_config[i].token; i++)
13887 setHashEntry(helptext_info,
13888 helptext_config[i].token,
13889 helptext_config[i].value);
13893 BEGIN_HASH_ITERATION(helptext_info, itr)
13895 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13896 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13898 END_HASH_ITERATION(hash, itr)
13903 // ----------------------------------------------------------------------------
13905 // ----------------------------------------------------------------------------
13907 #define MAX_NUM_CONVERT_LEVELS 1000
13909 void ConvertLevels(void)
13911 static LevelDirTree *convert_leveldir = NULL;
13912 static int convert_level_nr = -1;
13913 static int num_levels_handled = 0;
13914 static int num_levels_converted = 0;
13915 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13918 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13919 global.convert_leveldir);
13921 if (convert_leveldir == NULL)
13922 Fail("no such level identifier: '%s'", global.convert_leveldir);
13924 leveldir_current = convert_leveldir;
13926 if (global.convert_level_nr != -1)
13928 convert_leveldir->first_level = global.convert_level_nr;
13929 convert_leveldir->last_level = global.convert_level_nr;
13932 convert_level_nr = convert_leveldir->first_level;
13934 PrintLine("=", 79);
13935 Print("Converting levels\n");
13936 PrintLine("-", 79);
13937 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13938 Print("Level series name: '%s'\n", convert_leveldir->name);
13939 Print("Level series author: '%s'\n", convert_leveldir->author);
13940 Print("Number of levels: %d\n", convert_leveldir->levels);
13941 PrintLine("=", 79);
13944 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13945 levels_failed[i] = FALSE;
13947 while (convert_level_nr <= convert_leveldir->last_level)
13949 char *level_filename;
13952 level_nr = convert_level_nr++;
13954 Print("Level %03d: ", level_nr);
13956 LoadLevel(level_nr);
13957 if (level.no_level_file || level.no_valid_file)
13959 Print("(no level)\n");
13963 Print("converting level ... ");
13966 // special case: conversion of some EMC levels as requested by ACME
13967 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13970 level_filename = getDefaultLevelFilename(level_nr);
13971 new_level = !fileExists(level_filename);
13975 SaveLevel(level_nr);
13977 num_levels_converted++;
13979 Print("converted.\n");
13983 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13984 levels_failed[level_nr] = TRUE;
13986 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13989 num_levels_handled++;
13993 PrintLine("=", 79);
13994 Print("Number of levels handled: %d\n", num_levels_handled);
13995 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13996 (num_levels_handled ?
13997 num_levels_converted * 100 / num_levels_handled : 0));
13998 PrintLine("-", 79);
13999 Print("Summary (for automatic parsing by scripts):\n");
14000 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14001 convert_leveldir->identifier, num_levels_converted,
14002 num_levels_handled,
14003 (num_levels_handled ?
14004 num_levels_converted * 100 / num_levels_handled : 0));
14006 if (num_levels_handled != num_levels_converted)
14008 Print(", FAILED:");
14009 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14010 if (levels_failed[i])
14015 PrintLine("=", 79);
14017 CloseAllAndExit(0);
14021 // ----------------------------------------------------------------------------
14022 // create and save images for use in level sketches (raw BMP format)
14023 // ----------------------------------------------------------------------------
14025 void CreateLevelSketchImages(void)
14031 InitElementPropertiesGfxElement();
14033 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14034 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14036 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14038 int element = getMappedElement(i);
14039 char basename1[16];
14040 char basename2[16];
14044 sprintf(basename1, "%04d.bmp", i);
14045 sprintf(basename2, "%04ds.bmp", i);
14047 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14048 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14050 DrawSizedElement(0, 0, element, TILESIZE);
14051 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14053 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14054 Fail("cannot save level sketch image file '%s'", filename1);
14056 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14057 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14059 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14060 Fail("cannot save level sketch image file '%s'", filename2);
14065 // create corresponding SQL statements (for normal and small images)
14068 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14069 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14072 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14073 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14075 // optional: create content for forum level sketch demonstration post
14077 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14080 FreeBitmap(bitmap1);
14081 FreeBitmap(bitmap2);
14084 fprintf(stderr, "\n");
14086 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14088 CloseAllAndExit(0);
14092 // ----------------------------------------------------------------------------
14093 // create and save images for element collecting animations (raw BMP format)
14094 // ----------------------------------------------------------------------------
14096 static boolean createCollectImage(int element)
14098 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14101 void CreateCollectElementImages(void)
14105 int anim_frames = num_steps - 1;
14106 int tile_size = TILESIZE;
14107 int anim_width = tile_size * anim_frames;
14108 int anim_height = tile_size;
14109 int num_collect_images = 0;
14110 int pos_collect_images = 0;
14112 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14113 if (createCollectImage(i))
14114 num_collect_images++;
14116 Info("Creating %d element collecting animation images ...",
14117 num_collect_images);
14119 int dst_width = anim_width * 2;
14120 int dst_height = anim_height * num_collect_images / 2;
14121 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14122 char *basename_bmp = "RocksCollect.bmp";
14123 char *basename_png = "RocksCollect.png";
14124 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14125 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14126 int len_filename_bmp = strlen(filename_bmp);
14127 int len_filename_png = strlen(filename_png);
14128 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14129 char cmd_convert[max_command_len];
14131 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14135 // force using RGBA surface for destination bitmap
14136 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14137 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14139 dst_bitmap->surface =
14140 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14142 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14144 if (!createCollectImage(i))
14147 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14148 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14149 int graphic = el2img(i);
14150 char *token_name = element_info[i].token_name;
14151 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14152 Bitmap *src_bitmap;
14155 Info("- creating collecting image for '%s' ...", token_name);
14157 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14159 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14160 tile_size, tile_size, 0, 0);
14162 // force using RGBA surface for temporary bitmap (using transparent black)
14163 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14164 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14166 tmp_bitmap->surface =
14167 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14169 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14171 for (j = 0; j < anim_frames; j++)
14173 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14174 int frame_size = frame_size_final * num_steps;
14175 int offset = (tile_size - frame_size_final) / 2;
14176 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14178 while (frame_size > frame_size_final)
14182 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14184 FreeBitmap(frame_bitmap);
14186 frame_bitmap = half_bitmap;
14189 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14190 frame_size_final, frame_size_final,
14191 dst_x + j * tile_size + offset, dst_y + offset);
14193 FreeBitmap(frame_bitmap);
14196 tmp_bitmap->surface_masked = NULL;
14198 FreeBitmap(tmp_bitmap);
14200 pos_collect_images++;
14203 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14204 Fail("cannot save element collecting image file '%s'", filename_bmp);
14206 FreeBitmap(dst_bitmap);
14208 Info("Converting image file from BMP to PNG ...");
14210 if (system(cmd_convert) != 0)
14211 Fail("converting image file failed");
14213 unlink(filename_bmp);
14217 CloseAllAndExit(0);
14221 // ----------------------------------------------------------------------------
14222 // create and save images for custom and group elements (raw BMP format)
14223 // ----------------------------------------------------------------------------
14225 void CreateCustomElementImages(char *directory)
14227 char *src_basename = "RocksCE-template.ilbm";
14228 char *dst_basename = "RocksCE.bmp";
14229 char *src_filename = getPath2(directory, src_basename);
14230 char *dst_filename = getPath2(directory, dst_basename);
14231 Bitmap *src_bitmap;
14233 int yoffset_ce = 0;
14234 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14237 InitVideoDefaults();
14239 ReCreateBitmap(&backbuffer, video.width, video.height);
14241 src_bitmap = LoadImage(src_filename);
14243 bitmap = CreateBitmap(TILEX * 16 * 2,
14244 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14247 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14254 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14255 TILEX * x, TILEY * y + yoffset_ce);
14257 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14259 TILEX * x + TILEX * 16,
14260 TILEY * y + yoffset_ce);
14262 for (j = 2; j >= 0; j--)
14266 BlitBitmap(src_bitmap, bitmap,
14267 TILEX + c * 7, 0, 6, 10,
14268 TILEX * x + 6 + j * 7,
14269 TILEY * y + 11 + yoffset_ce);
14271 BlitBitmap(src_bitmap, bitmap,
14272 TILEX + c * 8, TILEY, 6, 10,
14273 TILEX * 16 + TILEX * x + 6 + j * 8,
14274 TILEY * y + 10 + yoffset_ce);
14280 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14287 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14288 TILEX * x, TILEY * y + yoffset_ge);
14290 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14292 TILEX * x + TILEX * 16,
14293 TILEY * y + yoffset_ge);
14295 for (j = 1; j >= 0; j--)
14299 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14300 TILEX * x + 6 + j * 10,
14301 TILEY * y + 11 + yoffset_ge);
14303 BlitBitmap(src_bitmap, bitmap,
14304 TILEX + c * 8, TILEY + 12, 6, 10,
14305 TILEX * 16 + TILEX * x + 10 + j * 8,
14306 TILEY * y + 10 + yoffset_ge);
14312 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14313 Fail("cannot save CE graphics file '%s'", dst_filename);
14315 FreeBitmap(bitmap);
14317 CloseAllAndExit(0);