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_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
323 // (these values are the same for each player)
326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
327 &li.block_last_field, FALSE // default case for EM levels
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
332 &li.sp_block_last_field, TRUE // default case for SP levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
337 &li.instant_relocation, FALSE
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
342 &li.can_pass_to_walkable, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
347 &li.block_snap_field, TRUE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
352 &li.continuous_snapping, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
357 &li.shifted_relocation, FALSE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
362 &li.lazy_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
367 &li.finish_dig_collect, TRUE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
372 &li.keep_walkable_ce, FALSE
375 // (these values are different for each player)
378 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
379 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
384 &li.initial_player_gravity[0], FALSE
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
389 &li.use_start_element[0], FALSE
393 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
394 &li.start_element[0], EL_PLAYER_1
398 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
399 &li.use_artwork_element[0], FALSE
403 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
404 &li.artwork_element[0], EL_PLAYER_1
408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
409 &li.use_explosion_element[0], FALSE
413 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
414 &li.explosion_element[0], EL_PLAYER_1
418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
419 &li.use_initial_inventory[0], FALSE
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
424 &li.initial_inventory_size[0], 1
428 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
429 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
435 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
436 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
440 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
441 &li.initial_player_gravity[1], FALSE
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
446 &li.use_start_element[1], FALSE
450 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
451 &li.start_element[1], EL_PLAYER_2
455 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
456 &li.use_artwork_element[1], FALSE
460 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
461 &li.artwork_element[1], EL_PLAYER_2
465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
466 &li.use_explosion_element[1], FALSE
470 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
471 &li.explosion_element[1], EL_PLAYER_2
475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
476 &li.use_initial_inventory[1], FALSE
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
481 &li.initial_inventory_size[1], 1
485 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
486 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
492 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
493 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
497 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
498 &li.initial_player_gravity[2], FALSE
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
503 &li.use_start_element[2], FALSE
507 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
508 &li.start_element[2], EL_PLAYER_3
512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
513 &li.use_artwork_element[2], FALSE
517 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
518 &li.artwork_element[2], EL_PLAYER_3
522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
523 &li.use_explosion_element[2], FALSE
527 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
528 &li.explosion_element[2], EL_PLAYER_3
532 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
533 &li.use_initial_inventory[2], FALSE
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
538 &li.initial_inventory_size[2], 1
542 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
543 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
549 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
550 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
554 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
555 &li.initial_player_gravity[3], FALSE
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
560 &li.use_start_element[3], FALSE
564 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
565 &li.start_element[3], EL_PLAYER_4
569 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
570 &li.use_artwork_element[3], FALSE
574 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
575 &li.artwork_element[3], EL_PLAYER_4
579 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
580 &li.use_explosion_element[3], FALSE
584 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
585 &li.explosion_element[3], EL_PLAYER_4
589 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
590 &li.use_initial_inventory[3], FALSE
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
595 &li.initial_inventory_size[3], 1
599 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
600 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
604 // (these values are only valid for BD style levels)
607 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
608 &li.bd_diagonal_movements, FALSE
612 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
613 &li.bd_topmost_player_active, TRUE
617 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
618 &li.bd_pushing_prob, 25
622 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
623 &li.bd_pushing_prob_with_sweet, 100
627 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
628 &li.bd_push_mega_rock_with_sweet, FALSE
633 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
634 &li.score[SC_DIAMOND_EXTRA], 20
638 EL_BD_MAGIC_WALL, -1,
639 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
640 &li.bd_magic_wall_wait_hatching, FALSE
643 EL_BD_MAGIC_WALL, -1,
644 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
645 &li.bd_magic_wall_stops_amoeba, TRUE
648 // (the following values are related to various game elements)
652 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
653 &li.score[SC_EMERALD], 10
658 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
659 &li.score[SC_DIAMOND], 10
664 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
665 &li.score[SC_BUG], 10
670 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
671 &li.score[SC_SPACESHIP], 10
676 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
677 &li.score[SC_PACMAN], 10
682 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
683 &li.score[SC_NUT], 10
688 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
689 &li.score[SC_DYNAMITE], 10
694 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
695 &li.score[SC_KEY], 10
700 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
701 &li.score[SC_PEARL], 10
706 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
707 &li.score[SC_CRYSTAL], 10
712 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
713 &li.amoeba_content, EL_DIAMOND
717 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
722 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
723 &li.grow_into_diggable, TRUE
728 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
729 &li.yamyam_content, EL_ROCK, NULL,
730 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
734 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
735 &li.score[SC_YAMYAM], 10
740 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
741 &li.score[SC_ROBOT], 10
745 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
751 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
757 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
758 &li.time_magic_wall, 10
763 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
764 &li.game_of_life[0], 2
768 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
769 &li.game_of_life[1], 3
773 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
774 &li.game_of_life[2], 3
778 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
779 &li.game_of_life[3], 3
783 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
784 &li.use_life_bugs, FALSE
789 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
794 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
799 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
804 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
809 EL_TIMEGATE_SWITCH, -1,
810 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
811 &li.time_timegate, 10
815 EL_LIGHT_SWITCH_ACTIVE, -1,
816 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
821 EL_SHIELD_NORMAL, -1,
822 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
823 &li.shield_normal_time, 10
826 EL_SHIELD_NORMAL, -1,
827 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
828 &li.score[SC_SHIELD], 10
832 EL_SHIELD_DEADLY, -1,
833 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
834 &li.shield_deadly_time, 10
837 EL_SHIELD_DEADLY, -1,
838 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
839 &li.score[SC_SHIELD], 10
844 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
849 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
850 &li.extra_time_score, 10
854 EL_TIME_ORB_FULL, -1,
855 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
856 &li.time_orb_time, 10
859 EL_TIME_ORB_FULL, -1,
860 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
861 &li.use_time_orb_bug, FALSE
866 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
867 &li.use_spring_bug, FALSE
872 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
873 &li.android_move_time, 10
877 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
878 &li.android_clone_time, 10
881 EL_EMC_ANDROID, SAVE_CONF_NEVER,
882 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
883 &li.android_clone_element[0], EL_EMPTY, NULL,
884 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
888 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
889 &li.android_clone_element[0], EL_EMPTY, NULL,
890 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
895 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
900 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
905 EL_EMC_MAGNIFIER, -1,
906 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
907 &li.magnify_score, 10
910 EL_EMC_MAGNIFIER, -1,
911 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
916 EL_EMC_MAGIC_BALL, -1,
917 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
921 EL_EMC_MAGIC_BALL, -1,
922 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
923 &li.ball_random, FALSE
926 EL_EMC_MAGIC_BALL, -1,
927 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
928 &li.ball_active_initial, FALSE
931 EL_EMC_MAGIC_BALL, -1,
932 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
933 &li.ball_content, EL_EMPTY, NULL,
934 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
938 EL_SOKOBAN_FIELD_EMPTY, -1,
939 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
940 &li.sb_fields_needed, TRUE
944 EL_SOKOBAN_OBJECT, -1,
945 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
946 &li.sb_objects_needed, TRUE
951 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
952 &li.mm_laser_red, FALSE
956 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
957 &li.mm_laser_green, FALSE
961 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
962 &li.mm_laser_blue, TRUE
967 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
968 &li.df_laser_red, TRUE
972 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
973 &li.df_laser_green, TRUE
977 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
978 &li.df_laser_blue, FALSE
982 EL_MM_FUSE_ACTIVE, -1,
983 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
988 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
994 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
999 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1000 &li.mm_ball_choice_mode, ANIM_RANDOM
1003 EL_MM_GRAY_BALL, -1,
1004 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1005 &li.mm_ball_content, EL_EMPTY, NULL,
1006 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1009 EL_MM_GRAY_BALL, -1,
1010 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1011 &li.rotate_mm_ball_content, TRUE
1014 EL_MM_GRAY_BALL, -1,
1015 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1016 &li.explode_mm_ball, FALSE
1020 EL_MM_STEEL_BLOCK, -1,
1021 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1022 &li.mm_time_block, 75
1025 EL_MM_LIGHTBALL, -1,
1026 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1027 &li.score[SC_ELEM_BONUS], 10
1037 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1041 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1042 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1046 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1047 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1052 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1053 &xx_envelope.autowrap, FALSE
1057 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1058 &xx_envelope.centered, FALSE
1063 TYPE_STRING, CONF_VALUE_BYTES(1),
1064 &xx_envelope.text, -1, NULL,
1065 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1066 &xx_default_string_empty[0]
1076 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1080 TYPE_STRING, CONF_VALUE_BYTES(1),
1081 &xx_ei.description[0], -1,
1082 &yy_ei.description[0],
1083 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1084 &xx_default_description[0]
1089 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1090 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1091 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1093 #if ENABLE_RESERVED_CODE
1094 // (reserved for later use)
1097 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1098 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1099 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1105 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1106 &xx_ei.use_gfx_element, FALSE,
1107 &yy_ei.use_gfx_element
1111 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1112 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1113 &yy_ei.gfx_element_initial
1118 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1119 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1120 &yy_ei.access_direction
1125 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1126 &xx_ei.collect_score_initial, 10,
1127 &yy_ei.collect_score_initial
1131 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1132 &xx_ei.collect_count_initial, 1,
1133 &yy_ei.collect_count_initial
1138 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1139 &xx_ei.ce_value_fixed_initial, 0,
1140 &yy_ei.ce_value_fixed_initial
1144 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1145 &xx_ei.ce_value_random_initial, 0,
1146 &yy_ei.ce_value_random_initial
1150 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1151 &xx_ei.use_last_ce_value, FALSE,
1152 &yy_ei.use_last_ce_value
1157 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1158 &xx_ei.push_delay_fixed, 8,
1159 &yy_ei.push_delay_fixed
1163 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1164 &xx_ei.push_delay_random, 8,
1165 &yy_ei.push_delay_random
1169 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1170 &xx_ei.drop_delay_fixed, 0,
1171 &yy_ei.drop_delay_fixed
1175 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1176 &xx_ei.drop_delay_random, 0,
1177 &yy_ei.drop_delay_random
1181 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1182 &xx_ei.move_delay_fixed, 0,
1183 &yy_ei.move_delay_fixed
1187 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1188 &xx_ei.move_delay_random, 0,
1189 &yy_ei.move_delay_random
1193 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1194 &xx_ei.step_delay_fixed, 0,
1195 &yy_ei.step_delay_fixed
1199 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1200 &xx_ei.step_delay_random, 0,
1201 &yy_ei.step_delay_random
1206 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1207 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1212 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1213 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1214 &yy_ei.move_direction_initial
1218 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1219 &xx_ei.move_stepsize, TILEX / 8,
1220 &yy_ei.move_stepsize
1225 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1226 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1227 &yy_ei.move_enter_element
1231 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1232 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1233 &yy_ei.move_leave_element
1237 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1238 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1239 &yy_ei.move_leave_type
1244 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1245 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1246 &yy_ei.slippery_type
1251 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1252 &xx_ei.explosion_type, EXPLODES_3X3,
1253 &yy_ei.explosion_type
1257 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1258 &xx_ei.explosion_delay, 16,
1259 &yy_ei.explosion_delay
1263 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1264 &xx_ei.ignition_delay, 8,
1265 &yy_ei.ignition_delay
1270 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1271 &xx_ei.content, EL_EMPTY_SPACE,
1273 &xx_num_contents, 1, 1
1276 // ---------- "num_change_pages" must be the last entry ---------------------
1279 -1, SAVE_CONF_ALWAYS,
1280 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1281 &xx_ei.num_change_pages, 1,
1282 &yy_ei.num_change_pages
1293 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1295 // ---------- "current_change_page" must be the first entry -----------------
1298 -1, SAVE_CONF_ALWAYS,
1299 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1300 &xx_current_change_page, -1
1303 // ---------- (the remaining entries can be in any order) -------------------
1307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1308 &xx_change.can_change, FALSE
1313 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1314 &xx_event_bits[0], 0
1318 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1319 &xx_event_bits[1], 0
1324 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1325 &xx_change.trigger_player, CH_PLAYER_ANY
1329 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1330 &xx_change.trigger_side, CH_SIDE_ANY
1334 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1335 &xx_change.trigger_page, CH_PAGE_ANY
1340 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1341 &xx_change.target_element, EL_EMPTY_SPACE
1346 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1347 &xx_change.delay_fixed, 0
1351 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1352 &xx_change.delay_random, 0
1356 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1357 &xx_change.delay_frames, FRAMES_PER_SECOND
1362 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1363 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1368 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1369 &xx_change.explode, FALSE
1373 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1374 &xx_change.use_target_content, FALSE
1378 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1379 &xx_change.only_if_complete, FALSE
1383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1384 &xx_change.use_random_replace, FALSE
1388 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1389 &xx_change.random_percentage, 100
1393 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1394 &xx_change.replace_when, CP_WHEN_EMPTY
1399 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1400 &xx_change.has_action, FALSE
1404 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1405 &xx_change.action_type, CA_NO_ACTION
1409 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1410 &xx_change.action_mode, CA_MODE_UNDEFINED
1414 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1415 &xx_change.action_arg, CA_ARG_UNDEFINED
1420 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1421 &xx_change.action_element, EL_EMPTY_SPACE
1426 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1427 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1428 &xx_num_contents, 1, 1
1438 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1442 TYPE_STRING, CONF_VALUE_BYTES(1),
1443 &xx_ei.description[0], -1, NULL,
1444 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1445 &xx_default_description[0]
1450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1451 &xx_ei.use_gfx_element, FALSE
1455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1456 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1461 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1462 &xx_group.choice_mode, ANIM_RANDOM
1467 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1468 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1469 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1479 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1483 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1484 &xx_ei.use_gfx_element, FALSE
1488 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1489 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1499 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1503 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1504 &li.block_snap_field, TRUE
1508 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1509 &li.continuous_snapping, TRUE
1513 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1514 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1518 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1519 &li.use_start_element[0], FALSE
1523 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1524 &li.start_element[0], EL_PLAYER_1
1528 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1529 &li.use_artwork_element[0], FALSE
1533 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1534 &li.artwork_element[0], EL_PLAYER_1
1538 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1539 &li.use_explosion_element[0], FALSE
1543 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1544 &li.explosion_element[0], EL_PLAYER_1
1559 filetype_id_list[] =
1561 { LEVEL_FILE_TYPE_RND, "RND" },
1562 { LEVEL_FILE_TYPE_BD, "BD" },
1563 { LEVEL_FILE_TYPE_EM, "EM" },
1564 { LEVEL_FILE_TYPE_SP, "SP" },
1565 { LEVEL_FILE_TYPE_DX, "DX" },
1566 { LEVEL_FILE_TYPE_SB, "SB" },
1567 { LEVEL_FILE_TYPE_DC, "DC" },
1568 { LEVEL_FILE_TYPE_MM, "MM" },
1569 { LEVEL_FILE_TYPE_MM, "DF" },
1574 // ============================================================================
1575 // level file functions
1576 // ============================================================================
1578 static boolean check_special_flags(char *flag)
1580 if (strEqual(options.special_flags, flag) ||
1581 strEqual(leveldir_current->special_flags, flag))
1587 static struct DateInfo getCurrentDate(void)
1589 time_t epoch_seconds = time(NULL);
1590 struct tm *now = localtime(&epoch_seconds);
1591 struct DateInfo date;
1593 date.year = now->tm_year + 1900;
1594 date.month = now->tm_mon + 1;
1595 date.day = now->tm_mday;
1597 date.src = DATE_SRC_CLOCK;
1602 static void resetEventFlags(struct ElementChangeInfo *change)
1606 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1607 change->has_event[i] = FALSE;
1610 static void resetEventBits(void)
1614 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1615 xx_event_bits[i] = 0;
1618 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1622 /* important: only change event flag if corresponding event bit is set
1623 (this is because all xx_event_bits[] values are loaded separately,
1624 and all xx_event_bits[] values are set back to zero before loading
1625 another value xx_event_bits[x] (each value representing 32 flags)) */
1627 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1628 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1629 change->has_event[i] = TRUE;
1632 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1636 /* in contrast to the above function setEventFlagsFromEventBits(), it
1637 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1638 depending on the corresponding change->has_event[i] values here, as
1639 all xx_event_bits[] values are reset in resetEventBits() before */
1641 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1642 if (change->has_event[i])
1643 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1646 static char *getDefaultElementDescription(struct ElementInfo *ei)
1648 static char description[MAX_ELEMENT_NAME_LEN + 1];
1649 char *default_description = (ei->custom_description != NULL ?
1650 ei->custom_description :
1651 ei->editor_description);
1654 // always start with reliable default values
1655 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1656 description[i] = '\0';
1658 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1659 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1661 return &description[0];
1664 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1666 char *default_description = getDefaultElementDescription(ei);
1669 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1670 ei->description[i] = default_description[i];
1673 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1677 for (i = 0; conf[i].data_type != -1; i++)
1679 int default_value = conf[i].default_value;
1680 int data_type = conf[i].data_type;
1681 int conf_type = conf[i].conf_type;
1682 int byte_mask = conf_type & CONF_MASK_BYTES;
1684 if (byte_mask == CONF_MASK_MULTI_BYTES)
1686 int default_num_entities = conf[i].default_num_entities;
1687 int max_num_entities = conf[i].max_num_entities;
1689 *(int *)(conf[i].num_entities) = default_num_entities;
1691 if (data_type == TYPE_STRING)
1693 char *default_string = conf[i].default_string;
1694 char *string = (char *)(conf[i].value);
1696 strncpy(string, default_string, max_num_entities);
1698 else if (data_type == TYPE_ELEMENT_LIST)
1700 int *element_array = (int *)(conf[i].value);
1703 for (j = 0; j < max_num_entities; j++)
1704 element_array[j] = default_value;
1706 else if (data_type == TYPE_CONTENT_LIST)
1708 struct Content *content = (struct Content *)(conf[i].value);
1711 for (c = 0; c < max_num_entities; c++)
1712 for (y = 0; y < 3; y++)
1713 for (x = 0; x < 3; x++)
1714 content[c].e[x][y] = default_value;
1717 else // constant size configuration data (1, 2 or 4 bytes)
1719 if (data_type == TYPE_BOOLEAN)
1720 *(boolean *)(conf[i].value) = default_value;
1722 *(int *) (conf[i].value) = default_value;
1727 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1731 for (i = 0; conf[i].data_type != -1; i++)
1733 int data_type = conf[i].data_type;
1734 int conf_type = conf[i].conf_type;
1735 int byte_mask = conf_type & CONF_MASK_BYTES;
1737 if (byte_mask == CONF_MASK_MULTI_BYTES)
1739 int max_num_entities = conf[i].max_num_entities;
1741 if (data_type == TYPE_STRING)
1743 char *string = (char *)(conf[i].value);
1744 char *string_copy = (char *)(conf[i].value_copy);
1746 strncpy(string_copy, string, max_num_entities);
1748 else if (data_type == TYPE_ELEMENT_LIST)
1750 int *element_array = (int *)(conf[i].value);
1751 int *element_array_copy = (int *)(conf[i].value_copy);
1754 for (j = 0; j < max_num_entities; j++)
1755 element_array_copy[j] = element_array[j];
1757 else if (data_type == TYPE_CONTENT_LIST)
1759 struct Content *content = (struct Content *)(conf[i].value);
1760 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1763 for (c = 0; c < max_num_entities; c++)
1764 for (y = 0; y < 3; y++)
1765 for (x = 0; x < 3; x++)
1766 content_copy[c].e[x][y] = content[c].e[x][y];
1769 else // constant size configuration data (1, 2 or 4 bytes)
1771 if (data_type == TYPE_BOOLEAN)
1772 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1774 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1779 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1783 xx_ei = *ei_from; // copy element data into temporary buffer
1784 yy_ei = *ei_to; // copy element data into temporary buffer
1786 copyConfigFromConfigList(chunk_config_CUSX_base);
1791 // ---------- reinitialize and copy change pages ----------
1793 ei_to->num_change_pages = ei_from->num_change_pages;
1794 ei_to->current_change_page = ei_from->current_change_page;
1796 setElementChangePages(ei_to, ei_to->num_change_pages);
1798 for (i = 0; i < ei_to->num_change_pages; i++)
1799 ei_to->change_page[i] = ei_from->change_page[i];
1801 // ---------- copy group element info ----------
1802 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1803 *ei_to->group = *ei_from->group;
1805 // mark this custom element as modified
1806 ei_to->modified_settings = TRUE;
1809 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1811 int change_page_size = sizeof(struct ElementChangeInfo);
1813 ei->num_change_pages = MAX(1, change_pages);
1816 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1818 if (ei->current_change_page >= ei->num_change_pages)
1819 ei->current_change_page = ei->num_change_pages - 1;
1821 ei->change = &ei->change_page[ei->current_change_page];
1824 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1826 xx_change = *change; // copy change data into temporary buffer
1828 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1830 *change = xx_change;
1832 resetEventFlags(change);
1834 change->direct_action = 0;
1835 change->other_action = 0;
1837 change->pre_change_function = NULL;
1838 change->change_function = NULL;
1839 change->post_change_function = NULL;
1842 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1846 li = *level; // copy level data into temporary buffer
1847 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1848 *level = li; // copy temporary buffer back to level data
1850 setLevelInfoToDefaults_BD();
1851 setLevelInfoToDefaults_EM();
1852 setLevelInfoToDefaults_SP();
1853 setLevelInfoToDefaults_MM();
1855 level->native_bd_level = &native_bd_level;
1856 level->native_em_level = &native_em_level;
1857 level->native_sp_level = &native_sp_level;
1858 level->native_mm_level = &native_mm_level;
1860 level->file_version = FILE_VERSION_ACTUAL;
1861 level->game_version = GAME_VERSION_ACTUAL;
1863 level->creation_date = getCurrentDate();
1865 level->encoding_16bit_field = TRUE;
1866 level->encoding_16bit_yamyam = TRUE;
1867 level->encoding_16bit_amoeba = TRUE;
1869 // clear level name and level author string buffers
1870 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1871 level->name[i] = '\0';
1872 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1873 level->author[i] = '\0';
1875 // set level name and level author to default values
1876 strcpy(level->name, NAMELESS_LEVEL_NAME);
1877 strcpy(level->author, ANONYMOUS_NAME);
1879 // set level playfield to playable default level with player and exit
1880 for (x = 0; x < MAX_LEV_FIELDX; x++)
1881 for (y = 0; y < MAX_LEV_FIELDY; y++)
1882 level->field[x][y] = EL_SAND;
1884 level->field[0][0] = EL_PLAYER_1;
1885 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1887 BorderElement = EL_STEELWALL;
1889 // detect custom elements when loading them
1890 level->file_has_custom_elements = FALSE;
1892 // set all bug compatibility flags to "false" => do not emulate this bug
1893 level->use_action_after_change_bug = FALSE;
1895 if (leveldir_current)
1897 // try to determine better author name than 'anonymous'
1898 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1900 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1901 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1905 switch (LEVELCLASS(leveldir_current))
1907 case LEVELCLASS_TUTORIAL:
1908 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1911 case LEVELCLASS_CONTRIB:
1912 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1913 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1916 case LEVELCLASS_PRIVATE:
1917 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1918 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1922 // keep default value
1929 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1931 static boolean clipboard_elements_initialized = FALSE;
1934 InitElementPropertiesStatic();
1936 li = *level; // copy level data into temporary buffer
1937 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1938 *level = li; // copy temporary buffer back to level data
1940 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1943 struct ElementInfo *ei = &element_info[element];
1945 if (element == EL_MM_GRAY_BALL)
1947 struct LevelInfo_MM *level_mm = level->native_mm_level;
1950 for (j = 0; j < level->num_mm_ball_contents; j++)
1951 level->mm_ball_content[j] =
1952 map_element_MM_to_RND(level_mm->ball_content[j]);
1955 // never initialize clipboard elements after the very first time
1956 // (to be able to use clipboard elements between several levels)
1957 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1960 if (IS_ENVELOPE(element))
1962 int envelope_nr = element - EL_ENVELOPE_1;
1964 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1966 level->envelope[envelope_nr] = xx_envelope;
1969 if (IS_CUSTOM_ELEMENT(element) ||
1970 IS_GROUP_ELEMENT(element) ||
1971 IS_INTERNAL_ELEMENT(element))
1973 xx_ei = *ei; // copy element data into temporary buffer
1975 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1980 setElementChangePages(ei, 1);
1981 setElementChangeInfoToDefaults(ei->change);
1983 if (IS_CUSTOM_ELEMENT(element) ||
1984 IS_GROUP_ELEMENT(element))
1986 setElementDescriptionToDefault(ei);
1988 ei->modified_settings = FALSE;
1991 if (IS_CUSTOM_ELEMENT(element) ||
1992 IS_INTERNAL_ELEMENT(element))
1994 // internal values used in level editor
1996 ei->access_type = 0;
1997 ei->access_layer = 0;
1998 ei->access_protected = 0;
1999 ei->walk_to_action = 0;
2000 ei->smash_targets = 0;
2003 ei->can_explode_by_fire = FALSE;
2004 ei->can_explode_smashed = FALSE;
2005 ei->can_explode_impact = FALSE;
2007 ei->current_change_page = 0;
2010 if (IS_GROUP_ELEMENT(element) ||
2011 IS_INTERNAL_ELEMENT(element))
2013 struct ElementGroupInfo *group;
2015 // initialize memory for list of elements in group
2016 if (ei->group == NULL)
2017 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2021 xx_group = *group; // copy group data into temporary buffer
2023 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2028 if (IS_EMPTY_ELEMENT(element) ||
2029 IS_INTERNAL_ELEMENT(element))
2031 xx_ei = *ei; // copy element data into temporary buffer
2033 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2039 clipboard_elements_initialized = TRUE;
2042 static void setLevelInfoToDefaults(struct LevelInfo *level,
2043 boolean level_info_only,
2044 boolean reset_file_status)
2046 setLevelInfoToDefaults_Level(level);
2048 if (!level_info_only)
2049 setLevelInfoToDefaults_Elements(level);
2051 if (reset_file_status)
2053 level->no_valid_file = FALSE;
2054 level->no_level_file = FALSE;
2057 level->changed = FALSE;
2060 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2062 level_file_info->nr = 0;
2063 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2064 level_file_info->packed = FALSE;
2066 setString(&level_file_info->basename, NULL);
2067 setString(&level_file_info->filename, NULL);
2070 int getMappedElement_SB(int, boolean);
2072 static void ActivateLevelTemplate(void)
2076 if (check_special_flags("load_xsb_to_ces"))
2078 // fill smaller playfields with padding "beyond border wall" elements
2079 if (level.fieldx < level_template.fieldx ||
2080 level.fieldy < level_template.fieldy)
2082 short field[level.fieldx][level.fieldy];
2083 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2084 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2085 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2086 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2088 // copy old playfield (which is smaller than the visible area)
2089 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2090 field[x][y] = level.field[x][y];
2092 // fill new, larger playfield with "beyond border wall" elements
2093 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2094 level.field[x][y] = getMappedElement_SB('_', TRUE);
2096 // copy the old playfield to the middle of the new playfield
2097 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2098 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2100 level.fieldx = new_fieldx;
2101 level.fieldy = new_fieldy;
2105 // Currently there is no special action needed to activate the template
2106 // data, because 'element_info' property settings overwrite the original
2107 // level data, while all other variables do not change.
2109 // Exception: 'from_level_template' elements in the original level playfield
2110 // are overwritten with the corresponding elements at the same position in
2111 // playfield from the level template.
2113 for (x = 0; x < level.fieldx; x++)
2114 for (y = 0; y < level.fieldy; y++)
2115 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2116 level.field[x][y] = level_template.field[x][y];
2118 if (check_special_flags("load_xsb_to_ces"))
2120 struct LevelInfo level_backup = level;
2122 // overwrite all individual level settings from template level settings
2123 level = level_template;
2125 // restore level file info
2126 level.file_info = level_backup.file_info;
2128 // restore playfield size
2129 level.fieldx = level_backup.fieldx;
2130 level.fieldy = level_backup.fieldy;
2132 // restore playfield content
2133 for (x = 0; x < level.fieldx; x++)
2134 for (y = 0; y < level.fieldy; y++)
2135 level.field[x][y] = level_backup.field[x][y];
2137 // restore name and author from individual level
2138 strcpy(level.name, level_backup.name);
2139 strcpy(level.author, level_backup.author);
2141 // restore flag "use_custom_template"
2142 level.use_custom_template = level_backup.use_custom_template;
2146 static boolean checkForPackageFromBasename_BD(char *basename)
2148 // check for native BD level file extensions
2149 if (!strSuffixLower(basename, ".bd") &&
2150 !strSuffixLower(basename, ".bdr") &&
2151 !strSuffixLower(basename, ".brc") &&
2152 !strSuffixLower(basename, ".gds"))
2155 // check for standard single-level BD files (like "001.bd")
2156 if (strSuffixLower(basename, ".bd") &&
2157 strlen(basename) == 6 &&
2158 basename[0] >= '0' && basename[0] <= '9' &&
2159 basename[1] >= '0' && basename[1] <= '9' &&
2160 basename[2] >= '0' && basename[2] <= '9')
2163 // this is a level package in native BD file format
2167 static char *getLevelFilenameFromBasename(char *basename)
2169 static char *filename = NULL;
2171 checked_free(filename);
2173 filename = getPath2(getCurrentLevelDir(), basename);
2178 static int getFileTypeFromBasename(char *basename)
2180 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2182 static char *filename = NULL;
2183 struct stat file_status;
2185 // ---------- try to determine file type from filename ----------
2187 // check for typical filename of a Supaplex level package file
2188 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2189 return LEVEL_FILE_TYPE_SP;
2191 // check for typical filename of a Diamond Caves II level package file
2192 if (strSuffixLower(basename, ".dc") ||
2193 strSuffixLower(basename, ".dc2"))
2194 return LEVEL_FILE_TYPE_DC;
2196 // check for typical filename of a Sokoban level package file
2197 if (strSuffixLower(basename, ".xsb") &&
2198 strchr(basename, '%') == NULL)
2199 return LEVEL_FILE_TYPE_SB;
2201 // check for typical filename of a Boulder Dash (GDash) level package file
2202 if (checkForPackageFromBasename_BD(basename))
2203 return LEVEL_FILE_TYPE_BD;
2205 // ---------- try to determine file type from filesize ----------
2207 checked_free(filename);
2208 filename = getPath2(getCurrentLevelDir(), basename);
2210 if (stat(filename, &file_status) == 0)
2212 // check for typical filesize of a Supaplex level package file
2213 if (file_status.st_size == 170496)
2214 return LEVEL_FILE_TYPE_SP;
2217 return LEVEL_FILE_TYPE_UNKNOWN;
2220 static int getFileTypeFromMagicBytes(char *filename, int type)
2224 if ((file = openFile(filename, MODE_READ)))
2226 char chunk_name[CHUNK_ID_LEN + 1];
2228 getFileChunkBE(file, chunk_name, NULL);
2230 if (strEqual(chunk_name, "MMII") ||
2231 strEqual(chunk_name, "MIRR"))
2232 type = LEVEL_FILE_TYPE_MM;
2240 static boolean checkForPackageFromBasename(char *basename)
2242 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2243 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2245 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2248 static char *getSingleLevelBasenameExt(int nr, char *extension)
2250 static char basename[MAX_FILENAME_LEN];
2253 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2255 sprintf(basename, "%03d.%s", nr, extension);
2260 static char *getSingleLevelBasename(int nr)
2262 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2265 static char *getPackedLevelBasename(int type)
2267 static char basename[MAX_FILENAME_LEN];
2268 char *directory = getCurrentLevelDir();
2270 DirectoryEntry *dir_entry;
2272 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2274 if ((dir = openDirectory(directory)) == NULL)
2276 Warn("cannot read current level directory '%s'", directory);
2281 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2283 char *entry_basename = dir_entry->basename;
2284 int entry_type = getFileTypeFromBasename(entry_basename);
2286 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2288 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2291 strcpy(basename, entry_basename);
2298 closeDirectory(dir);
2303 static char *getSingleLevelFilename(int nr)
2305 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2308 #if ENABLE_UNUSED_CODE
2309 static char *getPackedLevelFilename(int type)
2311 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2315 char *getDefaultLevelFilename(int nr)
2317 return getSingleLevelFilename(nr);
2320 #if ENABLE_UNUSED_CODE
2321 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2325 lfi->packed = FALSE;
2327 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2328 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2332 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2333 int type, char *format, ...)
2335 static char basename[MAX_FILENAME_LEN];
2338 va_start(ap, format);
2339 vsprintf(basename, format, ap);
2343 lfi->packed = FALSE;
2345 setString(&lfi->basename, basename);
2346 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2349 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2355 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2356 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2359 static int getFiletypeFromID(char *filetype_id)
2361 char *filetype_id_lower;
2362 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2365 if (filetype_id == NULL)
2366 return LEVEL_FILE_TYPE_UNKNOWN;
2368 filetype_id_lower = getStringToLower(filetype_id);
2370 for (i = 0; filetype_id_list[i].id != NULL; i++)
2372 char *id_lower = getStringToLower(filetype_id_list[i].id);
2374 if (strEqual(filetype_id_lower, id_lower))
2375 filetype = filetype_id_list[i].filetype;
2379 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2383 free(filetype_id_lower);
2388 char *getLocalLevelTemplateFilename(void)
2390 return getDefaultLevelFilename(-1);
2393 char *getGlobalLevelTemplateFilename(void)
2395 // global variable "leveldir_current" must be modified in the loop below
2396 LevelDirTree *leveldir_current_last = leveldir_current;
2397 char *filename = NULL;
2399 // check for template level in path from current to topmost tree node
2401 while (leveldir_current != NULL)
2403 filename = getDefaultLevelFilename(-1);
2405 if (fileExists(filename))
2408 leveldir_current = leveldir_current->node_parent;
2411 // restore global variable "leveldir_current" modified in above loop
2412 leveldir_current = leveldir_current_last;
2417 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2421 // special case: level number is negative => check for level template file
2424 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2425 getSingleLevelBasename(-1));
2427 // replace local level template filename with global template filename
2428 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2430 // no fallback if template file not existing
2434 // special case: check for file name/pattern specified in "levelinfo.conf"
2435 if (leveldir_current->level_filename != NULL)
2437 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2439 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2440 leveldir_current->level_filename, nr);
2442 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2444 if (fileExists(lfi->filename))
2447 else if (leveldir_current->level_filetype != NULL)
2449 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2451 // check for specified native level file with standard file name
2452 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2453 "%03d.%s", nr, LEVELFILE_EXTENSION);
2454 if (fileExists(lfi->filename))
2458 // check for native Rocks'n'Diamonds level file
2459 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2460 "%03d.%s", nr, LEVELFILE_EXTENSION);
2461 if (fileExists(lfi->filename))
2464 // check for native Boulder Dash level file
2465 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2466 if (fileExists(lfi->filename))
2469 // check for Emerald Mine level file (V1)
2470 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2471 'a' + (nr / 10) % 26, '0' + nr % 10);
2472 if (fileExists(lfi->filename))
2474 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2475 'A' + (nr / 10) % 26, '0' + nr % 10);
2476 if (fileExists(lfi->filename))
2479 // check for Emerald Mine level file (V2 to V5)
2480 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2481 if (fileExists(lfi->filename))
2484 // check for Emerald Mine level file (V6 / single mode)
2485 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2486 if (fileExists(lfi->filename))
2488 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2489 if (fileExists(lfi->filename))
2492 // check for Emerald Mine level file (V6 / teamwork mode)
2493 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2494 if (fileExists(lfi->filename))
2496 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2497 if (fileExists(lfi->filename))
2500 // check for various packed level file formats
2501 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2502 if (fileExists(lfi->filename))
2505 // no known level file found -- use default values (and fail later)
2506 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2507 "%03d.%s", nr, LEVELFILE_EXTENSION);
2510 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2512 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2513 lfi->type = getFileTypeFromBasename(lfi->basename);
2515 if (lfi->type == LEVEL_FILE_TYPE_RND)
2516 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2519 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2521 // always start with reliable default values
2522 setFileInfoToDefaults(level_file_info);
2524 level_file_info->nr = nr; // set requested level number
2526 determineLevelFileInfo_Filename(level_file_info);
2527 determineLevelFileInfo_Filetype(level_file_info);
2530 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2531 struct LevelFileInfo *lfi_to)
2533 lfi_to->nr = lfi_from->nr;
2534 lfi_to->type = lfi_from->type;
2535 lfi_to->packed = lfi_from->packed;
2537 setString(&lfi_to->basename, lfi_from->basename);
2538 setString(&lfi_to->filename, lfi_from->filename);
2541 // ----------------------------------------------------------------------------
2542 // functions for loading R'n'D level
2543 // ----------------------------------------------------------------------------
2545 int getMappedElement(int element)
2547 // remap some (historic, now obsolete) elements
2551 case EL_PLAYER_OBSOLETE:
2552 element = EL_PLAYER_1;
2555 case EL_KEY_OBSOLETE:
2559 case EL_EM_KEY_1_FILE_OBSOLETE:
2560 element = EL_EM_KEY_1;
2563 case EL_EM_KEY_2_FILE_OBSOLETE:
2564 element = EL_EM_KEY_2;
2567 case EL_EM_KEY_3_FILE_OBSOLETE:
2568 element = EL_EM_KEY_3;
2571 case EL_EM_KEY_4_FILE_OBSOLETE:
2572 element = EL_EM_KEY_4;
2575 case EL_ENVELOPE_OBSOLETE:
2576 element = EL_ENVELOPE_1;
2584 if (element >= NUM_FILE_ELEMENTS)
2586 Warn("invalid level element %d", element);
2588 element = EL_UNKNOWN;
2596 static int getMappedElementByVersion(int element, int game_version)
2598 // remap some elements due to certain game version
2600 if (game_version <= VERSION_IDENT(2,2,0,0))
2602 // map game font elements
2603 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2604 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2605 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2606 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2609 if (game_version < VERSION_IDENT(3,0,0,0))
2611 // map Supaplex gravity tube elements
2612 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2613 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2614 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2615 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2622 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2624 level->file_version = getFileVersion(file);
2625 level->game_version = getFileVersion(file);
2630 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2632 level->creation_date.year = getFile16BitBE(file);
2633 level->creation_date.month = getFile8Bit(file);
2634 level->creation_date.day = getFile8Bit(file);
2636 level->creation_date.src = DATE_SRC_LEVELFILE;
2641 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2643 int initial_player_stepsize;
2644 int initial_player_gravity;
2647 level->fieldx = getFile8Bit(file);
2648 level->fieldy = getFile8Bit(file);
2650 level->time = getFile16BitBE(file);
2651 level->gems_needed = getFile16BitBE(file);
2653 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2654 level->name[i] = getFile8Bit(file);
2655 level->name[MAX_LEVEL_NAME_LEN] = 0;
2657 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2658 level->score[i] = getFile8Bit(file);
2660 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2661 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2662 for (y = 0; y < 3; y++)
2663 for (x = 0; x < 3; x++)
2664 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2666 level->amoeba_speed = getFile8Bit(file);
2667 level->time_magic_wall = getFile8Bit(file);
2668 level->time_wheel = getFile8Bit(file);
2669 level->amoeba_content = getMappedElement(getFile8Bit(file));
2671 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2674 for (i = 0; i < MAX_PLAYERS; i++)
2675 level->initial_player_stepsize[i] = initial_player_stepsize;
2677 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2679 for (i = 0; i < MAX_PLAYERS; i++)
2680 level->initial_player_gravity[i] = initial_player_gravity;
2682 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2683 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2685 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2687 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2688 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2689 level->can_move_into_acid_bits = getFile32BitBE(file);
2690 level->dont_collide_with_bits = getFile8Bit(file);
2692 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2693 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2695 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2696 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2697 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2699 level->game_engine_type = getFile8Bit(file);
2701 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2706 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2710 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2711 level->name[i] = getFile8Bit(file);
2712 level->name[MAX_LEVEL_NAME_LEN] = 0;
2717 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2721 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2722 level->author[i] = getFile8Bit(file);
2723 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2728 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2731 int chunk_size_expected = level->fieldx * level->fieldy;
2733 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2734 stored with 16-bit encoding (and should be twice as big then).
2735 Even worse, playfield data was stored 16-bit when only yamyam content
2736 contained 16-bit elements and vice versa. */
2738 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2739 chunk_size_expected *= 2;
2741 if (chunk_size_expected != chunk_size)
2743 ReadUnusedBytesFromFile(file, chunk_size);
2744 return chunk_size_expected;
2747 for (y = 0; y < level->fieldy; y++)
2748 for (x = 0; x < level->fieldx; x++)
2749 level->field[x][y] =
2750 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2755 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2758 int header_size = 4;
2759 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2760 int chunk_size_expected = header_size + content_size;
2762 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2763 stored with 16-bit encoding (and should be twice as big then).
2764 Even worse, playfield data was stored 16-bit when only yamyam content
2765 contained 16-bit elements and vice versa. */
2767 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2768 chunk_size_expected += content_size;
2770 if (chunk_size_expected != chunk_size)
2772 ReadUnusedBytesFromFile(file, chunk_size);
2773 return chunk_size_expected;
2777 level->num_yamyam_contents = getFile8Bit(file);
2781 // correct invalid number of content fields -- should never happen
2782 if (level->num_yamyam_contents < 1 ||
2783 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2784 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2786 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2787 for (y = 0; y < 3; y++)
2788 for (x = 0; x < 3; x++)
2789 level->yamyam_content[i].e[x][y] =
2790 getMappedElement(level->encoding_16bit_field ?
2791 getFile16BitBE(file) : getFile8Bit(file));
2795 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2800 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2802 element = getMappedElement(getFile16BitBE(file));
2803 num_contents = getFile8Bit(file);
2805 getFile8Bit(file); // content x size (unused)
2806 getFile8Bit(file); // content y size (unused)
2808 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2810 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2811 for (y = 0; y < 3; y++)
2812 for (x = 0; x < 3; x++)
2813 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2815 // correct invalid number of content fields -- should never happen
2816 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2817 num_contents = STD_ELEMENT_CONTENTS;
2819 if (element == EL_YAMYAM)
2821 level->num_yamyam_contents = num_contents;
2823 for (i = 0; i < num_contents; i++)
2824 for (y = 0; y < 3; y++)
2825 for (x = 0; x < 3; x++)
2826 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2828 else if (element == EL_BD_AMOEBA)
2830 level->amoeba_content = content_array[0][0][0];
2834 Warn("cannot load content for element '%d'", element);
2840 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2846 int chunk_size_expected;
2848 element = getMappedElement(getFile16BitBE(file));
2849 if (!IS_ENVELOPE(element))
2850 element = EL_ENVELOPE_1;
2852 envelope_nr = element - EL_ENVELOPE_1;
2854 envelope_len = getFile16BitBE(file);
2856 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2857 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2859 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2861 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2862 if (chunk_size_expected != chunk_size)
2864 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2865 return chunk_size_expected;
2868 for (i = 0; i < envelope_len; i++)
2869 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2874 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2876 int num_changed_custom_elements = getFile16BitBE(file);
2877 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2880 if (chunk_size_expected != chunk_size)
2882 ReadUnusedBytesFromFile(file, chunk_size - 2);
2883 return chunk_size_expected;
2886 for (i = 0; i < num_changed_custom_elements; i++)
2888 int element = getMappedElement(getFile16BitBE(file));
2889 int properties = getFile32BitBE(file);
2891 if (IS_CUSTOM_ELEMENT(element))
2892 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2894 Warn("invalid custom element number %d", element);
2896 // older game versions that wrote level files with CUS1 chunks used
2897 // different default push delay values (not yet stored in level file)
2898 element_info[element].push_delay_fixed = 2;
2899 element_info[element].push_delay_random = 8;
2902 level->file_has_custom_elements = TRUE;
2907 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2909 int num_changed_custom_elements = getFile16BitBE(file);
2910 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2913 if (chunk_size_expected != chunk_size)
2915 ReadUnusedBytesFromFile(file, chunk_size - 2);
2916 return chunk_size_expected;
2919 for (i = 0; i < num_changed_custom_elements; i++)
2921 int element = getMappedElement(getFile16BitBE(file));
2922 int custom_target_element = getMappedElement(getFile16BitBE(file));
2924 if (IS_CUSTOM_ELEMENT(element))
2925 element_info[element].change->target_element = custom_target_element;
2927 Warn("invalid custom element number %d", element);
2930 level->file_has_custom_elements = TRUE;
2935 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2937 int num_changed_custom_elements = getFile16BitBE(file);
2938 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2941 if (chunk_size_expected != chunk_size)
2943 ReadUnusedBytesFromFile(file, chunk_size - 2);
2944 return chunk_size_expected;
2947 for (i = 0; i < num_changed_custom_elements; i++)
2949 int element = getMappedElement(getFile16BitBE(file));
2950 struct ElementInfo *ei = &element_info[element];
2951 unsigned int event_bits;
2953 if (!IS_CUSTOM_ELEMENT(element))
2955 Warn("invalid custom element number %d", element);
2957 element = EL_INTERNAL_DUMMY;
2960 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2961 ei->description[j] = getFile8Bit(file);
2962 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2964 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2966 // some free bytes for future properties and padding
2967 ReadUnusedBytesFromFile(file, 7);
2969 ei->use_gfx_element = getFile8Bit(file);
2970 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2972 ei->collect_score_initial = getFile8Bit(file);
2973 ei->collect_count_initial = getFile8Bit(file);
2975 ei->push_delay_fixed = getFile16BitBE(file);
2976 ei->push_delay_random = getFile16BitBE(file);
2977 ei->move_delay_fixed = getFile16BitBE(file);
2978 ei->move_delay_random = getFile16BitBE(file);
2980 ei->move_pattern = getFile16BitBE(file);
2981 ei->move_direction_initial = getFile8Bit(file);
2982 ei->move_stepsize = getFile8Bit(file);
2984 for (y = 0; y < 3; y++)
2985 for (x = 0; x < 3; x++)
2986 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2988 // bits 0 - 31 of "has_event[]"
2989 event_bits = getFile32BitBE(file);
2990 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2991 if (event_bits & (1u << j))
2992 ei->change->has_event[j] = TRUE;
2994 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2996 ei->change->delay_fixed = getFile16BitBE(file);
2997 ei->change->delay_random = getFile16BitBE(file);
2998 ei->change->delay_frames = getFile16BitBE(file);
3000 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3002 ei->change->explode = getFile8Bit(file);
3003 ei->change->use_target_content = getFile8Bit(file);
3004 ei->change->only_if_complete = getFile8Bit(file);
3005 ei->change->use_random_replace = getFile8Bit(file);
3007 ei->change->random_percentage = getFile8Bit(file);
3008 ei->change->replace_when = getFile8Bit(file);
3010 for (y = 0; y < 3; y++)
3011 for (x = 0; x < 3; x++)
3012 ei->change->target_content.e[x][y] =
3013 getMappedElement(getFile16BitBE(file));
3015 ei->slippery_type = getFile8Bit(file);
3017 // some free bytes for future properties and padding
3018 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3020 // mark that this custom element has been modified
3021 ei->modified_settings = TRUE;
3024 level->file_has_custom_elements = TRUE;
3029 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3031 struct ElementInfo *ei;
3032 int chunk_size_expected;
3036 // ---------- custom element base property values (96 bytes) ----------------
3038 element = getMappedElement(getFile16BitBE(file));
3040 if (!IS_CUSTOM_ELEMENT(element))
3042 Warn("invalid custom element number %d", element);
3044 ReadUnusedBytesFromFile(file, chunk_size - 2);
3049 ei = &element_info[element];
3051 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3052 ei->description[i] = getFile8Bit(file);
3053 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3055 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3057 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3059 ei->num_change_pages = getFile8Bit(file);
3061 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3062 if (chunk_size_expected != chunk_size)
3064 ReadUnusedBytesFromFile(file, chunk_size - 43);
3065 return chunk_size_expected;
3068 ei->ce_value_fixed_initial = getFile16BitBE(file);
3069 ei->ce_value_random_initial = getFile16BitBE(file);
3070 ei->use_last_ce_value = getFile8Bit(file);
3072 ei->use_gfx_element = getFile8Bit(file);
3073 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3075 ei->collect_score_initial = getFile8Bit(file);
3076 ei->collect_count_initial = getFile8Bit(file);
3078 ei->drop_delay_fixed = getFile8Bit(file);
3079 ei->push_delay_fixed = getFile8Bit(file);
3080 ei->drop_delay_random = getFile8Bit(file);
3081 ei->push_delay_random = getFile8Bit(file);
3082 ei->move_delay_fixed = getFile16BitBE(file);
3083 ei->move_delay_random = getFile16BitBE(file);
3085 // bits 0 - 15 of "move_pattern" ...
3086 ei->move_pattern = getFile16BitBE(file);
3087 ei->move_direction_initial = getFile8Bit(file);
3088 ei->move_stepsize = getFile8Bit(file);
3090 ei->slippery_type = getFile8Bit(file);
3092 for (y = 0; y < 3; y++)
3093 for (x = 0; x < 3; x++)
3094 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3096 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3097 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3098 ei->move_leave_type = getFile8Bit(file);
3100 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3101 ei->move_pattern |= (getFile16BitBE(file) << 16);
3103 ei->access_direction = getFile8Bit(file);
3105 ei->explosion_delay = getFile8Bit(file);
3106 ei->ignition_delay = getFile8Bit(file);
3107 ei->explosion_type = getFile8Bit(file);
3109 // some free bytes for future custom property values and padding
3110 ReadUnusedBytesFromFile(file, 1);
3112 // ---------- change page property values (48 bytes) ------------------------
3114 setElementChangePages(ei, ei->num_change_pages);
3116 for (i = 0; i < ei->num_change_pages; i++)
3118 struct ElementChangeInfo *change = &ei->change_page[i];
3119 unsigned int event_bits;
3121 // always start with reliable default values
3122 setElementChangeInfoToDefaults(change);
3124 // bits 0 - 31 of "has_event[]" ...
3125 event_bits = getFile32BitBE(file);
3126 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3127 if (event_bits & (1u << j))
3128 change->has_event[j] = TRUE;
3130 change->target_element = getMappedElement(getFile16BitBE(file));
3132 change->delay_fixed = getFile16BitBE(file);
3133 change->delay_random = getFile16BitBE(file);
3134 change->delay_frames = getFile16BitBE(file);
3136 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3138 change->explode = getFile8Bit(file);
3139 change->use_target_content = getFile8Bit(file);
3140 change->only_if_complete = getFile8Bit(file);
3141 change->use_random_replace = getFile8Bit(file);
3143 change->random_percentage = getFile8Bit(file);
3144 change->replace_when = getFile8Bit(file);
3146 for (y = 0; y < 3; y++)
3147 for (x = 0; x < 3; x++)
3148 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3150 change->can_change = getFile8Bit(file);
3152 change->trigger_side = getFile8Bit(file);
3154 change->trigger_player = getFile8Bit(file);
3155 change->trigger_page = getFile8Bit(file);
3157 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3158 CH_PAGE_ANY : (1 << change->trigger_page));
3160 change->has_action = getFile8Bit(file);
3161 change->action_type = getFile8Bit(file);
3162 change->action_mode = getFile8Bit(file);
3163 change->action_arg = getFile16BitBE(file);
3165 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3166 event_bits = getFile8Bit(file);
3167 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3168 if (event_bits & (1u << (j - 32)))
3169 change->has_event[j] = TRUE;
3172 // mark this custom element as modified
3173 ei->modified_settings = TRUE;
3175 level->file_has_custom_elements = TRUE;
3180 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3182 struct ElementInfo *ei;
3183 struct ElementGroupInfo *group;
3187 element = getMappedElement(getFile16BitBE(file));
3189 if (!IS_GROUP_ELEMENT(element))
3191 Warn("invalid group element number %d", element);
3193 ReadUnusedBytesFromFile(file, chunk_size - 2);
3198 ei = &element_info[element];
3200 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3201 ei->description[i] = getFile8Bit(file);
3202 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3204 group = element_info[element].group;
3206 group->num_elements = getFile8Bit(file);
3208 ei->use_gfx_element = getFile8Bit(file);
3209 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3211 group->choice_mode = getFile8Bit(file);
3213 // some free bytes for future values and padding
3214 ReadUnusedBytesFromFile(file, 3);
3216 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3217 group->element[i] = getMappedElement(getFile16BitBE(file));
3219 // mark this group element as modified
3220 element_info[element].modified_settings = TRUE;
3222 level->file_has_custom_elements = TRUE;
3227 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3228 int element, int real_element)
3230 int micro_chunk_size = 0;
3231 int conf_type = getFile8Bit(file);
3232 int byte_mask = conf_type & CONF_MASK_BYTES;
3233 boolean element_found = FALSE;
3236 micro_chunk_size += 1;
3238 if (byte_mask == CONF_MASK_MULTI_BYTES)
3240 int num_bytes = getFile16BitBE(file);
3241 byte *buffer = checked_malloc(num_bytes);
3243 ReadBytesFromFile(file, buffer, num_bytes);
3245 for (i = 0; conf[i].data_type != -1; i++)
3247 if (conf[i].element == element &&
3248 conf[i].conf_type == conf_type)
3250 int data_type = conf[i].data_type;
3251 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3252 int max_num_entities = conf[i].max_num_entities;
3254 if (num_entities > max_num_entities)
3256 Warn("truncating number of entities for element %d from %d to %d",
3257 element, num_entities, max_num_entities);
3259 num_entities = max_num_entities;
3262 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3263 data_type == TYPE_CONTENT_LIST))
3265 // for element and content lists, zero entities are not allowed
3266 Warn("found empty list of entities for element %d", element);
3268 // do not set "num_entities" here to prevent reading behind buffer
3270 *(int *)(conf[i].num_entities) = 1; // at least one is required
3274 *(int *)(conf[i].num_entities) = num_entities;
3277 element_found = TRUE;
3279 if (data_type == TYPE_STRING)
3281 char *string = (char *)(conf[i].value);
3284 for (j = 0; j < max_num_entities; j++)
3285 string[j] = (j < num_entities ? buffer[j] : '\0');
3287 else if (data_type == TYPE_ELEMENT_LIST)
3289 int *element_array = (int *)(conf[i].value);
3292 for (j = 0; j < num_entities; j++)
3294 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3296 else if (data_type == TYPE_CONTENT_LIST)
3298 struct Content *content= (struct Content *)(conf[i].value);
3301 for (c = 0; c < num_entities; c++)
3302 for (y = 0; y < 3; y++)
3303 for (x = 0; x < 3; x++)
3304 content[c].e[x][y] =
3305 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3308 element_found = FALSE;
3314 checked_free(buffer);
3316 micro_chunk_size += 2 + num_bytes;
3318 else // constant size configuration data (1, 2 or 4 bytes)
3320 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3321 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3322 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3324 for (i = 0; conf[i].data_type != -1; i++)
3326 if (conf[i].element == element &&
3327 conf[i].conf_type == conf_type)
3329 int data_type = conf[i].data_type;
3331 if (data_type == TYPE_ELEMENT)
3332 value = getMappedElement(value);
3334 if (data_type == TYPE_BOOLEAN)
3335 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3337 *(int *) (conf[i].value) = value;
3339 element_found = TRUE;
3345 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3350 char *error_conf_chunk_bytes =
3351 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3352 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3353 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3354 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3355 int error_element = real_element;
3357 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3358 error_conf_chunk_bytes, error_conf_chunk_token,
3359 error_element, EL_NAME(error_element));
3362 return micro_chunk_size;
3365 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3367 int real_chunk_size = 0;
3369 li = *level; // copy level data into temporary buffer
3371 while (!checkEndOfFile(file))
3373 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3375 if (real_chunk_size >= chunk_size)
3379 *level = li; // copy temporary buffer back to level data
3381 return real_chunk_size;
3384 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3386 int real_chunk_size = 0;
3388 li = *level; // copy level data into temporary buffer
3390 while (!checkEndOfFile(file))
3392 int element = getMappedElement(getFile16BitBE(file));
3394 real_chunk_size += 2;
3395 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3397 if (real_chunk_size >= chunk_size)
3401 *level = li; // copy temporary buffer back to level data
3403 return real_chunk_size;
3406 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3408 int real_chunk_size = 0;
3410 li = *level; // copy level data into temporary buffer
3412 while (!checkEndOfFile(file))
3414 int element = getMappedElement(getFile16BitBE(file));
3416 real_chunk_size += 2;
3417 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3419 if (real_chunk_size >= chunk_size)
3423 *level = li; // copy temporary buffer back to level data
3425 return real_chunk_size;
3428 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3430 int element = getMappedElement(getFile16BitBE(file));
3431 int envelope_nr = element - EL_ENVELOPE_1;
3432 int real_chunk_size = 2;
3434 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3436 while (!checkEndOfFile(file))
3438 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3441 if (real_chunk_size >= chunk_size)
3445 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3447 return real_chunk_size;
3450 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3452 int element = getMappedElement(getFile16BitBE(file));
3453 int real_chunk_size = 2;
3454 struct ElementInfo *ei = &element_info[element];
3457 xx_ei = *ei; // copy element data into temporary buffer
3459 xx_ei.num_change_pages = -1;
3461 while (!checkEndOfFile(file))
3463 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3465 if (xx_ei.num_change_pages != -1)
3468 if (real_chunk_size >= chunk_size)
3474 if (ei->num_change_pages == -1)
3476 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3479 ei->num_change_pages = 1;
3481 setElementChangePages(ei, 1);
3482 setElementChangeInfoToDefaults(ei->change);
3484 return real_chunk_size;
3487 // initialize number of change pages stored for this custom element
3488 setElementChangePages(ei, ei->num_change_pages);
3489 for (i = 0; i < ei->num_change_pages; i++)
3490 setElementChangeInfoToDefaults(&ei->change_page[i]);
3492 // start with reading properties for the first change page
3493 xx_current_change_page = 0;
3495 while (!checkEndOfFile(file))
3497 // level file might contain invalid change page number
3498 if (xx_current_change_page >= ei->num_change_pages)
3501 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3503 xx_change = *change; // copy change data into temporary buffer
3505 resetEventBits(); // reset bits; change page might have changed
3507 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3510 *change = xx_change;
3512 setEventFlagsFromEventBits(change);
3514 if (real_chunk_size >= chunk_size)
3518 level->file_has_custom_elements = TRUE;
3520 return real_chunk_size;
3523 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3525 int element = getMappedElement(getFile16BitBE(file));
3526 int real_chunk_size = 2;
3527 struct ElementInfo *ei = &element_info[element];
3528 struct ElementGroupInfo *group = ei->group;
3533 xx_ei = *ei; // copy element data into temporary buffer
3534 xx_group = *group; // copy group data into temporary buffer
3536 while (!checkEndOfFile(file))
3538 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3541 if (real_chunk_size >= chunk_size)
3548 level->file_has_custom_elements = TRUE;
3550 return real_chunk_size;
3553 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3555 int element = getMappedElement(getFile16BitBE(file));
3556 int real_chunk_size = 2;
3557 struct ElementInfo *ei = &element_info[element];
3559 xx_ei = *ei; // copy element data into temporary buffer
3561 while (!checkEndOfFile(file))
3563 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3566 if (real_chunk_size >= chunk_size)
3572 level->file_has_custom_elements = TRUE;
3574 return real_chunk_size;
3577 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3578 struct LevelFileInfo *level_file_info,
3579 boolean level_info_only)
3581 char *filename = level_file_info->filename;
3582 char cookie[MAX_LINE_LEN];
3583 char chunk_name[CHUNK_ID_LEN + 1];
3587 if (!(file = openFile(filename, MODE_READ)))
3589 level->no_valid_file = TRUE;
3590 level->no_level_file = TRUE;
3592 if (level_info_only)
3595 Warn("cannot read level '%s' -- using empty level", filename);
3597 if (!setup.editor.use_template_for_new_levels)
3600 // if level file not found, try to initialize level data from template
3601 filename = getGlobalLevelTemplateFilename();
3603 if (!(file = openFile(filename, MODE_READ)))
3606 // default: for empty levels, use level template for custom elements
3607 level->use_custom_template = TRUE;
3609 level->no_valid_file = FALSE;
3612 getFileChunkBE(file, chunk_name, NULL);
3613 if (strEqual(chunk_name, "RND1"))
3615 getFile32BitBE(file); // not used
3617 getFileChunkBE(file, chunk_name, NULL);
3618 if (!strEqual(chunk_name, "CAVE"))
3620 level->no_valid_file = TRUE;
3622 Warn("unknown format of level file '%s'", filename);
3629 else // check for pre-2.0 file format with cookie string
3631 strcpy(cookie, chunk_name);
3632 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3634 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3635 cookie[strlen(cookie) - 1] = '\0';
3637 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3639 level->no_valid_file = TRUE;
3641 Warn("unknown format of level file '%s'", filename);
3648 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3650 level->no_valid_file = TRUE;
3652 Warn("unsupported version of level file '%s'", filename);
3659 // pre-2.0 level files have no game version, so use file version here
3660 level->game_version = level->file_version;
3663 if (level->file_version < FILE_VERSION_1_2)
3665 // level files from versions before 1.2.0 without chunk structure
3666 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3667 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3675 int (*loader)(File *, int, struct LevelInfo *);
3679 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3680 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3681 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3682 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3683 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3684 { "INFO", -1, LoadLevel_INFO },
3685 { "BODY", -1, LoadLevel_BODY },
3686 { "CONT", -1, LoadLevel_CONT },
3687 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3688 { "CNT3", -1, LoadLevel_CNT3 },
3689 { "CUS1", -1, LoadLevel_CUS1 },
3690 { "CUS2", -1, LoadLevel_CUS2 },
3691 { "CUS3", -1, LoadLevel_CUS3 },
3692 { "CUS4", -1, LoadLevel_CUS4 },
3693 { "GRP1", -1, LoadLevel_GRP1 },
3694 { "CONF", -1, LoadLevel_CONF },
3695 { "ELEM", -1, LoadLevel_ELEM },
3696 { "NOTE", -1, LoadLevel_NOTE },
3697 { "CUSX", -1, LoadLevel_CUSX },
3698 { "GRPX", -1, LoadLevel_GRPX },
3699 { "EMPX", -1, LoadLevel_EMPX },
3704 while (getFileChunkBE(file, chunk_name, &chunk_size))
3708 while (chunk_info[i].name != NULL &&
3709 !strEqual(chunk_name, chunk_info[i].name))
3712 if (chunk_info[i].name == NULL)
3714 Warn("unknown chunk '%s' in level file '%s'",
3715 chunk_name, filename);
3717 ReadUnusedBytesFromFile(file, chunk_size);
3719 else if (chunk_info[i].size != -1 &&
3720 chunk_info[i].size != chunk_size)
3722 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3723 chunk_size, chunk_name, filename);
3725 ReadUnusedBytesFromFile(file, chunk_size);
3729 // call function to load this level chunk
3730 int chunk_size_expected =
3731 (chunk_info[i].loader)(file, chunk_size, level);
3733 if (chunk_size_expected < 0)
3735 Warn("error reading chunk '%s' in level file '%s'",
3736 chunk_name, filename);
3741 // the size of some chunks cannot be checked before reading other
3742 // chunks first (like "HEAD" and "BODY") that contain some header
3743 // information, so check them here
3744 if (chunk_size_expected != chunk_size)
3746 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3747 chunk_size, chunk_name, filename);
3759 // ----------------------------------------------------------------------------
3760 // functions for loading BD level
3761 // ----------------------------------------------------------------------------
3763 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3765 struct LevelInfo_BD *level_bd = level->native_bd_level;
3766 GdCave *cave = NULL; // will be changed below
3767 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3768 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3771 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3773 // cave and map newly allocated when set to defaults above
3774 cave = level_bd->cave;
3777 cave->intermission = level->bd_intermission;
3780 cave->level_time[0] = level->time;
3781 cave->level_diamonds[0] = level->gems_needed;
3784 cave->scheduling = level->bd_scheduling_type;
3785 cave->pal_timing = level->bd_pal_timing;
3786 cave->level_speed[0] = level->bd_cycle_delay_ms;
3787 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3788 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3789 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3792 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3793 cave->diamond_value = level->score[SC_EMERALD];
3794 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3796 // compatibility settings
3797 cave->lineshift = level->bd_line_shifting_borders;
3798 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3799 cave->short_explosions = level->bd_short_explosions;
3800 cave->gravity_affects_all = level->bd_gravity_affects_all;
3802 // player properties
3803 cave->diagonal_movements = level->bd_diagonal_movements;
3804 cave->active_is_first_found = level->bd_topmost_player_active;
3805 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3806 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3807 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3809 // element properties
3810 cave->level_magic_wall_time[0] = level->time_magic_wall;
3811 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
3812 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
3815 strncpy(cave->name, level->name, sizeof(GdString));
3816 cave->name[sizeof(GdString) - 1] = '\0';
3818 // playfield elements
3819 for (x = 0; x < cave->w; x++)
3820 for (y = 0; y < cave->h; y++)
3821 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3824 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3826 struct LevelInfo_BD *level_bd = level->native_bd_level;
3827 GdCave *cave = level_bd->cave;
3828 int bd_level_nr = level_bd->level_nr;
3831 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3832 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3835 level->bd_intermission = cave->intermission;
3838 level->time = cave->level_time[bd_level_nr];
3839 level->gems_needed = cave->level_diamonds[bd_level_nr];
3842 level->bd_scheduling_type = cave->scheduling;
3843 level->bd_pal_timing = cave->pal_timing;
3844 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3845 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3846 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3847 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3850 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3851 level->score[SC_EMERALD] = cave->diamond_value;
3852 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3854 // compatibility settings
3855 level->bd_line_shifting_borders = cave->lineshift;
3856 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
3857 level->bd_short_explosions = cave->short_explosions;
3858 level->bd_gravity_affects_all = cave->gravity_affects_all;
3860 // player properties
3861 level->bd_diagonal_movements = cave->diagonal_movements;
3862 level->bd_topmost_player_active = cave->active_is_first_found;
3863 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
3864 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
3865 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
3867 // element properties
3868 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3869 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
3870 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
3873 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3875 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3876 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3878 // playfield elements
3879 for (x = 0; x < level->fieldx; x++)
3880 for (y = 0; y < level->fieldy; y++)
3881 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3883 checked_free(cave_name);
3886 static void setTapeInfoToDefaults(void);
3888 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3890 struct LevelInfo_BD *level_bd = level->native_bd_level;
3891 GdCave *cave = level_bd->cave;
3892 GdReplay *replay = level_bd->replay;
3898 // always start with reliable default values
3899 setTapeInfoToDefaults();
3901 tape.level_nr = level_nr; // (currently not used)
3902 tape.random_seed = replay->seed;
3904 TapeSetDateFromIsoDateString(replay->date);
3907 tape.pos[tape.counter].delay = 0;
3909 tape.bd_replay = TRUE;
3911 // all time calculations only used to display approximate tape time
3912 int cave_speed = cave->speed;
3913 int milliseconds_game = 0;
3914 int milliseconds_elapsed = 20;
3916 for (i = 0; i < replay->movements->len; i++)
3918 int replay_action = replay->movements->data[i];
3919 int tape_action = map_action_BD_to_RND(replay_action);
3920 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3921 boolean success = 0;
3925 success = TapeAddAction(action);
3927 milliseconds_game += milliseconds_elapsed;
3929 if (milliseconds_game >= cave_speed)
3931 milliseconds_game -= cave_speed;
3938 tape.pos[tape.counter].delay = 0;
3939 tape.pos[tape.counter].action[0] = 0;
3943 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3949 TapeHaltRecording();
3953 // ----------------------------------------------------------------------------
3954 // functions for loading EM level
3955 // ----------------------------------------------------------------------------
3957 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3959 static int ball_xy[8][2] =
3970 struct LevelInfo_EM *level_em = level->native_em_level;
3971 struct CAVE *cav = level_em->cav;
3974 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3975 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3977 cav->time_seconds = level->time;
3978 cav->gems_needed = level->gems_needed;
3980 cav->emerald_score = level->score[SC_EMERALD];
3981 cav->diamond_score = level->score[SC_DIAMOND];
3982 cav->alien_score = level->score[SC_ROBOT];
3983 cav->tank_score = level->score[SC_SPACESHIP];
3984 cav->bug_score = level->score[SC_BUG];
3985 cav->eater_score = level->score[SC_YAMYAM];
3986 cav->nut_score = level->score[SC_NUT];
3987 cav->dynamite_score = level->score[SC_DYNAMITE];
3988 cav->key_score = level->score[SC_KEY];
3989 cav->exit_score = level->score[SC_TIME_BONUS];
3991 cav->num_eater_arrays = level->num_yamyam_contents;
3993 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3994 for (y = 0; y < 3; y++)
3995 for (x = 0; x < 3; x++)
3996 cav->eater_array[i][y * 3 + x] =
3997 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3999 cav->amoeba_time = level->amoeba_speed;
4000 cav->wonderwall_time = level->time_magic_wall;
4001 cav->wheel_time = level->time_wheel;
4003 cav->android_move_time = level->android_move_time;
4004 cav->android_clone_time = level->android_clone_time;
4005 cav->ball_random = level->ball_random;
4006 cav->ball_active = level->ball_active_initial;
4007 cav->ball_time = level->ball_time;
4008 cav->num_ball_arrays = level->num_ball_contents;
4010 cav->lenses_score = level->lenses_score;
4011 cav->magnify_score = level->magnify_score;
4012 cav->slurp_score = level->slurp_score;
4014 cav->lenses_time = level->lenses_time;
4015 cav->magnify_time = level->magnify_time;
4017 cav->wind_time = 9999;
4018 cav->wind_direction =
4019 map_direction_RND_to_EM(level->wind_direction_initial);
4021 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4022 for (j = 0; j < 8; j++)
4023 cav->ball_array[i][j] =
4024 map_element_RND_to_EM_cave(level->ball_content[i].
4025 e[ball_xy[j][0]][ball_xy[j][1]]);
4027 map_android_clone_elements_RND_to_EM(level);
4029 // first fill the complete playfield with the empty space element
4030 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4031 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4032 cav->cave[x][y] = Cblank;
4034 // then copy the real level contents from level file into the playfield
4035 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4037 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4039 if (level->field[x][y] == EL_AMOEBA_DEAD)
4040 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4042 cav->cave[x][y] = new_element;
4045 for (i = 0; i < MAX_PLAYERS; i++)
4047 cav->player_x[i] = -1;
4048 cav->player_y[i] = -1;
4051 // initialize player positions and delete players from the playfield
4052 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4054 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4056 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4058 cav->player_x[player_nr] = x;
4059 cav->player_y[player_nr] = y;
4061 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4066 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4068 static int ball_xy[8][2] =
4079 struct LevelInfo_EM *level_em = level->native_em_level;
4080 struct CAVE *cav = level_em->cav;
4083 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4084 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4086 level->time = cav->time_seconds;
4087 level->gems_needed = cav->gems_needed;
4089 sprintf(level->name, "Level %d", level->file_info.nr);
4091 level->score[SC_EMERALD] = cav->emerald_score;
4092 level->score[SC_DIAMOND] = cav->diamond_score;
4093 level->score[SC_ROBOT] = cav->alien_score;
4094 level->score[SC_SPACESHIP] = cav->tank_score;
4095 level->score[SC_BUG] = cav->bug_score;
4096 level->score[SC_YAMYAM] = cav->eater_score;
4097 level->score[SC_NUT] = cav->nut_score;
4098 level->score[SC_DYNAMITE] = cav->dynamite_score;
4099 level->score[SC_KEY] = cav->key_score;
4100 level->score[SC_TIME_BONUS] = cav->exit_score;
4102 level->num_yamyam_contents = cav->num_eater_arrays;
4104 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4105 for (y = 0; y < 3; y++)
4106 for (x = 0; x < 3; x++)
4107 level->yamyam_content[i].e[x][y] =
4108 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4110 level->amoeba_speed = cav->amoeba_time;
4111 level->time_magic_wall = cav->wonderwall_time;
4112 level->time_wheel = cav->wheel_time;
4114 level->android_move_time = cav->android_move_time;
4115 level->android_clone_time = cav->android_clone_time;
4116 level->ball_random = cav->ball_random;
4117 level->ball_active_initial = cav->ball_active;
4118 level->ball_time = cav->ball_time;
4119 level->num_ball_contents = cav->num_ball_arrays;
4121 level->lenses_score = cav->lenses_score;
4122 level->magnify_score = cav->magnify_score;
4123 level->slurp_score = cav->slurp_score;
4125 level->lenses_time = cav->lenses_time;
4126 level->magnify_time = cav->magnify_time;
4128 level->wind_direction_initial =
4129 map_direction_EM_to_RND(cav->wind_direction);
4131 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4132 for (j = 0; j < 8; j++)
4133 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4134 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4136 map_android_clone_elements_EM_to_RND(level);
4138 // convert the playfield (some elements need special treatment)
4139 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4141 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4143 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4144 new_element = EL_AMOEBA_DEAD;
4146 level->field[x][y] = new_element;
4149 for (i = 0; i < MAX_PLAYERS; i++)
4151 // in case of all players set to the same field, use the first player
4152 int nr = MAX_PLAYERS - i - 1;
4153 int jx = cav->player_x[nr];
4154 int jy = cav->player_y[nr];
4156 if (jx != -1 && jy != -1)
4157 level->field[jx][jy] = EL_PLAYER_1 + nr;
4160 // time score is counted for each 10 seconds left in Emerald Mine levels
4161 level->time_score_base = 10;
4165 // ----------------------------------------------------------------------------
4166 // functions for loading SP level
4167 // ----------------------------------------------------------------------------
4169 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4171 struct LevelInfo_SP *level_sp = level->native_sp_level;
4172 LevelInfoType *header = &level_sp->header;
4175 level_sp->width = level->fieldx;
4176 level_sp->height = level->fieldy;
4178 for (x = 0; x < level->fieldx; x++)
4179 for (y = 0; y < level->fieldy; y++)
4180 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4182 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4184 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4185 header->LevelTitle[i] = level->name[i];
4186 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4188 header->InfotronsNeeded = level->gems_needed;
4190 header->SpecialPortCount = 0;
4192 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4194 boolean gravity_port_found = FALSE;
4195 boolean gravity_port_valid = FALSE;
4196 int gravity_port_flag;
4197 int gravity_port_base_element;
4198 int element = level->field[x][y];
4200 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4201 element <= EL_SP_GRAVITY_ON_PORT_UP)
4203 gravity_port_found = TRUE;
4204 gravity_port_valid = TRUE;
4205 gravity_port_flag = 1;
4206 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4208 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4209 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4211 gravity_port_found = TRUE;
4212 gravity_port_valid = TRUE;
4213 gravity_port_flag = 0;
4214 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4216 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4217 element <= EL_SP_GRAVITY_PORT_UP)
4219 // change R'n'D style gravity inverting special port to normal port
4220 // (there are no gravity inverting ports in native Supaplex engine)
4222 gravity_port_found = TRUE;
4223 gravity_port_valid = FALSE;
4224 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4227 if (gravity_port_found)
4229 if (gravity_port_valid &&
4230 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4232 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4234 port->PortLocation = (y * level->fieldx + x) * 2;
4235 port->Gravity = gravity_port_flag;
4237 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4239 header->SpecialPortCount++;
4243 // change special gravity port to normal port
4245 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4248 level_sp->playfield[x][y] = element - EL_SP_START;
4253 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4255 struct LevelInfo_SP *level_sp = level->native_sp_level;
4256 LevelInfoType *header = &level_sp->header;
4257 boolean num_invalid_elements = 0;
4260 level->fieldx = level_sp->width;
4261 level->fieldy = level_sp->height;
4263 for (x = 0; x < level->fieldx; x++)
4265 for (y = 0; y < level->fieldy; y++)
4267 int element_old = level_sp->playfield[x][y];
4268 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4270 if (element_new == EL_UNKNOWN)
4272 num_invalid_elements++;
4274 Debug("level:native:SP", "invalid element %d at position %d, %d",
4278 level->field[x][y] = element_new;
4282 if (num_invalid_elements > 0)
4283 Warn("found %d invalid elements%s", num_invalid_elements,
4284 (!options.debug ? " (use '--debug' for more details)" : ""));
4286 for (i = 0; i < MAX_PLAYERS; i++)
4287 level->initial_player_gravity[i] =
4288 (header->InitialGravity == 1 ? TRUE : FALSE);
4290 // skip leading spaces
4291 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4292 if (header->LevelTitle[i] != ' ')
4296 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4297 level->name[j] = header->LevelTitle[i];
4298 level->name[j] = '\0';
4300 // cut trailing spaces
4302 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4303 level->name[j - 1] = '\0';
4305 level->gems_needed = header->InfotronsNeeded;
4307 for (i = 0; i < header->SpecialPortCount; i++)
4309 SpecialPortType *port = &header->SpecialPort[i];
4310 int port_location = port->PortLocation;
4311 int gravity = port->Gravity;
4312 int port_x, port_y, port_element;
4314 port_x = (port_location / 2) % level->fieldx;
4315 port_y = (port_location / 2) / level->fieldx;
4317 if (port_x < 0 || port_x >= level->fieldx ||
4318 port_y < 0 || port_y >= level->fieldy)
4320 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4325 port_element = level->field[port_x][port_y];
4327 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4328 port_element > EL_SP_GRAVITY_PORT_UP)
4330 Warn("no special port at position (%d, %d)", port_x, port_y);
4335 // change previous (wrong) gravity inverting special port to either
4336 // gravity enabling special port or gravity disabling special port
4337 level->field[port_x][port_y] +=
4338 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4339 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4342 // change special gravity ports without database entries to normal ports
4343 for (x = 0; x < level->fieldx; x++)
4344 for (y = 0; y < level->fieldy; y++)
4345 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4346 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4347 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4349 level->time = 0; // no time limit
4350 level->amoeba_speed = 0;
4351 level->time_magic_wall = 0;
4352 level->time_wheel = 0;
4353 level->amoeba_content = EL_EMPTY;
4355 // original Supaplex does not use score values -- rate by playing time
4356 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4357 level->score[i] = 0;
4359 level->rate_time_over_score = TRUE;
4361 // there are no yamyams in supaplex levels
4362 for (i = 0; i < level->num_yamyam_contents; i++)
4363 for (x = 0; x < 3; x++)
4364 for (y = 0; y < 3; y++)
4365 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4368 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4370 struct LevelInfo_SP *level_sp = level->native_sp_level;
4371 struct DemoInfo_SP *demo = &level_sp->demo;
4374 // always start with reliable default values
4375 demo->is_available = FALSE;
4378 if (TAPE_IS_EMPTY(tape))
4381 demo->level_nr = tape.level_nr; // (currently not used)
4383 level_sp->header.DemoRandomSeed = tape.random_seed;
4387 for (i = 0; i < tape.length; i++)
4389 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4390 int demo_repeat = tape.pos[i].delay;
4391 int demo_entries = (demo_repeat + 15) / 16;
4393 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4395 Warn("tape truncated: size exceeds maximum SP demo size %d",
4401 for (j = 0; j < demo_repeat / 16; j++)
4402 demo->data[demo->length++] = 0xf0 | demo_action;
4404 if (demo_repeat % 16)
4405 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4408 demo->is_available = TRUE;
4411 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4413 struct LevelInfo_SP *level_sp = level->native_sp_level;
4414 struct DemoInfo_SP *demo = &level_sp->demo;
4415 char *filename = level->file_info.filename;
4418 // always start with reliable default values
4419 setTapeInfoToDefaults();
4421 if (!demo->is_available)
4424 tape.level_nr = demo->level_nr; // (currently not used)
4425 tape.random_seed = level_sp->header.DemoRandomSeed;
4427 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4430 tape.pos[tape.counter].delay = 0;
4432 for (i = 0; i < demo->length; i++)
4434 int demo_action = demo->data[i] & 0x0f;
4435 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4436 int tape_action = map_key_SP_to_RND(demo_action);
4437 int tape_repeat = demo_repeat + 1;
4438 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4439 boolean success = 0;
4442 for (j = 0; j < tape_repeat; j++)
4443 success = TapeAddAction(action);
4447 Warn("SP demo truncated: size exceeds maximum tape size %d",
4454 TapeHaltRecording();
4458 // ----------------------------------------------------------------------------
4459 // functions for loading MM level
4460 // ----------------------------------------------------------------------------
4462 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4464 struct LevelInfo_MM *level_mm = level->native_mm_level;
4467 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4468 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4470 level_mm->time = level->time;
4471 level_mm->kettles_needed = level->gems_needed;
4472 level_mm->auto_count_kettles = level->auto_count_gems;
4474 level_mm->mm_laser_red = level->mm_laser_red;
4475 level_mm->mm_laser_green = level->mm_laser_green;
4476 level_mm->mm_laser_blue = level->mm_laser_blue;
4478 level_mm->df_laser_red = level->df_laser_red;
4479 level_mm->df_laser_green = level->df_laser_green;
4480 level_mm->df_laser_blue = level->df_laser_blue;
4482 strcpy(level_mm->name, level->name);
4483 strcpy(level_mm->author, level->author);
4485 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4486 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4487 level_mm->score[SC_KEY] = level->score[SC_KEY];
4488 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4489 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4491 level_mm->amoeba_speed = level->amoeba_speed;
4492 level_mm->time_fuse = level->mm_time_fuse;
4493 level_mm->time_bomb = level->mm_time_bomb;
4494 level_mm->time_ball = level->mm_time_ball;
4495 level_mm->time_block = level->mm_time_block;
4497 level_mm->num_ball_contents = level->num_mm_ball_contents;
4498 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4499 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4500 level_mm->explode_ball = level->explode_mm_ball;
4502 for (i = 0; i < level->num_mm_ball_contents; i++)
4503 level_mm->ball_content[i] =
4504 map_element_RND_to_MM(level->mm_ball_content[i]);
4506 for (x = 0; x < level->fieldx; x++)
4507 for (y = 0; y < level->fieldy; y++)
4509 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4512 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4514 struct LevelInfo_MM *level_mm = level->native_mm_level;
4517 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4518 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4520 level->time = level_mm->time;
4521 level->gems_needed = level_mm->kettles_needed;
4522 level->auto_count_gems = level_mm->auto_count_kettles;
4524 level->mm_laser_red = level_mm->mm_laser_red;
4525 level->mm_laser_green = level_mm->mm_laser_green;
4526 level->mm_laser_blue = level_mm->mm_laser_blue;
4528 level->df_laser_red = level_mm->df_laser_red;
4529 level->df_laser_green = level_mm->df_laser_green;
4530 level->df_laser_blue = level_mm->df_laser_blue;
4532 strcpy(level->name, level_mm->name);
4534 // only overwrite author from 'levelinfo.conf' if author defined in level
4535 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4536 strcpy(level->author, level_mm->author);
4538 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4539 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4540 level->score[SC_KEY] = level_mm->score[SC_KEY];
4541 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4542 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4544 level->amoeba_speed = level_mm->amoeba_speed;
4545 level->mm_time_fuse = level_mm->time_fuse;
4546 level->mm_time_bomb = level_mm->time_bomb;
4547 level->mm_time_ball = level_mm->time_ball;
4548 level->mm_time_block = level_mm->time_block;
4550 level->num_mm_ball_contents = level_mm->num_ball_contents;
4551 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4552 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4553 level->explode_mm_ball = level_mm->explode_ball;
4555 for (i = 0; i < level->num_mm_ball_contents; i++)
4556 level->mm_ball_content[i] =
4557 map_element_MM_to_RND(level_mm->ball_content[i]);
4559 for (x = 0; x < level->fieldx; x++)
4560 for (y = 0; y < level->fieldy; y++)
4561 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4565 // ----------------------------------------------------------------------------
4566 // functions for loading DC level
4567 // ----------------------------------------------------------------------------
4569 #define DC_LEVEL_HEADER_SIZE 344
4571 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4574 static int last_data_encoded;
4578 int diff_hi, diff_lo;
4579 int data_hi, data_lo;
4580 unsigned short data_decoded;
4584 last_data_encoded = 0;
4591 diff = data_encoded - last_data_encoded;
4592 diff_hi = diff & ~0xff;
4593 diff_lo = diff & 0xff;
4597 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4598 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4599 data_hi = data_hi & 0xff00;
4601 data_decoded = data_hi | data_lo;
4603 last_data_encoded = data_encoded;
4605 offset1 = (offset1 + 1) % 31;
4606 offset2 = offset2 & 0xff;
4608 return data_decoded;
4611 static int getMappedElement_DC(int element)
4619 // 0x0117 - 0x036e: (?)
4622 // 0x042d - 0x0684: (?)
4638 element = EL_CRYSTAL;
4641 case 0x0e77: // quicksand (boulder)
4642 element = EL_QUICKSAND_FAST_FULL;
4645 case 0x0e99: // slow quicksand (boulder)
4646 element = EL_QUICKSAND_FULL;
4650 element = EL_EM_EXIT_OPEN;
4654 element = EL_EM_EXIT_CLOSED;
4658 element = EL_EM_STEEL_EXIT_OPEN;
4662 element = EL_EM_STEEL_EXIT_CLOSED;
4665 case 0x0f4f: // dynamite (lit 1)
4666 element = EL_EM_DYNAMITE_ACTIVE;
4669 case 0x0f57: // dynamite (lit 2)
4670 element = EL_EM_DYNAMITE_ACTIVE;
4673 case 0x0f5f: // dynamite (lit 3)
4674 element = EL_EM_DYNAMITE_ACTIVE;
4677 case 0x0f67: // dynamite (lit 4)
4678 element = EL_EM_DYNAMITE_ACTIVE;
4685 element = EL_AMOEBA_WET;
4689 element = EL_AMOEBA_DROP;
4693 element = EL_DC_MAGIC_WALL;
4697 element = EL_SPACESHIP_UP;
4701 element = EL_SPACESHIP_DOWN;
4705 element = EL_SPACESHIP_LEFT;
4709 element = EL_SPACESHIP_RIGHT;
4713 element = EL_BUG_UP;
4717 element = EL_BUG_DOWN;
4721 element = EL_BUG_LEFT;
4725 element = EL_BUG_RIGHT;
4729 element = EL_MOLE_UP;
4733 element = EL_MOLE_DOWN;
4737 element = EL_MOLE_LEFT;
4741 element = EL_MOLE_RIGHT;
4749 element = EL_YAMYAM_UP;
4753 element = EL_SWITCHGATE_OPEN;
4757 element = EL_SWITCHGATE_CLOSED;
4761 element = EL_DC_SWITCHGATE_SWITCH_UP;
4765 element = EL_TIMEGATE_CLOSED;
4768 case 0x144c: // conveyor belt switch (green)
4769 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4772 case 0x144f: // conveyor belt switch (red)
4773 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4776 case 0x1452: // conveyor belt switch (blue)
4777 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4781 element = EL_CONVEYOR_BELT_3_MIDDLE;
4785 element = EL_CONVEYOR_BELT_3_LEFT;
4789 element = EL_CONVEYOR_BELT_3_RIGHT;
4793 element = EL_CONVEYOR_BELT_1_MIDDLE;
4797 element = EL_CONVEYOR_BELT_1_LEFT;
4801 element = EL_CONVEYOR_BELT_1_RIGHT;
4805 element = EL_CONVEYOR_BELT_4_MIDDLE;
4809 element = EL_CONVEYOR_BELT_4_LEFT;
4813 element = EL_CONVEYOR_BELT_4_RIGHT;
4817 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4821 element = EL_EXPANDABLE_WALL_VERTICAL;
4825 element = EL_EXPANDABLE_WALL_ANY;
4828 case 0x14ce: // growing steel wall (left/right)
4829 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4832 case 0x14df: // growing steel wall (up/down)
4833 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4836 case 0x14e8: // growing steel wall (up/down/left/right)
4837 element = EL_EXPANDABLE_STEELWALL_ANY;
4841 element = EL_SHIELD_DEADLY;
4845 element = EL_EXTRA_TIME;
4853 element = EL_EMPTY_SPACE;
4856 case 0x1578: // quicksand (empty)
4857 element = EL_QUICKSAND_FAST_EMPTY;
4860 case 0x1579: // slow quicksand (empty)
4861 element = EL_QUICKSAND_EMPTY;
4871 element = EL_EM_DYNAMITE;
4874 case 0x15a1: // key (red)
4875 element = EL_EM_KEY_1;
4878 case 0x15a2: // key (yellow)
4879 element = EL_EM_KEY_2;
4882 case 0x15a3: // key (blue)
4883 element = EL_EM_KEY_4;
4886 case 0x15a4: // key (green)
4887 element = EL_EM_KEY_3;
4890 case 0x15a5: // key (white)
4891 element = EL_DC_KEY_WHITE;
4895 element = EL_WALL_SLIPPERY;
4902 case 0x15a8: // wall (not round)
4906 case 0x15a9: // (blue)
4907 element = EL_CHAR_A;
4910 case 0x15aa: // (blue)
4911 element = EL_CHAR_B;
4914 case 0x15ab: // (blue)
4915 element = EL_CHAR_C;
4918 case 0x15ac: // (blue)
4919 element = EL_CHAR_D;
4922 case 0x15ad: // (blue)
4923 element = EL_CHAR_E;
4926 case 0x15ae: // (blue)
4927 element = EL_CHAR_F;
4930 case 0x15af: // (blue)
4931 element = EL_CHAR_G;
4934 case 0x15b0: // (blue)
4935 element = EL_CHAR_H;
4938 case 0x15b1: // (blue)
4939 element = EL_CHAR_I;
4942 case 0x15b2: // (blue)
4943 element = EL_CHAR_J;
4946 case 0x15b3: // (blue)
4947 element = EL_CHAR_K;
4950 case 0x15b4: // (blue)
4951 element = EL_CHAR_L;
4954 case 0x15b5: // (blue)
4955 element = EL_CHAR_M;
4958 case 0x15b6: // (blue)
4959 element = EL_CHAR_N;
4962 case 0x15b7: // (blue)
4963 element = EL_CHAR_O;
4966 case 0x15b8: // (blue)
4967 element = EL_CHAR_P;
4970 case 0x15b9: // (blue)
4971 element = EL_CHAR_Q;
4974 case 0x15ba: // (blue)
4975 element = EL_CHAR_R;
4978 case 0x15bb: // (blue)
4979 element = EL_CHAR_S;
4982 case 0x15bc: // (blue)
4983 element = EL_CHAR_T;
4986 case 0x15bd: // (blue)
4987 element = EL_CHAR_U;
4990 case 0x15be: // (blue)
4991 element = EL_CHAR_V;
4994 case 0x15bf: // (blue)
4995 element = EL_CHAR_W;
4998 case 0x15c0: // (blue)
4999 element = EL_CHAR_X;
5002 case 0x15c1: // (blue)
5003 element = EL_CHAR_Y;
5006 case 0x15c2: // (blue)
5007 element = EL_CHAR_Z;
5010 case 0x15c3: // (blue)
5011 element = EL_CHAR_AUMLAUT;
5014 case 0x15c4: // (blue)
5015 element = EL_CHAR_OUMLAUT;
5018 case 0x15c5: // (blue)
5019 element = EL_CHAR_UUMLAUT;
5022 case 0x15c6: // (blue)
5023 element = EL_CHAR_0;
5026 case 0x15c7: // (blue)
5027 element = EL_CHAR_1;
5030 case 0x15c8: // (blue)
5031 element = EL_CHAR_2;
5034 case 0x15c9: // (blue)
5035 element = EL_CHAR_3;
5038 case 0x15ca: // (blue)
5039 element = EL_CHAR_4;
5042 case 0x15cb: // (blue)
5043 element = EL_CHAR_5;
5046 case 0x15cc: // (blue)
5047 element = EL_CHAR_6;
5050 case 0x15cd: // (blue)
5051 element = EL_CHAR_7;
5054 case 0x15ce: // (blue)
5055 element = EL_CHAR_8;
5058 case 0x15cf: // (blue)
5059 element = EL_CHAR_9;
5062 case 0x15d0: // (blue)
5063 element = EL_CHAR_PERIOD;
5066 case 0x15d1: // (blue)
5067 element = EL_CHAR_EXCLAM;
5070 case 0x15d2: // (blue)
5071 element = EL_CHAR_COLON;
5074 case 0x15d3: // (blue)
5075 element = EL_CHAR_LESS;
5078 case 0x15d4: // (blue)
5079 element = EL_CHAR_GREATER;
5082 case 0x15d5: // (blue)
5083 element = EL_CHAR_QUESTION;
5086 case 0x15d6: // (blue)
5087 element = EL_CHAR_COPYRIGHT;
5090 case 0x15d7: // (blue)
5091 element = EL_CHAR_UP;
5094 case 0x15d8: // (blue)
5095 element = EL_CHAR_DOWN;
5098 case 0x15d9: // (blue)
5099 element = EL_CHAR_BUTTON;
5102 case 0x15da: // (blue)
5103 element = EL_CHAR_PLUS;
5106 case 0x15db: // (blue)
5107 element = EL_CHAR_MINUS;
5110 case 0x15dc: // (blue)
5111 element = EL_CHAR_APOSTROPHE;
5114 case 0x15dd: // (blue)
5115 element = EL_CHAR_PARENLEFT;
5118 case 0x15de: // (blue)
5119 element = EL_CHAR_PARENRIGHT;
5122 case 0x15df: // (green)
5123 element = EL_CHAR_A;
5126 case 0x15e0: // (green)
5127 element = EL_CHAR_B;
5130 case 0x15e1: // (green)
5131 element = EL_CHAR_C;
5134 case 0x15e2: // (green)
5135 element = EL_CHAR_D;
5138 case 0x15e3: // (green)
5139 element = EL_CHAR_E;
5142 case 0x15e4: // (green)
5143 element = EL_CHAR_F;
5146 case 0x15e5: // (green)
5147 element = EL_CHAR_G;
5150 case 0x15e6: // (green)
5151 element = EL_CHAR_H;
5154 case 0x15e7: // (green)
5155 element = EL_CHAR_I;
5158 case 0x15e8: // (green)
5159 element = EL_CHAR_J;
5162 case 0x15e9: // (green)
5163 element = EL_CHAR_K;
5166 case 0x15ea: // (green)
5167 element = EL_CHAR_L;
5170 case 0x15eb: // (green)
5171 element = EL_CHAR_M;
5174 case 0x15ec: // (green)
5175 element = EL_CHAR_N;
5178 case 0x15ed: // (green)
5179 element = EL_CHAR_O;
5182 case 0x15ee: // (green)
5183 element = EL_CHAR_P;
5186 case 0x15ef: // (green)
5187 element = EL_CHAR_Q;
5190 case 0x15f0: // (green)
5191 element = EL_CHAR_R;
5194 case 0x15f1: // (green)
5195 element = EL_CHAR_S;
5198 case 0x15f2: // (green)
5199 element = EL_CHAR_T;
5202 case 0x15f3: // (green)
5203 element = EL_CHAR_U;
5206 case 0x15f4: // (green)
5207 element = EL_CHAR_V;
5210 case 0x15f5: // (green)
5211 element = EL_CHAR_W;
5214 case 0x15f6: // (green)
5215 element = EL_CHAR_X;
5218 case 0x15f7: // (green)
5219 element = EL_CHAR_Y;
5222 case 0x15f8: // (green)
5223 element = EL_CHAR_Z;
5226 case 0x15f9: // (green)
5227 element = EL_CHAR_AUMLAUT;
5230 case 0x15fa: // (green)
5231 element = EL_CHAR_OUMLAUT;
5234 case 0x15fb: // (green)
5235 element = EL_CHAR_UUMLAUT;
5238 case 0x15fc: // (green)
5239 element = EL_CHAR_0;
5242 case 0x15fd: // (green)
5243 element = EL_CHAR_1;
5246 case 0x15fe: // (green)
5247 element = EL_CHAR_2;
5250 case 0x15ff: // (green)
5251 element = EL_CHAR_3;
5254 case 0x1600: // (green)
5255 element = EL_CHAR_4;
5258 case 0x1601: // (green)
5259 element = EL_CHAR_5;
5262 case 0x1602: // (green)
5263 element = EL_CHAR_6;
5266 case 0x1603: // (green)
5267 element = EL_CHAR_7;
5270 case 0x1604: // (green)
5271 element = EL_CHAR_8;
5274 case 0x1605: // (green)
5275 element = EL_CHAR_9;
5278 case 0x1606: // (green)
5279 element = EL_CHAR_PERIOD;
5282 case 0x1607: // (green)
5283 element = EL_CHAR_EXCLAM;
5286 case 0x1608: // (green)
5287 element = EL_CHAR_COLON;
5290 case 0x1609: // (green)
5291 element = EL_CHAR_LESS;
5294 case 0x160a: // (green)
5295 element = EL_CHAR_GREATER;
5298 case 0x160b: // (green)
5299 element = EL_CHAR_QUESTION;
5302 case 0x160c: // (green)
5303 element = EL_CHAR_COPYRIGHT;
5306 case 0x160d: // (green)
5307 element = EL_CHAR_UP;
5310 case 0x160e: // (green)
5311 element = EL_CHAR_DOWN;
5314 case 0x160f: // (green)
5315 element = EL_CHAR_BUTTON;
5318 case 0x1610: // (green)
5319 element = EL_CHAR_PLUS;
5322 case 0x1611: // (green)
5323 element = EL_CHAR_MINUS;
5326 case 0x1612: // (green)
5327 element = EL_CHAR_APOSTROPHE;
5330 case 0x1613: // (green)
5331 element = EL_CHAR_PARENLEFT;
5334 case 0x1614: // (green)
5335 element = EL_CHAR_PARENRIGHT;
5338 case 0x1615: // (blue steel)
5339 element = EL_STEEL_CHAR_A;
5342 case 0x1616: // (blue steel)
5343 element = EL_STEEL_CHAR_B;
5346 case 0x1617: // (blue steel)
5347 element = EL_STEEL_CHAR_C;
5350 case 0x1618: // (blue steel)
5351 element = EL_STEEL_CHAR_D;
5354 case 0x1619: // (blue steel)
5355 element = EL_STEEL_CHAR_E;
5358 case 0x161a: // (blue steel)
5359 element = EL_STEEL_CHAR_F;
5362 case 0x161b: // (blue steel)
5363 element = EL_STEEL_CHAR_G;
5366 case 0x161c: // (blue steel)
5367 element = EL_STEEL_CHAR_H;
5370 case 0x161d: // (blue steel)
5371 element = EL_STEEL_CHAR_I;
5374 case 0x161e: // (blue steel)
5375 element = EL_STEEL_CHAR_J;
5378 case 0x161f: // (blue steel)
5379 element = EL_STEEL_CHAR_K;
5382 case 0x1620: // (blue steel)
5383 element = EL_STEEL_CHAR_L;
5386 case 0x1621: // (blue steel)
5387 element = EL_STEEL_CHAR_M;
5390 case 0x1622: // (blue steel)
5391 element = EL_STEEL_CHAR_N;
5394 case 0x1623: // (blue steel)
5395 element = EL_STEEL_CHAR_O;
5398 case 0x1624: // (blue steel)
5399 element = EL_STEEL_CHAR_P;
5402 case 0x1625: // (blue steel)
5403 element = EL_STEEL_CHAR_Q;
5406 case 0x1626: // (blue steel)
5407 element = EL_STEEL_CHAR_R;
5410 case 0x1627: // (blue steel)
5411 element = EL_STEEL_CHAR_S;
5414 case 0x1628: // (blue steel)
5415 element = EL_STEEL_CHAR_T;
5418 case 0x1629: // (blue steel)
5419 element = EL_STEEL_CHAR_U;
5422 case 0x162a: // (blue steel)
5423 element = EL_STEEL_CHAR_V;
5426 case 0x162b: // (blue steel)
5427 element = EL_STEEL_CHAR_W;
5430 case 0x162c: // (blue steel)
5431 element = EL_STEEL_CHAR_X;
5434 case 0x162d: // (blue steel)
5435 element = EL_STEEL_CHAR_Y;
5438 case 0x162e: // (blue steel)
5439 element = EL_STEEL_CHAR_Z;
5442 case 0x162f: // (blue steel)
5443 element = EL_STEEL_CHAR_AUMLAUT;
5446 case 0x1630: // (blue steel)
5447 element = EL_STEEL_CHAR_OUMLAUT;
5450 case 0x1631: // (blue steel)
5451 element = EL_STEEL_CHAR_UUMLAUT;
5454 case 0x1632: // (blue steel)
5455 element = EL_STEEL_CHAR_0;
5458 case 0x1633: // (blue steel)
5459 element = EL_STEEL_CHAR_1;
5462 case 0x1634: // (blue steel)
5463 element = EL_STEEL_CHAR_2;
5466 case 0x1635: // (blue steel)
5467 element = EL_STEEL_CHAR_3;
5470 case 0x1636: // (blue steel)
5471 element = EL_STEEL_CHAR_4;
5474 case 0x1637: // (blue steel)
5475 element = EL_STEEL_CHAR_5;
5478 case 0x1638: // (blue steel)
5479 element = EL_STEEL_CHAR_6;
5482 case 0x1639: // (blue steel)
5483 element = EL_STEEL_CHAR_7;
5486 case 0x163a: // (blue steel)
5487 element = EL_STEEL_CHAR_8;
5490 case 0x163b: // (blue steel)
5491 element = EL_STEEL_CHAR_9;
5494 case 0x163c: // (blue steel)
5495 element = EL_STEEL_CHAR_PERIOD;
5498 case 0x163d: // (blue steel)
5499 element = EL_STEEL_CHAR_EXCLAM;
5502 case 0x163e: // (blue steel)
5503 element = EL_STEEL_CHAR_COLON;
5506 case 0x163f: // (blue steel)
5507 element = EL_STEEL_CHAR_LESS;
5510 case 0x1640: // (blue steel)
5511 element = EL_STEEL_CHAR_GREATER;
5514 case 0x1641: // (blue steel)
5515 element = EL_STEEL_CHAR_QUESTION;
5518 case 0x1642: // (blue steel)
5519 element = EL_STEEL_CHAR_COPYRIGHT;
5522 case 0x1643: // (blue steel)
5523 element = EL_STEEL_CHAR_UP;
5526 case 0x1644: // (blue steel)
5527 element = EL_STEEL_CHAR_DOWN;
5530 case 0x1645: // (blue steel)
5531 element = EL_STEEL_CHAR_BUTTON;
5534 case 0x1646: // (blue steel)
5535 element = EL_STEEL_CHAR_PLUS;
5538 case 0x1647: // (blue steel)
5539 element = EL_STEEL_CHAR_MINUS;
5542 case 0x1648: // (blue steel)
5543 element = EL_STEEL_CHAR_APOSTROPHE;
5546 case 0x1649: // (blue steel)
5547 element = EL_STEEL_CHAR_PARENLEFT;
5550 case 0x164a: // (blue steel)
5551 element = EL_STEEL_CHAR_PARENRIGHT;
5554 case 0x164b: // (green steel)
5555 element = EL_STEEL_CHAR_A;
5558 case 0x164c: // (green steel)
5559 element = EL_STEEL_CHAR_B;
5562 case 0x164d: // (green steel)
5563 element = EL_STEEL_CHAR_C;
5566 case 0x164e: // (green steel)
5567 element = EL_STEEL_CHAR_D;
5570 case 0x164f: // (green steel)
5571 element = EL_STEEL_CHAR_E;
5574 case 0x1650: // (green steel)
5575 element = EL_STEEL_CHAR_F;
5578 case 0x1651: // (green steel)
5579 element = EL_STEEL_CHAR_G;
5582 case 0x1652: // (green steel)
5583 element = EL_STEEL_CHAR_H;
5586 case 0x1653: // (green steel)
5587 element = EL_STEEL_CHAR_I;
5590 case 0x1654: // (green steel)
5591 element = EL_STEEL_CHAR_J;
5594 case 0x1655: // (green steel)
5595 element = EL_STEEL_CHAR_K;
5598 case 0x1656: // (green steel)
5599 element = EL_STEEL_CHAR_L;
5602 case 0x1657: // (green steel)
5603 element = EL_STEEL_CHAR_M;
5606 case 0x1658: // (green steel)
5607 element = EL_STEEL_CHAR_N;
5610 case 0x1659: // (green steel)
5611 element = EL_STEEL_CHAR_O;
5614 case 0x165a: // (green steel)
5615 element = EL_STEEL_CHAR_P;
5618 case 0x165b: // (green steel)
5619 element = EL_STEEL_CHAR_Q;
5622 case 0x165c: // (green steel)
5623 element = EL_STEEL_CHAR_R;
5626 case 0x165d: // (green steel)
5627 element = EL_STEEL_CHAR_S;
5630 case 0x165e: // (green steel)
5631 element = EL_STEEL_CHAR_T;
5634 case 0x165f: // (green steel)
5635 element = EL_STEEL_CHAR_U;
5638 case 0x1660: // (green steel)
5639 element = EL_STEEL_CHAR_V;
5642 case 0x1661: // (green steel)
5643 element = EL_STEEL_CHAR_W;
5646 case 0x1662: // (green steel)
5647 element = EL_STEEL_CHAR_X;
5650 case 0x1663: // (green steel)
5651 element = EL_STEEL_CHAR_Y;
5654 case 0x1664: // (green steel)
5655 element = EL_STEEL_CHAR_Z;
5658 case 0x1665: // (green steel)
5659 element = EL_STEEL_CHAR_AUMLAUT;
5662 case 0x1666: // (green steel)
5663 element = EL_STEEL_CHAR_OUMLAUT;
5666 case 0x1667: // (green steel)
5667 element = EL_STEEL_CHAR_UUMLAUT;
5670 case 0x1668: // (green steel)
5671 element = EL_STEEL_CHAR_0;
5674 case 0x1669: // (green steel)
5675 element = EL_STEEL_CHAR_1;
5678 case 0x166a: // (green steel)
5679 element = EL_STEEL_CHAR_2;
5682 case 0x166b: // (green steel)
5683 element = EL_STEEL_CHAR_3;
5686 case 0x166c: // (green steel)
5687 element = EL_STEEL_CHAR_4;
5690 case 0x166d: // (green steel)
5691 element = EL_STEEL_CHAR_5;
5694 case 0x166e: // (green steel)
5695 element = EL_STEEL_CHAR_6;
5698 case 0x166f: // (green steel)
5699 element = EL_STEEL_CHAR_7;
5702 case 0x1670: // (green steel)
5703 element = EL_STEEL_CHAR_8;
5706 case 0x1671: // (green steel)
5707 element = EL_STEEL_CHAR_9;
5710 case 0x1672: // (green steel)
5711 element = EL_STEEL_CHAR_PERIOD;
5714 case 0x1673: // (green steel)
5715 element = EL_STEEL_CHAR_EXCLAM;
5718 case 0x1674: // (green steel)
5719 element = EL_STEEL_CHAR_COLON;
5722 case 0x1675: // (green steel)
5723 element = EL_STEEL_CHAR_LESS;
5726 case 0x1676: // (green steel)
5727 element = EL_STEEL_CHAR_GREATER;
5730 case 0x1677: // (green steel)
5731 element = EL_STEEL_CHAR_QUESTION;
5734 case 0x1678: // (green steel)
5735 element = EL_STEEL_CHAR_COPYRIGHT;
5738 case 0x1679: // (green steel)
5739 element = EL_STEEL_CHAR_UP;
5742 case 0x167a: // (green steel)
5743 element = EL_STEEL_CHAR_DOWN;
5746 case 0x167b: // (green steel)
5747 element = EL_STEEL_CHAR_BUTTON;
5750 case 0x167c: // (green steel)
5751 element = EL_STEEL_CHAR_PLUS;
5754 case 0x167d: // (green steel)
5755 element = EL_STEEL_CHAR_MINUS;
5758 case 0x167e: // (green steel)
5759 element = EL_STEEL_CHAR_APOSTROPHE;
5762 case 0x167f: // (green steel)
5763 element = EL_STEEL_CHAR_PARENLEFT;
5766 case 0x1680: // (green steel)
5767 element = EL_STEEL_CHAR_PARENRIGHT;
5770 case 0x1681: // gate (red)
5771 element = EL_EM_GATE_1;
5774 case 0x1682: // secret gate (red)
5775 element = EL_EM_GATE_1_GRAY;
5778 case 0x1683: // gate (yellow)
5779 element = EL_EM_GATE_2;
5782 case 0x1684: // secret gate (yellow)
5783 element = EL_EM_GATE_2_GRAY;
5786 case 0x1685: // gate (blue)
5787 element = EL_EM_GATE_4;
5790 case 0x1686: // secret gate (blue)
5791 element = EL_EM_GATE_4_GRAY;
5794 case 0x1687: // gate (green)
5795 element = EL_EM_GATE_3;
5798 case 0x1688: // secret gate (green)
5799 element = EL_EM_GATE_3_GRAY;
5802 case 0x1689: // gate (white)
5803 element = EL_DC_GATE_WHITE;
5806 case 0x168a: // secret gate (white)
5807 element = EL_DC_GATE_WHITE_GRAY;
5810 case 0x168b: // secret gate (no key)
5811 element = EL_DC_GATE_FAKE_GRAY;
5815 element = EL_ROBOT_WHEEL;
5819 element = EL_DC_TIMEGATE_SWITCH;
5823 element = EL_ACID_POOL_BOTTOM;
5827 element = EL_ACID_POOL_TOPLEFT;
5831 element = EL_ACID_POOL_TOPRIGHT;
5835 element = EL_ACID_POOL_BOTTOMLEFT;
5839 element = EL_ACID_POOL_BOTTOMRIGHT;
5843 element = EL_STEELWALL;
5847 element = EL_STEELWALL_SLIPPERY;
5850 case 0x1695: // steel wall (not round)
5851 element = EL_STEELWALL;
5854 case 0x1696: // steel wall (left)
5855 element = EL_DC_STEELWALL_1_LEFT;
5858 case 0x1697: // steel wall (bottom)
5859 element = EL_DC_STEELWALL_1_BOTTOM;
5862 case 0x1698: // steel wall (right)
5863 element = EL_DC_STEELWALL_1_RIGHT;
5866 case 0x1699: // steel wall (top)
5867 element = EL_DC_STEELWALL_1_TOP;
5870 case 0x169a: // steel wall (left/bottom)
5871 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5874 case 0x169b: // steel wall (right/bottom)
5875 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5878 case 0x169c: // steel wall (right/top)
5879 element = EL_DC_STEELWALL_1_TOPRIGHT;
5882 case 0x169d: // steel wall (left/top)
5883 element = EL_DC_STEELWALL_1_TOPLEFT;
5886 case 0x169e: // steel wall (right/bottom small)
5887 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5890 case 0x169f: // steel wall (left/bottom small)
5891 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5894 case 0x16a0: // steel wall (right/top small)
5895 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5898 case 0x16a1: // steel wall (left/top small)
5899 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5902 case 0x16a2: // steel wall (left/right)
5903 element = EL_DC_STEELWALL_1_VERTICAL;
5906 case 0x16a3: // steel wall (top/bottom)
5907 element = EL_DC_STEELWALL_1_HORIZONTAL;
5910 case 0x16a4: // steel wall 2 (left end)
5911 element = EL_DC_STEELWALL_2_LEFT;
5914 case 0x16a5: // steel wall 2 (right end)
5915 element = EL_DC_STEELWALL_2_RIGHT;
5918 case 0x16a6: // steel wall 2 (top end)
5919 element = EL_DC_STEELWALL_2_TOP;
5922 case 0x16a7: // steel wall 2 (bottom end)
5923 element = EL_DC_STEELWALL_2_BOTTOM;
5926 case 0x16a8: // steel wall 2 (left/right)
5927 element = EL_DC_STEELWALL_2_HORIZONTAL;
5930 case 0x16a9: // steel wall 2 (up/down)
5931 element = EL_DC_STEELWALL_2_VERTICAL;
5934 case 0x16aa: // steel wall 2 (mid)
5935 element = EL_DC_STEELWALL_2_MIDDLE;
5939 element = EL_SIGN_EXCLAMATION;
5943 element = EL_SIGN_RADIOACTIVITY;
5947 element = EL_SIGN_STOP;
5951 element = EL_SIGN_WHEELCHAIR;
5955 element = EL_SIGN_PARKING;
5959 element = EL_SIGN_NO_ENTRY;
5963 element = EL_SIGN_HEART;
5967 element = EL_SIGN_GIVE_WAY;
5971 element = EL_SIGN_ENTRY_FORBIDDEN;
5975 element = EL_SIGN_EMERGENCY_EXIT;
5979 element = EL_SIGN_YIN_YANG;
5983 element = EL_WALL_EMERALD;
5987 element = EL_WALL_DIAMOND;
5991 element = EL_WALL_PEARL;
5995 element = EL_WALL_CRYSTAL;
5999 element = EL_INVISIBLE_WALL;
6003 element = EL_INVISIBLE_STEELWALL;
6007 // EL_INVISIBLE_SAND
6010 element = EL_LIGHT_SWITCH;
6014 element = EL_ENVELOPE_1;
6018 if (element >= 0x0117 && element <= 0x036e) // (?)
6019 element = EL_DIAMOND;
6020 else if (element >= 0x042d && element <= 0x0684) // (?)
6021 element = EL_EMERALD;
6022 else if (element >= 0x157c && element <= 0x158b)
6024 else if (element >= 0x1590 && element <= 0x159f)
6025 element = EL_DC_LANDMINE;
6026 else if (element >= 0x16bc && element <= 0x16cb)
6027 element = EL_INVISIBLE_SAND;
6030 Warn("unknown Diamond Caves element 0x%04x", element);
6032 element = EL_UNKNOWN;
6037 return getMappedElement(element);
6040 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6042 byte header[DC_LEVEL_HEADER_SIZE];
6044 int envelope_header_pos = 62;
6045 int envelope_content_pos = 94;
6046 int level_name_pos = 251;
6047 int level_author_pos = 292;
6048 int envelope_header_len;
6049 int envelope_content_len;
6051 int level_author_len;
6053 int num_yamyam_contents;
6056 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6058 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6060 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6062 header[i * 2 + 0] = header_word >> 8;
6063 header[i * 2 + 1] = header_word & 0xff;
6066 // read some values from level header to check level decoding integrity
6067 fieldx = header[6] | (header[7] << 8);
6068 fieldy = header[8] | (header[9] << 8);
6069 num_yamyam_contents = header[60] | (header[61] << 8);
6071 // do some simple sanity checks to ensure that level was correctly decoded
6072 if (fieldx < 1 || fieldx > 256 ||
6073 fieldy < 1 || fieldy > 256 ||
6074 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6076 level->no_valid_file = TRUE;
6078 Warn("cannot decode level from stream -- using empty level");
6083 // maximum envelope header size is 31 bytes
6084 envelope_header_len = header[envelope_header_pos];
6085 // maximum envelope content size is 110 (156?) bytes
6086 envelope_content_len = header[envelope_content_pos];
6088 // maximum level title size is 40 bytes
6089 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6090 // maximum level author size is 30 (51?) bytes
6091 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6095 for (i = 0; i < envelope_header_len; i++)
6096 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6097 level->envelope[0].text[envelope_size++] =
6098 header[envelope_header_pos + 1 + i];
6100 if (envelope_header_len > 0 && envelope_content_len > 0)
6102 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6103 level->envelope[0].text[envelope_size++] = '\n';
6104 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6105 level->envelope[0].text[envelope_size++] = '\n';
6108 for (i = 0; i < envelope_content_len; i++)
6109 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6110 level->envelope[0].text[envelope_size++] =
6111 header[envelope_content_pos + 1 + i];
6113 level->envelope[0].text[envelope_size] = '\0';
6115 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6116 level->envelope[0].ysize = 10;
6117 level->envelope[0].autowrap = TRUE;
6118 level->envelope[0].centered = TRUE;
6120 for (i = 0; i < level_name_len; i++)
6121 level->name[i] = header[level_name_pos + 1 + i];
6122 level->name[level_name_len] = '\0';
6124 for (i = 0; i < level_author_len; i++)
6125 level->author[i] = header[level_author_pos + 1 + i];
6126 level->author[level_author_len] = '\0';
6128 num_yamyam_contents = header[60] | (header[61] << 8);
6129 level->num_yamyam_contents =
6130 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6132 for (i = 0; i < num_yamyam_contents; i++)
6134 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6136 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6137 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6139 if (i < MAX_ELEMENT_CONTENTS)
6140 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6144 fieldx = header[6] | (header[7] << 8);
6145 fieldy = header[8] | (header[9] << 8);
6146 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6147 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6149 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6151 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6152 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6154 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6155 level->field[x][y] = getMappedElement_DC(element_dc);
6158 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6159 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6160 level->field[x][y] = EL_PLAYER_1;
6162 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6163 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6164 level->field[x][y] = EL_PLAYER_2;
6166 level->gems_needed = header[18] | (header[19] << 8);
6168 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6169 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6170 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6171 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6172 level->score[SC_NUT] = header[28] | (header[29] << 8);
6173 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6174 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6175 level->score[SC_BUG] = header[34] | (header[35] << 8);
6176 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6177 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6178 level->score[SC_KEY] = header[40] | (header[41] << 8);
6179 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6181 level->time = header[44] | (header[45] << 8);
6183 level->amoeba_speed = header[46] | (header[47] << 8);
6184 level->time_light = header[48] | (header[49] << 8);
6185 level->time_timegate = header[50] | (header[51] << 8);
6186 level->time_wheel = header[52] | (header[53] << 8);
6187 level->time_magic_wall = header[54] | (header[55] << 8);
6188 level->extra_time = header[56] | (header[57] << 8);
6189 level->shield_normal_time = header[58] | (header[59] << 8);
6191 // shield and extra time elements do not have a score
6192 level->score[SC_SHIELD] = 0;
6193 level->extra_time_score = 0;
6195 // set time for normal and deadly shields to the same value
6196 level->shield_deadly_time = level->shield_normal_time;
6198 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6199 // can slip down from flat walls, like normal walls and steel walls
6200 level->em_slippery_gems = TRUE;
6202 // time score is counted for each 10 seconds left in Diamond Caves levels
6203 level->time_score_base = 10;
6206 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6207 struct LevelFileInfo *level_file_info,
6208 boolean level_info_only)
6210 char *filename = level_file_info->filename;
6212 int num_magic_bytes = 8;
6213 char magic_bytes[num_magic_bytes + 1];
6214 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6216 if (!(file = openFile(filename, MODE_READ)))
6218 level->no_valid_file = TRUE;
6220 if (!level_info_only)
6221 Warn("cannot read level '%s' -- using empty level", filename);
6226 // fseek(file, 0x0000, SEEK_SET);
6228 if (level_file_info->packed)
6230 // read "magic bytes" from start of file
6231 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6232 magic_bytes[0] = '\0';
6234 // check "magic bytes" for correct file format
6235 if (!strPrefix(magic_bytes, "DC2"))
6237 level->no_valid_file = TRUE;
6239 Warn("unknown DC level file '%s' -- using empty level", filename);
6244 if (strPrefix(magic_bytes, "DC2Win95") ||
6245 strPrefix(magic_bytes, "DC2Win98"))
6247 int position_first_level = 0x00fa;
6248 int extra_bytes = 4;
6251 // advance file stream to first level inside the level package
6252 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6254 // each block of level data is followed by block of non-level data
6255 num_levels_to_skip *= 2;
6257 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6258 while (num_levels_to_skip >= 0)
6260 // advance file stream to next level inside the level package
6261 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6263 level->no_valid_file = TRUE;
6265 Warn("cannot fseek in file '%s' -- using empty level", filename);
6270 // skip apparently unused extra bytes following each level
6271 ReadUnusedBytesFromFile(file, extra_bytes);
6273 // read size of next level in level package
6274 skip_bytes = getFile32BitLE(file);
6276 num_levels_to_skip--;
6281 level->no_valid_file = TRUE;
6283 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6289 LoadLevelFromFileStream_DC(file, level);
6295 // ----------------------------------------------------------------------------
6296 // functions for loading SB level
6297 // ----------------------------------------------------------------------------
6299 int getMappedElement_SB(int element_ascii, boolean use_ces)
6307 sb_element_mapping[] =
6309 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6310 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6311 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6312 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6313 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6314 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6315 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6316 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6323 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6324 if (element_ascii == sb_element_mapping[i].ascii)
6325 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6327 return EL_UNDEFINED;
6330 static void SetLevelSettings_SB(struct LevelInfo *level)
6334 level->use_step_counter = TRUE;
6337 level->score[SC_TIME_BONUS] = 0;
6338 level->time_score_base = 1;
6339 level->rate_time_over_score = TRUE;
6342 level->auto_exit_sokoban = TRUE;
6345 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6346 struct LevelFileInfo *level_file_info,
6347 boolean level_info_only)
6349 char *filename = level_file_info->filename;
6350 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6351 char last_comment[MAX_LINE_LEN];
6352 char level_name[MAX_LINE_LEN];
6355 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6356 boolean read_continued_line = FALSE;
6357 boolean reading_playfield = FALSE;
6358 boolean got_valid_playfield_line = FALSE;
6359 boolean invalid_playfield_char = FALSE;
6360 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6361 int file_level_nr = 0;
6362 int x = 0, y = 0; // initialized to make compilers happy
6364 last_comment[0] = '\0';
6365 level_name[0] = '\0';
6367 if (!(file = openFile(filename, MODE_READ)))
6369 level->no_valid_file = TRUE;
6371 if (!level_info_only)
6372 Warn("cannot read level '%s' -- using empty level", filename);
6377 while (!checkEndOfFile(file))
6379 // level successfully read, but next level may follow here
6380 if (!got_valid_playfield_line && reading_playfield)
6382 // read playfield from single level file -- skip remaining file
6383 if (!level_file_info->packed)
6386 if (file_level_nr >= num_levels_to_skip)
6391 last_comment[0] = '\0';
6392 level_name[0] = '\0';
6394 reading_playfield = FALSE;
6397 got_valid_playfield_line = FALSE;
6399 // read next line of input file
6400 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6403 // cut trailing line break (this can be newline and/or carriage return)
6404 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6405 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6408 // copy raw input line for later use (mainly debugging output)
6409 strcpy(line_raw, line);
6411 if (read_continued_line)
6413 // append new line to existing line, if there is enough space
6414 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6415 strcat(previous_line, line_ptr);
6417 strcpy(line, previous_line); // copy storage buffer to line
6419 read_continued_line = FALSE;
6422 // if the last character is '\', continue at next line
6423 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6425 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6426 strcpy(previous_line, line); // copy line to storage buffer
6428 read_continued_line = TRUE;
6434 if (line[0] == '\0')
6437 // extract comment text from comment line
6440 for (line_ptr = line; *line_ptr; line_ptr++)
6441 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6444 strcpy(last_comment, line_ptr);
6449 // extract level title text from line containing level title
6450 if (line[0] == '\'')
6452 strcpy(level_name, &line[1]);
6454 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6455 level_name[strlen(level_name) - 1] = '\0';
6460 // skip lines containing only spaces (or empty lines)
6461 for (line_ptr = line; *line_ptr; line_ptr++)
6462 if (*line_ptr != ' ')
6464 if (*line_ptr == '\0')
6467 // at this point, we have found a line containing part of a playfield
6469 got_valid_playfield_line = TRUE;
6471 if (!reading_playfield)
6473 reading_playfield = TRUE;
6474 invalid_playfield_char = FALSE;
6476 for (x = 0; x < MAX_LEV_FIELDX; x++)
6477 for (y = 0; y < MAX_LEV_FIELDY; y++)
6478 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6483 // start with topmost tile row
6487 // skip playfield line if larger row than allowed
6488 if (y >= MAX_LEV_FIELDY)
6491 // start with leftmost tile column
6494 // read playfield elements from line
6495 for (line_ptr = line; *line_ptr; line_ptr++)
6497 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6499 // stop parsing playfield line if larger column than allowed
6500 if (x >= MAX_LEV_FIELDX)
6503 if (mapped_sb_element == EL_UNDEFINED)
6505 invalid_playfield_char = TRUE;
6510 level->field[x][y] = mapped_sb_element;
6512 // continue with next tile column
6515 level->fieldx = MAX(x, level->fieldx);
6518 if (invalid_playfield_char)
6520 // if first playfield line, treat invalid lines as comment lines
6522 reading_playfield = FALSE;
6527 // continue with next tile row
6535 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6536 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6538 if (!reading_playfield)
6540 level->no_valid_file = TRUE;
6542 Warn("cannot read level '%s' -- using empty level", filename);
6547 if (*level_name != '\0')
6549 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6550 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6552 else if (*last_comment != '\0')
6554 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6555 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6559 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6562 // set all empty fields beyond the border walls to invisible steel wall
6563 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6565 if ((x == 0 || x == level->fieldx - 1 ||
6566 y == 0 || y == level->fieldy - 1) &&
6567 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6568 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6569 level->field, level->fieldx, level->fieldy);
6572 // set special level settings for Sokoban levels
6573 SetLevelSettings_SB(level);
6575 if (load_xsb_to_ces)
6577 // special global settings can now be set in level template
6578 level->use_custom_template = TRUE;
6583 // -------------------------------------------------------------------------
6584 // functions for handling native levels
6585 // -------------------------------------------------------------------------
6587 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6588 struct LevelFileInfo *level_file_info,
6589 boolean level_info_only)
6593 // determine position of requested level inside level package
6594 if (level_file_info->packed)
6595 pos = level_file_info->nr - leveldir_current->first_level;
6597 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6598 level->no_valid_file = TRUE;
6601 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6602 struct LevelFileInfo *level_file_info,
6603 boolean level_info_only)
6605 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6606 level->no_valid_file = TRUE;
6609 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6610 struct LevelFileInfo *level_file_info,
6611 boolean level_info_only)
6615 // determine position of requested level inside level package
6616 if (level_file_info->packed)
6617 pos = level_file_info->nr - leveldir_current->first_level;
6619 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6620 level->no_valid_file = TRUE;
6623 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6624 struct LevelFileInfo *level_file_info,
6625 boolean level_info_only)
6627 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6628 level->no_valid_file = TRUE;
6631 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6633 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6634 CopyNativeLevel_RND_to_BD(level);
6635 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6636 CopyNativeLevel_RND_to_EM(level);
6637 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6638 CopyNativeLevel_RND_to_SP(level);
6639 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6640 CopyNativeLevel_RND_to_MM(level);
6643 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6645 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6646 CopyNativeLevel_BD_to_RND(level);
6647 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6648 CopyNativeLevel_EM_to_RND(level);
6649 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6650 CopyNativeLevel_SP_to_RND(level);
6651 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6652 CopyNativeLevel_MM_to_RND(level);
6655 void SaveNativeLevel(struct LevelInfo *level)
6657 // saving native level files only supported for some game engines
6658 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6659 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6662 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6663 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6664 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6665 char *filename = getLevelFilenameFromBasename(basename);
6667 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6670 boolean success = FALSE;
6672 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6674 CopyNativeLevel_RND_to_BD(level);
6675 // CopyNativeTape_RND_to_BD(level);
6677 success = SaveNativeLevel_BD(filename);
6679 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6681 CopyNativeLevel_RND_to_SP(level);
6682 CopyNativeTape_RND_to_SP(level);
6684 success = SaveNativeLevel_SP(filename);
6688 Request("Native level file saved!", REQ_CONFIRM);
6690 Request("Failed to save native level file!", REQ_CONFIRM);
6694 // ----------------------------------------------------------------------------
6695 // functions for loading generic level
6696 // ----------------------------------------------------------------------------
6698 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6699 struct LevelFileInfo *level_file_info,
6700 boolean level_info_only)
6702 // always start with reliable default values
6703 setLevelInfoToDefaults(level, level_info_only, TRUE);
6705 switch (level_file_info->type)
6707 case LEVEL_FILE_TYPE_RND:
6708 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6711 case LEVEL_FILE_TYPE_BD:
6712 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6713 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6716 case LEVEL_FILE_TYPE_EM:
6717 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6718 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6721 case LEVEL_FILE_TYPE_SP:
6722 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6723 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6726 case LEVEL_FILE_TYPE_MM:
6727 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6728 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6731 case LEVEL_FILE_TYPE_DC:
6732 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6735 case LEVEL_FILE_TYPE_SB:
6736 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6740 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6744 // if level file is invalid, restore level structure to default values
6745 if (level->no_valid_file)
6746 setLevelInfoToDefaults(level, level_info_only, FALSE);
6748 if (check_special_flags("use_native_bd_game_engine"))
6749 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6751 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6752 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6754 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6755 CopyNativeLevel_Native_to_RND(level);
6758 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6760 static struct LevelFileInfo level_file_info;
6762 // always start with reliable default values
6763 setFileInfoToDefaults(&level_file_info);
6765 level_file_info.nr = 0; // unknown level number
6766 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6768 setString(&level_file_info.filename, filename);
6770 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6773 static void LoadLevel_InitVersion(struct LevelInfo *level)
6777 if (leveldir_current == NULL) // only when dumping level
6780 // all engine modifications also valid for levels which use latest engine
6781 if (level->game_version < VERSION_IDENT(3,2,0,5))
6783 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6784 level->time_score_base = 10;
6787 if (leveldir_current->latest_engine)
6789 // ---------- use latest game engine --------------------------------------
6791 /* For all levels which are forced to use the latest game engine version
6792 (normally all but user contributed, private and undefined levels), set
6793 the game engine version to the actual version; this allows for actual
6794 corrections in the game engine to take effect for existing, converted
6795 levels (from "classic" or other existing games) to make the emulation
6796 of the corresponding game more accurate, while (hopefully) not breaking
6797 existing levels created from other players. */
6799 level->game_version = GAME_VERSION_ACTUAL;
6801 /* Set special EM style gems behaviour: EM style gems slip down from
6802 normal, steel and growing wall. As this is a more fundamental change,
6803 it seems better to set the default behaviour to "off" (as it is more
6804 natural) and make it configurable in the level editor (as a property
6805 of gem style elements). Already existing converted levels (neither
6806 private nor contributed levels) are changed to the new behaviour. */
6808 if (level->file_version < FILE_VERSION_2_0)
6809 level->em_slippery_gems = TRUE;
6814 // ---------- use game engine the level was created with --------------------
6816 /* For all levels which are not forced to use the latest game engine
6817 version (normally user contributed, private and undefined levels),
6818 use the version of the game engine the levels were created for.
6820 Since 2.0.1, the game engine version is now directly stored
6821 in the level file (chunk "VERS"), so there is no need anymore
6822 to set the game version from the file version (except for old,
6823 pre-2.0 levels, where the game version is still taken from the
6824 file format version used to store the level -- see above). */
6826 // player was faster than enemies in 1.0.0 and before
6827 if (level->file_version == FILE_VERSION_1_0)
6828 for (i = 0; i < MAX_PLAYERS; i++)
6829 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6831 // default behaviour for EM style gems was "slippery" only in 2.0.1
6832 if (level->game_version == VERSION_IDENT(2,0,1,0))
6833 level->em_slippery_gems = TRUE;
6835 // springs could be pushed over pits before (pre-release version) 2.2.0
6836 if (level->game_version < VERSION_IDENT(2,2,0,0))
6837 level->use_spring_bug = TRUE;
6839 if (level->game_version < VERSION_IDENT(3,2,0,5))
6841 // time orb caused limited time in endless time levels before 3.2.0-5
6842 level->use_time_orb_bug = TRUE;
6844 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6845 level->block_snap_field = FALSE;
6847 // extra time score was same value as time left score before 3.2.0-5
6848 level->extra_time_score = level->score[SC_TIME_BONUS];
6851 if (level->game_version < VERSION_IDENT(3,2,0,7))
6853 // default behaviour for snapping was "not continuous" before 3.2.0-7
6854 level->continuous_snapping = FALSE;
6857 // only few elements were able to actively move into acid before 3.1.0
6858 // trigger settings did not exist before 3.1.0; set to default "any"
6859 if (level->game_version < VERSION_IDENT(3,1,0,0))
6861 // correct "can move into acid" settings (all zero in old levels)
6863 level->can_move_into_acid_bits = 0; // nothing can move into acid
6864 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6866 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6867 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6868 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6869 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6871 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6872 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6874 // correct trigger settings (stored as zero == "none" in old levels)
6876 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6878 int element = EL_CUSTOM_START + i;
6879 struct ElementInfo *ei = &element_info[element];
6881 for (j = 0; j < ei->num_change_pages; j++)
6883 struct ElementChangeInfo *change = &ei->change_page[j];
6885 change->trigger_player = CH_PLAYER_ANY;
6886 change->trigger_page = CH_PAGE_ANY;
6891 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6893 int element = EL_CUSTOM_256;
6894 struct ElementInfo *ei = &element_info[element];
6895 struct ElementChangeInfo *change = &ei->change_page[0];
6897 /* This is needed to fix a problem that was caused by a bugfix in function
6898 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6899 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6900 not replace walkable elements, but instead just placed the player on it,
6901 without placing the Sokoban field under the player). Unfortunately, this
6902 breaks "Snake Bite" style levels when the snake is halfway through a door
6903 that just closes (the snake head is still alive and can be moved in this
6904 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6905 player (without Sokoban element) which then gets killed as designed). */
6907 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6908 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6909 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6910 change->target_element = EL_PLAYER_1;
6913 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6914 if (level->game_version < VERSION_IDENT(3,2,5,0))
6916 /* This is needed to fix a problem that was caused by a bugfix in function
6917 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6918 corrects the behaviour when a custom element changes to another custom
6919 element with a higher element number that has change actions defined.
6920 Normally, only one change per frame is allowed for custom elements.
6921 Therefore, it is checked if a custom element already changed in the
6922 current frame; if it did, subsequent changes are suppressed.
6923 Unfortunately, this is only checked for element changes, but not for
6924 change actions, which are still executed. As the function above loops
6925 through all custom elements from lower to higher, an element change
6926 resulting in a lower CE number won't be checked again, while a target
6927 element with a higher number will also be checked, and potential change
6928 actions will get executed for this CE, too (which is wrong), while
6929 further changes are ignored (which is correct). As this bugfix breaks
6930 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6931 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6932 behaviour for existing levels and tapes that make use of this bug */
6934 level->use_action_after_change_bug = TRUE;
6937 // not centering level after relocating player was default only in 3.2.3
6938 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6939 level->shifted_relocation = TRUE;
6941 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6942 if (level->game_version < VERSION_IDENT(3,2,6,0))
6943 level->em_explodes_by_fire = TRUE;
6945 // levels were solved by the first player entering an exit up to 4.1.0.0
6946 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6947 level->solved_by_one_player = TRUE;
6949 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6950 if (level->game_version < VERSION_IDENT(4,1,1,1))
6951 level->use_life_bugs = TRUE;
6953 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6954 if (level->game_version < VERSION_IDENT(4,1,1,1))
6955 level->sb_objects_needed = FALSE;
6957 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6958 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6959 level->finish_dig_collect = FALSE;
6961 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6962 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6963 level->keep_walkable_ce = TRUE;
6966 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6968 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6971 // check if this level is (not) a Sokoban level
6972 for (y = 0; y < level->fieldy; y++)
6973 for (x = 0; x < level->fieldx; x++)
6974 if (!IS_SB_ELEMENT(Tile[x][y]))
6975 is_sokoban_level = FALSE;
6977 if (is_sokoban_level)
6979 // set special level settings for Sokoban levels
6980 SetLevelSettings_SB(level);
6984 static void LoadLevel_InitSettings(struct LevelInfo *level)
6986 // adjust level settings for (non-native) Sokoban-style levels
6987 LoadLevel_InitSettings_SB(level);
6989 // rename levels with title "nameless level" or if renaming is forced
6990 if (leveldir_current->empty_level_name != NULL &&
6991 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6992 leveldir_current->force_level_name))
6993 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6994 leveldir_current->empty_level_name, level_nr);
6997 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7001 // map elements that have changed in newer versions
7002 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7003 level->game_version);
7004 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7005 for (x = 0; x < 3; x++)
7006 for (y = 0; y < 3; y++)
7007 level->yamyam_content[i].e[x][y] =
7008 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7009 level->game_version);
7013 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7017 // map custom element change events that have changed in newer versions
7018 // (these following values were accidentally changed in version 3.0.1)
7019 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7020 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7022 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7024 int element = EL_CUSTOM_START + i;
7026 // order of checking and copying events to be mapped is important
7027 // (do not change the start and end value -- they are constant)
7028 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7030 if (HAS_CHANGE_EVENT(element, j - 2))
7032 SET_CHANGE_EVENT(element, j - 2, FALSE);
7033 SET_CHANGE_EVENT(element, j, TRUE);
7037 // order of checking and copying events to be mapped is important
7038 // (do not change the start and end value -- they are constant)
7039 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7041 if (HAS_CHANGE_EVENT(element, j - 1))
7043 SET_CHANGE_EVENT(element, j - 1, FALSE);
7044 SET_CHANGE_EVENT(element, j, TRUE);
7050 // initialize "can_change" field for old levels with only one change page
7051 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7053 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7055 int element = EL_CUSTOM_START + i;
7057 if (CAN_CHANGE(element))
7058 element_info[element].change->can_change = TRUE;
7062 // correct custom element values (for old levels without these options)
7063 if (level->game_version < VERSION_IDENT(3,1,1,0))
7065 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7067 int element = EL_CUSTOM_START + i;
7068 struct ElementInfo *ei = &element_info[element];
7070 if (ei->access_direction == MV_NO_DIRECTION)
7071 ei->access_direction = MV_ALL_DIRECTIONS;
7075 // correct custom element values (fix invalid values for all versions)
7078 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7080 int element = EL_CUSTOM_START + i;
7081 struct ElementInfo *ei = &element_info[element];
7083 for (j = 0; j < ei->num_change_pages; j++)
7085 struct ElementChangeInfo *change = &ei->change_page[j];
7087 if (change->trigger_player == CH_PLAYER_NONE)
7088 change->trigger_player = CH_PLAYER_ANY;
7090 if (change->trigger_side == CH_SIDE_NONE)
7091 change->trigger_side = CH_SIDE_ANY;
7096 // initialize "can_explode" field for old levels which did not store this
7097 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7098 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7100 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7102 int element = EL_CUSTOM_START + i;
7104 if (EXPLODES_1X1_OLD(element))
7105 element_info[element].explosion_type = EXPLODES_1X1;
7107 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7108 EXPLODES_SMASHED(element) ||
7109 EXPLODES_IMPACT(element)));
7113 // correct previously hard-coded move delay values for maze runner style
7114 if (level->game_version < VERSION_IDENT(3,1,1,0))
7116 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7118 int element = EL_CUSTOM_START + i;
7120 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7122 // previously hard-coded and therefore ignored
7123 element_info[element].move_delay_fixed = 9;
7124 element_info[element].move_delay_random = 0;
7129 // set some other uninitialized values of custom elements in older levels
7130 if (level->game_version < VERSION_IDENT(3,1,0,0))
7132 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7134 int element = EL_CUSTOM_START + i;
7136 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7138 element_info[element].explosion_delay = 17;
7139 element_info[element].ignition_delay = 8;
7143 // set mouse click change events to work for left/middle/right mouse button
7144 if (level->game_version < VERSION_IDENT(4,2,3,0))
7146 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7148 int element = EL_CUSTOM_START + i;
7149 struct ElementInfo *ei = &element_info[element];
7151 for (j = 0; j < ei->num_change_pages; j++)
7153 struct ElementChangeInfo *change = &ei->change_page[j];
7155 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7156 change->has_event[CE_PRESSED_BY_MOUSE] ||
7157 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7158 change->has_event[CE_MOUSE_PRESSED_ON_X])
7159 change->trigger_side = CH_SIDE_ANY;
7165 static void LoadLevel_InitElements(struct LevelInfo *level)
7167 LoadLevel_InitStandardElements(level);
7169 if (level->file_has_custom_elements)
7170 LoadLevel_InitCustomElements(level);
7172 // initialize element properties for level editor etc.
7173 InitElementPropertiesEngine(level->game_version);
7174 InitElementPropertiesGfxElement();
7177 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7181 // map elements that have changed in newer versions
7182 for (y = 0; y < level->fieldy; y++)
7183 for (x = 0; x < level->fieldx; x++)
7184 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7185 level->game_version);
7187 // clear unused playfield data (nicer if level gets resized in editor)
7188 for (x = 0; x < MAX_LEV_FIELDX; x++)
7189 for (y = 0; y < MAX_LEV_FIELDY; y++)
7190 if (x >= level->fieldx || y >= level->fieldy)
7191 level->field[x][y] = EL_EMPTY;
7193 // copy elements to runtime playfield array
7194 for (x = 0; x < MAX_LEV_FIELDX; x++)
7195 for (y = 0; y < MAX_LEV_FIELDY; y++)
7196 Tile[x][y] = level->field[x][y];
7198 // initialize level size variables for faster access
7199 lev_fieldx = level->fieldx;
7200 lev_fieldy = level->fieldy;
7202 // determine border element for this level
7203 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7204 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7209 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7211 struct LevelFileInfo *level_file_info = &level->file_info;
7213 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7214 CopyNativeLevel_RND_to_Native(level);
7217 static void LoadLevelTemplate_LoadAndInit(void)
7219 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7221 LoadLevel_InitVersion(&level_template);
7222 LoadLevel_InitElements(&level_template);
7223 LoadLevel_InitSettings(&level_template);
7225 ActivateLevelTemplate();
7228 void LoadLevelTemplate(int nr)
7230 if (!fileExists(getGlobalLevelTemplateFilename()))
7232 Warn("no level template found for this level");
7237 setLevelFileInfo(&level_template.file_info, nr);
7239 LoadLevelTemplate_LoadAndInit();
7242 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7244 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7246 LoadLevelTemplate_LoadAndInit();
7249 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7251 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7253 if (level.use_custom_template)
7255 if (network_level != NULL)
7256 LoadNetworkLevelTemplate(network_level);
7258 LoadLevelTemplate(-1);
7261 LoadLevel_InitVersion(&level);
7262 LoadLevel_InitElements(&level);
7263 LoadLevel_InitPlayfield(&level);
7264 LoadLevel_InitSettings(&level);
7266 LoadLevel_InitNativeEngines(&level);
7269 void LoadLevel(int nr)
7271 SetLevelSetInfo(leveldir_current->identifier, nr);
7273 setLevelFileInfo(&level.file_info, nr);
7275 LoadLevel_LoadAndInit(NULL);
7278 void LoadLevelInfoOnly(int nr)
7280 setLevelFileInfo(&level.file_info, nr);
7282 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7285 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7287 SetLevelSetInfo(network_level->leveldir_identifier,
7288 network_level->file_info.nr);
7290 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7292 LoadLevel_LoadAndInit(network_level);
7295 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7299 chunk_size += putFileVersion(file, level->file_version);
7300 chunk_size += putFileVersion(file, level->game_version);
7305 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7309 chunk_size += putFile16BitBE(file, level->creation_date.year);
7310 chunk_size += putFile8Bit(file, level->creation_date.month);
7311 chunk_size += putFile8Bit(file, level->creation_date.day);
7316 #if ENABLE_HISTORIC_CHUNKS
7317 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7321 putFile8Bit(file, level->fieldx);
7322 putFile8Bit(file, level->fieldy);
7324 putFile16BitBE(file, level->time);
7325 putFile16BitBE(file, level->gems_needed);
7327 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7328 putFile8Bit(file, level->name[i]);
7330 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7331 putFile8Bit(file, level->score[i]);
7333 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7334 for (y = 0; y < 3; y++)
7335 for (x = 0; x < 3; x++)
7336 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7337 level->yamyam_content[i].e[x][y]));
7338 putFile8Bit(file, level->amoeba_speed);
7339 putFile8Bit(file, level->time_magic_wall);
7340 putFile8Bit(file, level->time_wheel);
7341 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7342 level->amoeba_content));
7343 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7344 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7345 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7346 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7348 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7350 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7351 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7352 putFile32BitBE(file, level->can_move_into_acid_bits);
7353 putFile8Bit(file, level->dont_collide_with_bits);
7355 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7356 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7358 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7359 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7360 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7362 putFile8Bit(file, level->game_engine_type);
7364 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7368 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7373 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7374 chunk_size += putFile8Bit(file, level->name[i]);
7379 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7384 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7385 chunk_size += putFile8Bit(file, level->author[i]);
7390 #if ENABLE_HISTORIC_CHUNKS
7391 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7396 for (y = 0; y < level->fieldy; y++)
7397 for (x = 0; x < level->fieldx; x++)
7398 if (level->encoding_16bit_field)
7399 chunk_size += putFile16BitBE(file, level->field[x][y]);
7401 chunk_size += putFile8Bit(file, level->field[x][y]);
7407 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7412 for (y = 0; y < level->fieldy; y++)
7413 for (x = 0; x < level->fieldx; x++)
7414 chunk_size += putFile16BitBE(file, level->field[x][y]);
7419 #if ENABLE_HISTORIC_CHUNKS
7420 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7424 putFile8Bit(file, EL_YAMYAM);
7425 putFile8Bit(file, level->num_yamyam_contents);
7426 putFile8Bit(file, 0);
7427 putFile8Bit(file, 0);
7429 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7430 for (y = 0; y < 3; y++)
7431 for (x = 0; x < 3; x++)
7432 if (level->encoding_16bit_field)
7433 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7435 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7439 #if ENABLE_HISTORIC_CHUNKS
7440 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7443 int num_contents, content_xsize, content_ysize;
7444 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7446 if (element == EL_YAMYAM)
7448 num_contents = level->num_yamyam_contents;
7452 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7453 for (y = 0; y < 3; y++)
7454 for (x = 0; x < 3; x++)
7455 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7457 else if (element == EL_BD_AMOEBA)
7463 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7464 for (y = 0; y < 3; y++)
7465 for (x = 0; x < 3; x++)
7466 content_array[i][x][y] = EL_EMPTY;
7467 content_array[0][0][0] = level->amoeba_content;
7471 // chunk header already written -- write empty chunk data
7472 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7474 Warn("cannot save content for element '%d'", element);
7479 putFile16BitBE(file, element);
7480 putFile8Bit(file, num_contents);
7481 putFile8Bit(file, content_xsize);
7482 putFile8Bit(file, content_ysize);
7484 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7486 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7487 for (y = 0; y < 3; y++)
7488 for (x = 0; x < 3; x++)
7489 putFile16BitBE(file, content_array[i][x][y]);
7493 #if ENABLE_HISTORIC_CHUNKS
7494 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7496 int envelope_nr = element - EL_ENVELOPE_1;
7497 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7501 chunk_size += putFile16BitBE(file, element);
7502 chunk_size += putFile16BitBE(file, envelope_len);
7503 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7504 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7506 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7507 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7509 for (i = 0; i < envelope_len; i++)
7510 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7516 #if ENABLE_HISTORIC_CHUNKS
7517 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7518 int num_changed_custom_elements)
7522 putFile16BitBE(file, num_changed_custom_elements);
7524 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7526 int element = EL_CUSTOM_START + i;
7528 struct ElementInfo *ei = &element_info[element];
7530 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7532 if (check < num_changed_custom_elements)
7534 putFile16BitBE(file, element);
7535 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7542 if (check != num_changed_custom_elements) // should not happen
7543 Warn("inconsistent number of custom element properties");
7547 #if ENABLE_HISTORIC_CHUNKS
7548 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7549 int num_changed_custom_elements)
7553 putFile16BitBE(file, num_changed_custom_elements);
7555 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7557 int element = EL_CUSTOM_START + i;
7559 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7561 if (check < num_changed_custom_elements)
7563 putFile16BitBE(file, element);
7564 putFile16BitBE(file, element_info[element].change->target_element);
7571 if (check != num_changed_custom_elements) // should not happen
7572 Warn("inconsistent number of custom target elements");
7576 #if ENABLE_HISTORIC_CHUNKS
7577 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7578 int num_changed_custom_elements)
7580 int i, j, x, y, check = 0;
7582 putFile16BitBE(file, num_changed_custom_elements);
7584 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7586 int element = EL_CUSTOM_START + i;
7587 struct ElementInfo *ei = &element_info[element];
7589 if (ei->modified_settings)
7591 if (check < num_changed_custom_elements)
7593 putFile16BitBE(file, element);
7595 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7596 putFile8Bit(file, ei->description[j]);
7598 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7600 // some free bytes for future properties and padding
7601 WriteUnusedBytesToFile(file, 7);
7603 putFile8Bit(file, ei->use_gfx_element);
7604 putFile16BitBE(file, ei->gfx_element_initial);
7606 putFile8Bit(file, ei->collect_score_initial);
7607 putFile8Bit(file, ei->collect_count_initial);
7609 putFile16BitBE(file, ei->push_delay_fixed);
7610 putFile16BitBE(file, ei->push_delay_random);
7611 putFile16BitBE(file, ei->move_delay_fixed);
7612 putFile16BitBE(file, ei->move_delay_random);
7614 putFile16BitBE(file, ei->move_pattern);
7615 putFile8Bit(file, ei->move_direction_initial);
7616 putFile8Bit(file, ei->move_stepsize);
7618 for (y = 0; y < 3; y++)
7619 for (x = 0; x < 3; x++)
7620 putFile16BitBE(file, ei->content.e[x][y]);
7622 putFile32BitBE(file, ei->change->events);
7624 putFile16BitBE(file, ei->change->target_element);
7626 putFile16BitBE(file, ei->change->delay_fixed);
7627 putFile16BitBE(file, ei->change->delay_random);
7628 putFile16BitBE(file, ei->change->delay_frames);
7630 putFile16BitBE(file, ei->change->initial_trigger_element);
7632 putFile8Bit(file, ei->change->explode);
7633 putFile8Bit(file, ei->change->use_target_content);
7634 putFile8Bit(file, ei->change->only_if_complete);
7635 putFile8Bit(file, ei->change->use_random_replace);
7637 putFile8Bit(file, ei->change->random_percentage);
7638 putFile8Bit(file, ei->change->replace_when);
7640 for (y = 0; y < 3; y++)
7641 for (x = 0; x < 3; x++)
7642 putFile16BitBE(file, ei->change->content.e[x][y]);
7644 putFile8Bit(file, ei->slippery_type);
7646 // some free bytes for future properties and padding
7647 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7654 if (check != num_changed_custom_elements) // should not happen
7655 Warn("inconsistent number of custom element properties");
7659 #if ENABLE_HISTORIC_CHUNKS
7660 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7662 struct ElementInfo *ei = &element_info[element];
7665 // ---------- custom element base property values (96 bytes) ----------------
7667 putFile16BitBE(file, element);
7669 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7670 putFile8Bit(file, ei->description[i]);
7672 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7674 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7676 putFile8Bit(file, ei->num_change_pages);
7678 putFile16BitBE(file, ei->ce_value_fixed_initial);
7679 putFile16BitBE(file, ei->ce_value_random_initial);
7680 putFile8Bit(file, ei->use_last_ce_value);
7682 putFile8Bit(file, ei->use_gfx_element);
7683 putFile16BitBE(file, ei->gfx_element_initial);
7685 putFile8Bit(file, ei->collect_score_initial);
7686 putFile8Bit(file, ei->collect_count_initial);
7688 putFile8Bit(file, ei->drop_delay_fixed);
7689 putFile8Bit(file, ei->push_delay_fixed);
7690 putFile8Bit(file, ei->drop_delay_random);
7691 putFile8Bit(file, ei->push_delay_random);
7692 putFile16BitBE(file, ei->move_delay_fixed);
7693 putFile16BitBE(file, ei->move_delay_random);
7695 // bits 0 - 15 of "move_pattern" ...
7696 putFile16BitBE(file, ei->move_pattern & 0xffff);
7697 putFile8Bit(file, ei->move_direction_initial);
7698 putFile8Bit(file, ei->move_stepsize);
7700 putFile8Bit(file, ei->slippery_type);
7702 for (y = 0; y < 3; y++)
7703 for (x = 0; x < 3; x++)
7704 putFile16BitBE(file, ei->content.e[x][y]);
7706 putFile16BitBE(file, ei->move_enter_element);
7707 putFile16BitBE(file, ei->move_leave_element);
7708 putFile8Bit(file, ei->move_leave_type);
7710 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7711 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7713 putFile8Bit(file, ei->access_direction);
7715 putFile8Bit(file, ei->explosion_delay);
7716 putFile8Bit(file, ei->ignition_delay);
7717 putFile8Bit(file, ei->explosion_type);
7719 // some free bytes for future custom property values and padding
7720 WriteUnusedBytesToFile(file, 1);
7722 // ---------- change page property values (48 bytes) ------------------------
7724 for (i = 0; i < ei->num_change_pages; i++)
7726 struct ElementChangeInfo *change = &ei->change_page[i];
7727 unsigned int event_bits;
7729 // bits 0 - 31 of "has_event[]" ...
7731 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7732 if (change->has_event[j])
7733 event_bits |= (1u << j);
7734 putFile32BitBE(file, event_bits);
7736 putFile16BitBE(file, change->target_element);
7738 putFile16BitBE(file, change->delay_fixed);
7739 putFile16BitBE(file, change->delay_random);
7740 putFile16BitBE(file, change->delay_frames);
7742 putFile16BitBE(file, change->initial_trigger_element);
7744 putFile8Bit(file, change->explode);
7745 putFile8Bit(file, change->use_target_content);
7746 putFile8Bit(file, change->only_if_complete);
7747 putFile8Bit(file, change->use_random_replace);
7749 putFile8Bit(file, change->random_percentage);
7750 putFile8Bit(file, change->replace_when);
7752 for (y = 0; y < 3; y++)
7753 for (x = 0; x < 3; x++)
7754 putFile16BitBE(file, change->target_content.e[x][y]);
7756 putFile8Bit(file, change->can_change);
7758 putFile8Bit(file, change->trigger_side);
7760 putFile8Bit(file, change->trigger_player);
7761 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7762 log_2(change->trigger_page)));
7764 putFile8Bit(file, change->has_action);
7765 putFile8Bit(file, change->action_type);
7766 putFile8Bit(file, change->action_mode);
7767 putFile16BitBE(file, change->action_arg);
7769 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7771 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7772 if (change->has_event[j])
7773 event_bits |= (1u << (j - 32));
7774 putFile8Bit(file, event_bits);
7779 #if ENABLE_HISTORIC_CHUNKS
7780 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7782 struct ElementInfo *ei = &element_info[element];
7783 struct ElementGroupInfo *group = ei->group;
7786 putFile16BitBE(file, element);
7788 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7789 putFile8Bit(file, ei->description[i]);
7791 putFile8Bit(file, group->num_elements);
7793 putFile8Bit(file, ei->use_gfx_element);
7794 putFile16BitBE(file, ei->gfx_element_initial);
7796 putFile8Bit(file, group->choice_mode);
7798 // some free bytes for future values and padding
7799 WriteUnusedBytesToFile(file, 3);
7801 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7802 putFile16BitBE(file, group->element[i]);
7806 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7807 boolean write_element)
7809 int save_type = entry->save_type;
7810 int data_type = entry->data_type;
7811 int conf_type = entry->conf_type;
7812 int byte_mask = conf_type & CONF_MASK_BYTES;
7813 int element = entry->element;
7814 int default_value = entry->default_value;
7816 boolean modified = FALSE;
7818 if (byte_mask != CONF_MASK_MULTI_BYTES)
7820 void *value_ptr = entry->value;
7821 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7824 // check if any settings have been modified before saving them
7825 if (value != default_value)
7828 // do not save if explicitly told or if unmodified default settings
7829 if ((save_type == SAVE_CONF_NEVER) ||
7830 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7834 num_bytes += putFile16BitBE(file, element);
7836 num_bytes += putFile8Bit(file, conf_type);
7837 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7838 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7839 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7842 else if (data_type == TYPE_STRING)
7844 char *default_string = entry->default_string;
7845 char *string = (char *)(entry->value);
7846 int string_length = strlen(string);
7849 // check if any settings have been modified before saving them
7850 if (!strEqual(string, default_string))
7853 // do not save if explicitly told or if unmodified default settings
7854 if ((save_type == SAVE_CONF_NEVER) ||
7855 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7859 num_bytes += putFile16BitBE(file, element);
7861 num_bytes += putFile8Bit(file, conf_type);
7862 num_bytes += putFile16BitBE(file, string_length);
7864 for (i = 0; i < string_length; i++)
7865 num_bytes += putFile8Bit(file, string[i]);
7867 else if (data_type == TYPE_ELEMENT_LIST)
7869 int *element_array = (int *)(entry->value);
7870 int num_elements = *(int *)(entry->num_entities);
7873 // check if any settings have been modified before saving them
7874 for (i = 0; i < num_elements; i++)
7875 if (element_array[i] != default_value)
7878 // do not save if explicitly told or if unmodified default settings
7879 if ((save_type == SAVE_CONF_NEVER) ||
7880 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7884 num_bytes += putFile16BitBE(file, element);
7886 num_bytes += putFile8Bit(file, conf_type);
7887 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7889 for (i = 0; i < num_elements; i++)
7890 num_bytes += putFile16BitBE(file, element_array[i]);
7892 else if (data_type == TYPE_CONTENT_LIST)
7894 struct Content *content = (struct Content *)(entry->value);
7895 int num_contents = *(int *)(entry->num_entities);
7898 // check if any settings have been modified before saving them
7899 for (i = 0; i < num_contents; i++)
7900 for (y = 0; y < 3; y++)
7901 for (x = 0; x < 3; x++)
7902 if (content[i].e[x][y] != default_value)
7905 // do not save if explicitly told or if unmodified default settings
7906 if ((save_type == SAVE_CONF_NEVER) ||
7907 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7911 num_bytes += putFile16BitBE(file, element);
7913 num_bytes += putFile8Bit(file, conf_type);
7914 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7916 for (i = 0; i < num_contents; i++)
7917 for (y = 0; y < 3; y++)
7918 for (x = 0; x < 3; x++)
7919 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7925 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7930 li = *level; // copy level data into temporary buffer
7932 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7933 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7938 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7943 li = *level; // copy level data into temporary buffer
7945 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7946 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7951 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7953 int envelope_nr = element - EL_ENVELOPE_1;
7957 chunk_size += putFile16BitBE(file, element);
7959 // copy envelope data into temporary buffer
7960 xx_envelope = level->envelope[envelope_nr];
7962 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7963 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7968 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7970 struct ElementInfo *ei = &element_info[element];
7974 chunk_size += putFile16BitBE(file, element);
7976 xx_ei = *ei; // copy element data into temporary buffer
7978 // set default description string for this specific element
7979 strcpy(xx_default_description, getDefaultElementDescription(ei));
7981 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7982 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7984 for (i = 0; i < ei->num_change_pages; i++)
7986 struct ElementChangeInfo *change = &ei->change_page[i];
7988 xx_current_change_page = i;
7990 xx_change = *change; // copy change data into temporary buffer
7993 setEventBitsFromEventFlags(change);
7995 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7996 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8003 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8005 struct ElementInfo *ei = &element_info[element];
8006 struct ElementGroupInfo *group = ei->group;
8010 chunk_size += putFile16BitBE(file, element);
8012 xx_ei = *ei; // copy element data into temporary buffer
8013 xx_group = *group; // copy group data into temporary buffer
8015 // set default description string for this specific element
8016 strcpy(xx_default_description, getDefaultElementDescription(ei));
8018 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8019 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8024 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8026 struct ElementInfo *ei = &element_info[element];
8030 chunk_size += putFile16BitBE(file, element);
8032 xx_ei = *ei; // copy element data into temporary buffer
8034 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8035 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8040 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8041 boolean save_as_template)
8047 if (!(file = fopen(filename, MODE_WRITE)))
8049 Warn("cannot save level file '%s'", filename);
8054 level->file_version = FILE_VERSION_ACTUAL;
8055 level->game_version = GAME_VERSION_ACTUAL;
8057 level->creation_date = getCurrentDate();
8059 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8060 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8062 chunk_size = SaveLevel_VERS(NULL, level);
8063 putFileChunkBE(file, "VERS", chunk_size);
8064 SaveLevel_VERS(file, level);
8066 chunk_size = SaveLevel_DATE(NULL, level);
8067 putFileChunkBE(file, "DATE", chunk_size);
8068 SaveLevel_DATE(file, level);
8070 chunk_size = SaveLevel_NAME(NULL, level);
8071 putFileChunkBE(file, "NAME", chunk_size);
8072 SaveLevel_NAME(file, level);
8074 chunk_size = SaveLevel_AUTH(NULL, level);
8075 putFileChunkBE(file, "AUTH", chunk_size);
8076 SaveLevel_AUTH(file, level);
8078 chunk_size = SaveLevel_INFO(NULL, level);
8079 putFileChunkBE(file, "INFO", chunk_size);
8080 SaveLevel_INFO(file, level);
8082 chunk_size = SaveLevel_BODY(NULL, level);
8083 putFileChunkBE(file, "BODY", chunk_size);
8084 SaveLevel_BODY(file, level);
8086 chunk_size = SaveLevel_ELEM(NULL, level);
8087 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8089 putFileChunkBE(file, "ELEM", chunk_size);
8090 SaveLevel_ELEM(file, level);
8093 for (i = 0; i < NUM_ENVELOPES; i++)
8095 int element = EL_ENVELOPE_1 + i;
8097 chunk_size = SaveLevel_NOTE(NULL, level, element);
8098 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8100 putFileChunkBE(file, "NOTE", chunk_size);
8101 SaveLevel_NOTE(file, level, element);
8105 // if not using template level, check for non-default custom/group elements
8106 if (!level->use_custom_template || save_as_template)
8108 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8110 int element = EL_CUSTOM_START + i;
8112 chunk_size = SaveLevel_CUSX(NULL, level, element);
8113 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8115 putFileChunkBE(file, "CUSX", chunk_size);
8116 SaveLevel_CUSX(file, level, element);
8120 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8122 int element = EL_GROUP_START + i;
8124 chunk_size = SaveLevel_GRPX(NULL, level, element);
8125 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8127 putFileChunkBE(file, "GRPX", chunk_size);
8128 SaveLevel_GRPX(file, level, element);
8132 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8134 int element = GET_EMPTY_ELEMENT(i);
8136 chunk_size = SaveLevel_EMPX(NULL, level, element);
8137 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8139 putFileChunkBE(file, "EMPX", chunk_size);
8140 SaveLevel_EMPX(file, level, element);
8147 SetFilePermissions(filename, PERMS_PRIVATE);
8150 void SaveLevel(int nr)
8152 char *filename = getDefaultLevelFilename(nr);
8154 SaveLevelFromFilename(&level, filename, FALSE);
8157 void SaveLevelTemplate(void)
8159 char *filename = getLocalLevelTemplateFilename();
8161 SaveLevelFromFilename(&level, filename, TRUE);
8164 boolean SaveLevelChecked(int nr)
8166 char *filename = getDefaultLevelFilename(nr);
8167 boolean new_level = !fileExists(filename);
8168 boolean level_saved = FALSE;
8170 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8175 Request("Level saved!", REQ_CONFIRM);
8183 void DumpLevel(struct LevelInfo *level)
8185 if (level->no_level_file || level->no_valid_file)
8187 Warn("cannot dump -- no valid level file found");
8193 Print("Level xxx (file version %08d, game version %08d)\n",
8194 level->file_version, level->game_version);
8197 Print("Level author: '%s'\n", level->author);
8198 Print("Level title: '%s'\n", level->name);
8200 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8202 Print("Level time: %d seconds\n", level->time);
8203 Print("Gems needed: %d\n", level->gems_needed);
8205 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8206 Print("Time for wheel: %d seconds\n", level->time_wheel);
8207 Print("Time for light: %d seconds\n", level->time_light);
8208 Print("Time for timegate: %d seconds\n", level->time_timegate);
8210 Print("Amoeba speed: %d\n", level->amoeba_speed);
8213 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8214 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8215 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8216 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8217 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8218 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8224 for (i = 0; i < NUM_ENVELOPES; i++)
8226 char *text = level->envelope[i].text;
8227 int text_len = strlen(text);
8228 boolean has_text = FALSE;
8230 for (j = 0; j < text_len; j++)
8231 if (text[j] != ' ' && text[j] != '\n')
8237 Print("Envelope %d:\n'%s'\n", i + 1, text);
8245 void DumpLevels(void)
8247 static LevelDirTree *dumplevel_leveldir = NULL;
8249 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8250 global.dumplevel_leveldir);
8252 if (dumplevel_leveldir == NULL)
8253 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8255 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8256 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8257 Fail("no such level number: %d", global.dumplevel_level_nr);
8259 leveldir_current = dumplevel_leveldir;
8261 LoadLevel(global.dumplevel_level_nr);
8268 // ============================================================================
8269 // tape file functions
8270 // ============================================================================
8272 static void setTapeInfoToDefaults(void)
8276 // always start with reliable default values (empty tape)
8279 // default values (also for pre-1.2 tapes) with only the first player
8280 tape.player_participates[0] = TRUE;
8281 for (i = 1; i < MAX_PLAYERS; i++)
8282 tape.player_participates[i] = FALSE;
8284 // at least one (default: the first) player participates in every tape
8285 tape.num_participating_players = 1;
8287 tape.property_bits = TAPE_PROPERTY_NONE;
8289 tape.level_nr = level_nr;
8291 tape.changed = FALSE;
8292 tape.solved = FALSE;
8294 tape.recording = FALSE;
8295 tape.playing = FALSE;
8296 tape.pausing = FALSE;
8298 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8299 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8301 tape.no_info_chunk = TRUE;
8302 tape.no_valid_file = FALSE;
8305 static int getTapePosSize(struct TapeInfo *tape)
8307 int tape_pos_size = 0;
8309 if (tape->use_key_actions)
8310 tape_pos_size += tape->num_participating_players;
8312 if (tape->use_mouse_actions)
8313 tape_pos_size += 3; // x and y position and mouse button mask
8315 tape_pos_size += 1; // tape action delay value
8317 return tape_pos_size;
8320 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8322 tape->use_key_actions = FALSE;
8323 tape->use_mouse_actions = FALSE;
8325 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8326 tape->use_key_actions = TRUE;
8328 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8329 tape->use_mouse_actions = TRUE;
8332 static int getTapeActionValue(struct TapeInfo *tape)
8334 return (tape->use_key_actions &&
8335 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8336 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8337 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8338 TAPE_ACTIONS_DEFAULT);
8341 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8343 tape->file_version = getFileVersion(file);
8344 tape->game_version = getFileVersion(file);
8349 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8353 tape->random_seed = getFile32BitBE(file);
8354 tape->date = getFile32BitBE(file);
8355 tape->length = getFile32BitBE(file);
8357 // read header fields that are new since version 1.2
8358 if (tape->file_version >= FILE_VERSION_1_2)
8360 byte store_participating_players = getFile8Bit(file);
8363 // since version 1.2, tapes store which players participate in the tape
8364 tape->num_participating_players = 0;
8365 for (i = 0; i < MAX_PLAYERS; i++)
8367 tape->player_participates[i] = FALSE;
8369 if (store_participating_players & (1 << i))
8371 tape->player_participates[i] = TRUE;
8372 tape->num_participating_players++;
8376 setTapeActionFlags(tape, getFile8Bit(file));
8378 tape->property_bits = getFile8Bit(file);
8379 tape->solved = getFile8Bit(file);
8381 engine_version = getFileVersion(file);
8382 if (engine_version > 0)
8383 tape->engine_version = engine_version;
8385 tape->engine_version = tape->game_version;
8391 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8393 tape->scr_fieldx = getFile8Bit(file);
8394 tape->scr_fieldy = getFile8Bit(file);
8399 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8401 char *level_identifier = NULL;
8402 int level_identifier_size;
8405 tape->no_info_chunk = FALSE;
8407 level_identifier_size = getFile16BitBE(file);
8409 level_identifier = checked_malloc(level_identifier_size);
8411 for (i = 0; i < level_identifier_size; i++)
8412 level_identifier[i] = getFile8Bit(file);
8414 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8415 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8417 checked_free(level_identifier);
8419 tape->level_nr = getFile16BitBE(file);
8421 chunk_size = 2 + level_identifier_size + 2;
8426 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8429 int tape_pos_size = getTapePosSize(tape);
8430 int chunk_size_expected = tape_pos_size * tape->length;
8432 if (chunk_size_expected != chunk_size)
8434 ReadUnusedBytesFromFile(file, chunk_size);
8435 return chunk_size_expected;
8438 for (i = 0; i < tape->length; i++)
8440 if (i >= MAX_TAPE_LEN)
8442 Warn("tape truncated -- size exceeds maximum tape size %d",
8445 // tape too large; read and ignore remaining tape data from this chunk
8446 for (;i < tape->length; i++)
8447 ReadUnusedBytesFromFile(file, tape_pos_size);
8452 if (tape->use_key_actions)
8454 for (j = 0; j < MAX_PLAYERS; j++)
8456 tape->pos[i].action[j] = MV_NONE;
8458 if (tape->player_participates[j])
8459 tape->pos[i].action[j] = getFile8Bit(file);
8463 if (tape->use_mouse_actions)
8465 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8466 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8467 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8470 tape->pos[i].delay = getFile8Bit(file);
8472 if (tape->file_version == FILE_VERSION_1_0)
8474 // eliminate possible diagonal moves in old tapes
8475 // this is only for backward compatibility
8477 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8478 byte action = tape->pos[i].action[0];
8479 int k, num_moves = 0;
8481 for (k = 0; k < 4; k++)
8483 if (action & joy_dir[k])
8485 tape->pos[i + num_moves].action[0] = joy_dir[k];
8487 tape->pos[i + num_moves].delay = 0;
8496 tape->length += num_moves;
8499 else if (tape->file_version < FILE_VERSION_2_0)
8501 // convert pre-2.0 tapes to new tape format
8503 if (tape->pos[i].delay > 1)
8506 tape->pos[i + 1] = tape->pos[i];
8507 tape->pos[i + 1].delay = 1;
8510 for (j = 0; j < MAX_PLAYERS; j++)
8511 tape->pos[i].action[j] = MV_NONE;
8512 tape->pos[i].delay--;
8519 if (checkEndOfFile(file))
8523 if (i != tape->length)
8524 chunk_size = tape_pos_size * i;
8529 static void LoadTape_SokobanSolution(char *filename)
8532 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8534 if (!(file = openFile(filename, MODE_READ)))
8536 tape.no_valid_file = TRUE;
8541 while (!checkEndOfFile(file))
8543 unsigned char c = getByteFromFile(file);
8545 if (checkEndOfFile(file))
8552 tape.pos[tape.length].action[0] = MV_UP;
8553 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8559 tape.pos[tape.length].action[0] = MV_DOWN;
8560 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8566 tape.pos[tape.length].action[0] = MV_LEFT;
8567 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8573 tape.pos[tape.length].action[0] = MV_RIGHT;
8574 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8582 // ignore white-space characters
8586 tape.no_valid_file = TRUE;
8588 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8596 if (tape.no_valid_file)
8599 tape.length_frames = GetTapeLengthFrames();
8600 tape.length_seconds = GetTapeLengthSeconds();
8603 void LoadTapeFromFilename(char *filename)
8605 char cookie[MAX_LINE_LEN];
8606 char chunk_name[CHUNK_ID_LEN + 1];
8610 // always start with reliable default values
8611 setTapeInfoToDefaults();
8613 if (strSuffix(filename, ".sln"))
8615 LoadTape_SokobanSolution(filename);
8620 if (!(file = openFile(filename, MODE_READ)))
8622 tape.no_valid_file = TRUE;
8627 getFileChunkBE(file, chunk_name, NULL);
8628 if (strEqual(chunk_name, "RND1"))
8630 getFile32BitBE(file); // not used
8632 getFileChunkBE(file, chunk_name, NULL);
8633 if (!strEqual(chunk_name, "TAPE"))
8635 tape.no_valid_file = TRUE;
8637 Warn("unknown format of tape file '%s'", filename);
8644 else // check for pre-2.0 file format with cookie string
8646 strcpy(cookie, chunk_name);
8647 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8649 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8650 cookie[strlen(cookie) - 1] = '\0';
8652 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8654 tape.no_valid_file = TRUE;
8656 Warn("unknown format of tape file '%s'", filename);
8663 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8665 tape.no_valid_file = TRUE;
8667 Warn("unsupported version of tape file '%s'", filename);
8674 // pre-2.0 tape files have no game version, so use file version here
8675 tape.game_version = tape.file_version;
8678 if (tape.file_version < FILE_VERSION_1_2)
8680 // tape files from versions before 1.2.0 without chunk structure
8681 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8682 LoadTape_BODY(file, 2 * tape.length, &tape);
8690 int (*loader)(File *, int, struct TapeInfo *);
8694 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8695 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8696 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8697 { "INFO", -1, LoadTape_INFO },
8698 { "BODY", -1, LoadTape_BODY },
8702 while (getFileChunkBE(file, chunk_name, &chunk_size))
8706 while (chunk_info[i].name != NULL &&
8707 !strEqual(chunk_name, chunk_info[i].name))
8710 if (chunk_info[i].name == NULL)
8712 Warn("unknown chunk '%s' in tape file '%s'",
8713 chunk_name, filename);
8715 ReadUnusedBytesFromFile(file, chunk_size);
8717 else if (chunk_info[i].size != -1 &&
8718 chunk_info[i].size != chunk_size)
8720 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8721 chunk_size, chunk_name, filename);
8723 ReadUnusedBytesFromFile(file, chunk_size);
8727 // call function to load this tape chunk
8728 int chunk_size_expected =
8729 (chunk_info[i].loader)(file, chunk_size, &tape);
8731 // the size of some chunks cannot be checked before reading other
8732 // chunks first (like "HEAD" and "BODY") that contain some header
8733 // information, so check them here
8734 if (chunk_size_expected != chunk_size)
8736 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8737 chunk_size, chunk_name, filename);
8745 tape.length_frames = GetTapeLengthFrames();
8746 tape.length_seconds = GetTapeLengthSeconds();
8749 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8751 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8753 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8754 tape.engine_version);
8758 void LoadTape(int nr)
8760 char *filename = getTapeFilename(nr);
8762 LoadTapeFromFilename(filename);
8765 void LoadSolutionTape(int nr)
8767 char *filename = getSolutionTapeFilename(nr);
8769 LoadTapeFromFilename(filename);
8771 if (TAPE_IS_EMPTY(tape))
8773 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8774 level.native_bd_level->replay != NULL)
8775 CopyNativeTape_BD_to_RND(&level);
8776 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8777 level.native_sp_level->demo.is_available)
8778 CopyNativeTape_SP_to_RND(&level);
8782 void LoadScoreTape(char *score_tape_basename, int nr)
8784 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8786 LoadTapeFromFilename(filename);
8789 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8791 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8793 LoadTapeFromFilename(filename);
8796 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8798 // chunk required for team mode tapes with non-default screen size
8799 return (tape->num_participating_players > 1 &&
8800 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8801 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8804 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8806 putFileVersion(file, tape->file_version);
8807 putFileVersion(file, tape->game_version);
8810 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8813 byte store_participating_players = 0;
8815 // set bits for participating players for compact storage
8816 for (i = 0; i < MAX_PLAYERS; i++)
8817 if (tape->player_participates[i])
8818 store_participating_players |= (1 << i);
8820 putFile32BitBE(file, tape->random_seed);
8821 putFile32BitBE(file, tape->date);
8822 putFile32BitBE(file, tape->length);
8824 putFile8Bit(file, store_participating_players);
8826 putFile8Bit(file, getTapeActionValue(tape));
8828 putFile8Bit(file, tape->property_bits);
8829 putFile8Bit(file, tape->solved);
8831 putFileVersion(file, tape->engine_version);
8834 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8836 putFile8Bit(file, tape->scr_fieldx);
8837 putFile8Bit(file, tape->scr_fieldy);
8840 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8842 int level_identifier_size = strlen(tape->level_identifier) + 1;
8845 putFile16BitBE(file, level_identifier_size);
8847 for (i = 0; i < level_identifier_size; i++)
8848 putFile8Bit(file, tape->level_identifier[i]);
8850 putFile16BitBE(file, tape->level_nr);
8853 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8857 for (i = 0; i < tape->length; i++)
8859 if (tape->use_key_actions)
8861 for (j = 0; j < MAX_PLAYERS; j++)
8862 if (tape->player_participates[j])
8863 putFile8Bit(file, tape->pos[i].action[j]);
8866 if (tape->use_mouse_actions)
8868 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8869 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8870 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8873 putFile8Bit(file, tape->pos[i].delay);
8877 void SaveTapeToFilename(char *filename)
8881 int info_chunk_size;
8882 int body_chunk_size;
8884 if (!(file = fopen(filename, MODE_WRITE)))
8886 Warn("cannot save level recording file '%s'", filename);
8891 tape_pos_size = getTapePosSize(&tape);
8893 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8894 body_chunk_size = tape_pos_size * tape.length;
8896 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8897 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8899 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8900 SaveTape_VERS(file, &tape);
8902 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8903 SaveTape_HEAD(file, &tape);
8905 if (checkSaveTape_SCRN(&tape))
8907 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8908 SaveTape_SCRN(file, &tape);
8911 putFileChunkBE(file, "INFO", info_chunk_size);
8912 SaveTape_INFO(file, &tape);
8914 putFileChunkBE(file, "BODY", body_chunk_size);
8915 SaveTape_BODY(file, &tape);
8919 SetFilePermissions(filename, PERMS_PRIVATE);
8922 static void SaveTapeExt(char *filename)
8926 tape.file_version = FILE_VERSION_ACTUAL;
8927 tape.game_version = GAME_VERSION_ACTUAL;
8929 tape.num_participating_players = 0;
8931 // count number of participating players
8932 for (i = 0; i < MAX_PLAYERS; i++)
8933 if (tape.player_participates[i])
8934 tape.num_participating_players++;
8936 SaveTapeToFilename(filename);
8938 tape.changed = FALSE;
8941 void SaveTape(int nr)
8943 char *filename = getTapeFilename(nr);
8945 InitTapeDirectory(leveldir_current->subdir);
8947 SaveTapeExt(filename);
8950 void SaveScoreTape(int nr)
8952 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8954 // used instead of "leveldir_current->subdir" (for network games)
8955 InitScoreTapeDirectory(levelset.identifier, nr);
8957 SaveTapeExt(filename);
8960 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8961 unsigned int req_state_added)
8963 char *filename = getTapeFilename(nr);
8964 boolean new_tape = !fileExists(filename);
8965 boolean tape_saved = FALSE;
8967 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8972 Request(msg_saved, REQ_CONFIRM | req_state_added);
8980 boolean SaveTapeChecked(int nr)
8982 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8985 boolean SaveTapeChecked_LevelSolved(int nr)
8987 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8988 "Level solved! Tape saved!", REQ_STAY_OPEN);
8991 void DumpTape(struct TapeInfo *tape)
8993 int tape_frame_counter;
8996 if (tape->no_valid_file)
8998 Warn("cannot dump -- no valid tape file found");
9005 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9006 tape->level_nr, tape->file_version, tape->game_version);
9007 Print(" (effective engine version %08d)\n",
9008 tape->engine_version);
9009 Print("Level series identifier: '%s'\n", tape->level_identifier);
9011 Print("Solution tape: %s\n",
9012 tape->solved ? "yes" :
9013 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9015 Print("Special tape properties: ");
9016 if (tape->property_bits == TAPE_PROPERTY_NONE)
9018 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9019 Print("[em_random_bug]");
9020 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9021 Print("[game_speed]");
9022 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9024 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9025 Print("[single_step]");
9026 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9027 Print("[snapshot]");
9028 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9029 Print("[replayed]");
9030 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9031 Print("[tas_keys]");
9032 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9033 Print("[small_graphics]");
9036 int year2 = tape->date / 10000;
9037 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9038 int month_index_raw = (tape->date / 100) % 100;
9039 int month_index = month_index_raw % 12; // prevent invalid index
9040 int month = month_index + 1;
9041 int day = tape->date % 100;
9043 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9047 tape_frame_counter = 0;
9049 for (i = 0; i < tape->length; i++)
9051 if (i >= MAX_TAPE_LEN)
9056 for (j = 0; j < MAX_PLAYERS; j++)
9058 if (tape->player_participates[j])
9060 int action = tape->pos[i].action[j];
9062 Print("%d:%02x ", j, action);
9063 Print("[%c%c%c%c|%c%c] - ",
9064 (action & JOY_LEFT ? '<' : ' '),
9065 (action & JOY_RIGHT ? '>' : ' '),
9066 (action & JOY_UP ? '^' : ' '),
9067 (action & JOY_DOWN ? 'v' : ' '),
9068 (action & JOY_BUTTON_1 ? '1' : ' '),
9069 (action & JOY_BUTTON_2 ? '2' : ' '));
9073 Print("(%03d) ", tape->pos[i].delay);
9074 Print("[%05d]\n", tape_frame_counter);
9076 tape_frame_counter += tape->pos[i].delay;
9082 void DumpTapes(void)
9084 static LevelDirTree *dumptape_leveldir = NULL;
9086 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9087 global.dumptape_leveldir);
9089 if (dumptape_leveldir == NULL)
9090 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9092 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9093 global.dumptape_level_nr > dumptape_leveldir->last_level)
9094 Fail("no such level number: %d", global.dumptape_level_nr);
9096 leveldir_current = dumptape_leveldir;
9098 if (options.mytapes)
9099 LoadTape(global.dumptape_level_nr);
9101 LoadSolutionTape(global.dumptape_level_nr);
9109 // ============================================================================
9110 // score file functions
9111 // ============================================================================
9113 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9117 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9119 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9120 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9121 scores->entry[i].score = 0;
9122 scores->entry[i].time = 0;
9124 scores->entry[i].id = -1;
9125 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9126 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9127 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9128 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9129 strcpy(scores->entry[i].country_code, "??");
9132 scores->num_entries = 0;
9133 scores->last_added = -1;
9134 scores->last_added_local = -1;
9136 scores->updated = FALSE;
9137 scores->uploaded = FALSE;
9138 scores->tape_downloaded = FALSE;
9139 scores->force_last_added = FALSE;
9141 // The following values are intentionally not reset here:
9145 // - continue_playing
9146 // - continue_on_return
9149 static void setScoreInfoToDefaults(void)
9151 setScoreInfoToDefaultsExt(&scores);
9154 static void setServerScoreInfoToDefaults(void)
9156 setScoreInfoToDefaultsExt(&server_scores);
9159 static void LoadScore_OLD(int nr)
9162 char *filename = getScoreFilename(nr);
9163 char cookie[MAX_LINE_LEN];
9164 char line[MAX_LINE_LEN];
9168 if (!(file = fopen(filename, MODE_READ)))
9171 // check file identifier
9172 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9174 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9175 cookie[strlen(cookie) - 1] = '\0';
9177 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9179 Warn("unknown format of score file '%s'", filename);
9186 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9188 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9189 Warn("fscanf() failed; %s", strerror(errno));
9191 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9194 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9195 line[strlen(line) - 1] = '\0';
9197 for (line_ptr = line; *line_ptr; line_ptr++)
9199 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9201 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9202 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9211 static void ConvertScore_OLD(void)
9213 // only convert score to time for levels that rate playing time over score
9214 if (!level.rate_time_over_score)
9217 // convert old score to playing time for score-less levels (like Supaplex)
9218 int time_final_max = 999;
9221 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9223 int score = scores.entry[i].score;
9225 if (score > 0 && score < time_final_max)
9226 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9230 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9232 scores->file_version = getFileVersion(file);
9233 scores->game_version = getFileVersion(file);
9238 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9240 char *level_identifier = NULL;
9241 int level_identifier_size;
9244 level_identifier_size = getFile16BitBE(file);
9246 level_identifier = checked_malloc(level_identifier_size);
9248 for (i = 0; i < level_identifier_size; i++)
9249 level_identifier[i] = getFile8Bit(file);
9251 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9252 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9254 checked_free(level_identifier);
9256 scores->level_nr = getFile16BitBE(file);
9257 scores->num_entries = getFile16BitBE(file);
9259 chunk_size = 2 + level_identifier_size + 2 + 2;
9264 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9268 for (i = 0; i < scores->num_entries; i++)
9270 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9271 scores->entry[i].name[j] = getFile8Bit(file);
9273 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9276 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9281 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9285 for (i = 0; i < scores->num_entries; i++)
9286 scores->entry[i].score = getFile16BitBE(file);
9288 chunk_size = scores->num_entries * 2;
9293 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9297 for (i = 0; i < scores->num_entries; i++)
9298 scores->entry[i].score = getFile32BitBE(file);
9300 chunk_size = scores->num_entries * 4;
9305 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9309 for (i = 0; i < scores->num_entries; i++)
9310 scores->entry[i].time = getFile32BitBE(file);
9312 chunk_size = scores->num_entries * 4;
9317 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9321 for (i = 0; i < scores->num_entries; i++)
9323 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9324 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9326 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9329 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9334 void LoadScore(int nr)
9336 char *filename = getScoreFilename(nr);
9337 char cookie[MAX_LINE_LEN];
9338 char chunk_name[CHUNK_ID_LEN + 1];
9340 boolean old_score_file_format = FALSE;
9343 // always start with reliable default values
9344 setScoreInfoToDefaults();
9346 if (!(file = openFile(filename, MODE_READ)))
9349 getFileChunkBE(file, chunk_name, NULL);
9350 if (strEqual(chunk_name, "RND1"))
9352 getFile32BitBE(file); // not used
9354 getFileChunkBE(file, chunk_name, NULL);
9355 if (!strEqual(chunk_name, "SCOR"))
9357 Warn("unknown format of score file '%s'", filename);
9364 else // check for old file format with cookie string
9366 strcpy(cookie, chunk_name);
9367 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9369 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9370 cookie[strlen(cookie) - 1] = '\0';
9372 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9374 Warn("unknown format of score file '%s'", filename);
9381 old_score_file_format = TRUE;
9384 if (old_score_file_format)
9386 // score files from versions before 4.2.4.0 without chunk structure
9389 // convert score to time, if possible (mainly for Supaplex levels)
9398 int (*loader)(File *, int, struct ScoreInfo *);
9402 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9403 { "INFO", -1, LoadScore_INFO },
9404 { "NAME", -1, LoadScore_NAME },
9405 { "SCOR", -1, LoadScore_SCOR },
9406 { "SC4R", -1, LoadScore_SC4R },
9407 { "TIME", -1, LoadScore_TIME },
9408 { "TAPE", -1, LoadScore_TAPE },
9413 while (getFileChunkBE(file, chunk_name, &chunk_size))
9417 while (chunk_info[i].name != NULL &&
9418 !strEqual(chunk_name, chunk_info[i].name))
9421 if (chunk_info[i].name == NULL)
9423 Warn("unknown chunk '%s' in score file '%s'",
9424 chunk_name, filename);
9426 ReadUnusedBytesFromFile(file, chunk_size);
9428 else if (chunk_info[i].size != -1 &&
9429 chunk_info[i].size != chunk_size)
9431 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9432 chunk_size, chunk_name, filename);
9434 ReadUnusedBytesFromFile(file, chunk_size);
9438 // call function to load this score chunk
9439 int chunk_size_expected =
9440 (chunk_info[i].loader)(file, chunk_size, &scores);
9442 // the size of some chunks cannot be checked before reading other
9443 // chunks first (like "HEAD" and "BODY") that contain some header
9444 // information, so check them here
9445 if (chunk_size_expected != chunk_size)
9447 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9448 chunk_size, chunk_name, filename);
9457 #if ENABLE_HISTORIC_CHUNKS
9458 void SaveScore_OLD(int nr)
9461 char *filename = getScoreFilename(nr);
9464 // used instead of "leveldir_current->subdir" (for network games)
9465 InitScoreDirectory(levelset.identifier);
9467 if (!(file = fopen(filename, MODE_WRITE)))
9469 Warn("cannot save score for level %d", nr);
9474 fprintf(file, "%s\n\n", SCORE_COOKIE);
9476 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9477 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9481 SetFilePermissions(filename, PERMS_PRIVATE);
9485 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9487 putFileVersion(file, scores->file_version);
9488 putFileVersion(file, scores->game_version);
9491 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9493 int level_identifier_size = strlen(scores->level_identifier) + 1;
9496 putFile16BitBE(file, level_identifier_size);
9498 for (i = 0; i < level_identifier_size; i++)
9499 putFile8Bit(file, scores->level_identifier[i]);
9501 putFile16BitBE(file, scores->level_nr);
9502 putFile16BitBE(file, scores->num_entries);
9505 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9509 for (i = 0; i < scores->num_entries; i++)
9511 int name_size = strlen(scores->entry[i].name);
9513 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9514 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9518 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9522 for (i = 0; i < scores->num_entries; i++)
9523 putFile16BitBE(file, scores->entry[i].score);
9526 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9530 for (i = 0; i < scores->num_entries; i++)
9531 putFile32BitBE(file, scores->entry[i].score);
9534 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9538 for (i = 0; i < scores->num_entries; i++)
9539 putFile32BitBE(file, scores->entry[i].time);
9542 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9546 for (i = 0; i < scores->num_entries; i++)
9548 int size = strlen(scores->entry[i].tape_basename);
9550 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9551 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9555 static void SaveScoreToFilename(char *filename)
9558 int info_chunk_size;
9559 int name_chunk_size;
9560 int scor_chunk_size;
9561 int sc4r_chunk_size;
9562 int time_chunk_size;
9563 int tape_chunk_size;
9564 boolean has_large_score_values;
9567 if (!(file = fopen(filename, MODE_WRITE)))
9569 Warn("cannot save score file '%s'", filename);
9574 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9575 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9576 scor_chunk_size = scores.num_entries * 2;
9577 sc4r_chunk_size = scores.num_entries * 4;
9578 time_chunk_size = scores.num_entries * 4;
9579 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9581 has_large_score_values = FALSE;
9582 for (i = 0; i < scores.num_entries; i++)
9583 if (scores.entry[i].score > 0xffff)
9584 has_large_score_values = TRUE;
9586 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9587 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9589 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9590 SaveScore_VERS(file, &scores);
9592 putFileChunkBE(file, "INFO", info_chunk_size);
9593 SaveScore_INFO(file, &scores);
9595 putFileChunkBE(file, "NAME", name_chunk_size);
9596 SaveScore_NAME(file, &scores);
9598 if (has_large_score_values)
9600 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9601 SaveScore_SC4R(file, &scores);
9605 putFileChunkBE(file, "SCOR", scor_chunk_size);
9606 SaveScore_SCOR(file, &scores);
9609 putFileChunkBE(file, "TIME", time_chunk_size);
9610 SaveScore_TIME(file, &scores);
9612 putFileChunkBE(file, "TAPE", tape_chunk_size);
9613 SaveScore_TAPE(file, &scores);
9617 SetFilePermissions(filename, PERMS_PRIVATE);
9620 void SaveScore(int nr)
9622 char *filename = getScoreFilename(nr);
9625 // used instead of "leveldir_current->subdir" (for network games)
9626 InitScoreDirectory(levelset.identifier);
9628 scores.file_version = FILE_VERSION_ACTUAL;
9629 scores.game_version = GAME_VERSION_ACTUAL;
9631 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9632 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9633 scores.level_nr = level_nr;
9635 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9636 if (scores.entry[i].score == 0 &&
9637 scores.entry[i].time == 0 &&
9638 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9641 scores.num_entries = i;
9643 if (scores.num_entries == 0)
9646 SaveScoreToFilename(filename);
9649 static void LoadServerScoreFromCache(int nr)
9651 struct ScoreEntry score_entry;
9660 { &score_entry.score, FALSE, 0 },
9661 { &score_entry.time, FALSE, 0 },
9662 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9663 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9664 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9665 { &score_entry.id, FALSE, 0 },
9666 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9667 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9668 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9669 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9673 char *filename = getScoreCacheFilename(nr);
9674 SetupFileHash *score_hash = loadSetupFileHash(filename);
9677 server_scores.num_entries = 0;
9679 if (score_hash == NULL)
9682 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9684 score_entry = server_scores.entry[i];
9686 for (j = 0; score_mapping[j].value != NULL; j++)
9690 sprintf(token, "%02d.%d", i, j);
9692 char *value = getHashEntry(score_hash, token);
9697 if (score_mapping[j].is_string)
9699 char *score_value = (char *)score_mapping[j].value;
9700 int value_size = score_mapping[j].string_size;
9702 strncpy(score_value, value, value_size);
9703 score_value[value_size] = '\0';
9707 int *score_value = (int *)score_mapping[j].value;
9709 *score_value = atoi(value);
9712 server_scores.num_entries = i + 1;
9715 server_scores.entry[i] = score_entry;
9718 freeSetupFileHash(score_hash);
9721 void LoadServerScore(int nr, boolean download_score)
9723 if (!setup.use_api_server)
9726 // always start with reliable default values
9727 setServerScoreInfoToDefaults();
9729 // 1st step: load server scores from cache file (which may not exist)
9730 // (this should prevent reading it while the thread is writing to it)
9731 LoadServerScoreFromCache(nr);
9733 if (download_score && runtime.use_api_server)
9735 // 2nd step: download server scores from score server to cache file
9736 // (as thread, as it might time out if the server is not reachable)
9737 ApiGetScoreAsThread(nr);
9741 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9743 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9745 // if score tape not uploaded, ask for uploading missing tapes later
9746 if (!setup.has_remaining_tapes)
9747 setup.ask_for_remaining_tapes = TRUE;
9749 setup.provide_uploading_tapes = TRUE;
9750 setup.has_remaining_tapes = TRUE;
9752 SaveSetup_ServerSetup();
9755 void SaveServerScore(int nr, boolean tape_saved)
9757 if (!runtime.use_api_server)
9759 PrepareScoreTapesForUpload(leveldir_current->subdir);
9764 ApiAddScoreAsThread(nr, tape_saved, NULL);
9767 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9768 char *score_tape_filename)
9770 if (!runtime.use_api_server)
9773 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9776 void LoadLocalAndServerScore(int nr, boolean download_score)
9778 int last_added_local = scores.last_added_local;
9779 boolean force_last_added = scores.force_last_added;
9781 // needed if only showing server scores
9782 setScoreInfoToDefaults();
9784 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9787 // restore last added local score entry (before merging server scores)
9788 scores.last_added = scores.last_added_local = last_added_local;
9790 if (setup.use_api_server &&
9791 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9793 // load server scores from cache file and trigger update from server
9794 LoadServerScore(nr, download_score);
9796 // merge local scores with scores from server
9800 if (force_last_added)
9801 scores.force_last_added = force_last_added;
9805 // ============================================================================
9806 // setup file functions
9807 // ============================================================================
9809 #define TOKEN_STR_PLAYER_PREFIX "player_"
9812 static struct TokenInfo global_setup_tokens[] =
9816 &setup.player_name, "player_name"
9820 &setup.multiple_users, "multiple_users"
9824 &setup.sound, "sound"
9828 &setup.sound_loops, "repeating_sound_loops"
9832 &setup.sound_music, "background_music"
9836 &setup.sound_simple, "simple_sound_effects"
9840 &setup.toons, "toons"
9844 &setup.global_animations, "global_animations"
9848 &setup.scroll_delay, "scroll_delay"
9852 &setup.forced_scroll_delay, "forced_scroll_delay"
9856 &setup.scroll_delay_value, "scroll_delay_value"
9860 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9864 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9868 &setup.fade_screens, "fade_screens"
9872 &setup.autorecord, "automatic_tape_recording"
9876 &setup.autorecord_after_replay, "autorecord_after_replay"
9880 &setup.auto_pause_on_start, "auto_pause_on_start"
9884 &setup.show_titlescreen, "show_titlescreen"
9888 &setup.quick_doors, "quick_doors"
9892 &setup.team_mode, "team_mode"
9896 &setup.handicap, "handicap"
9900 &setup.skip_levels, "skip_levels"
9904 &setup.increment_levels, "increment_levels"
9908 &setup.auto_play_next_level, "auto_play_next_level"
9912 &setup.count_score_after_game, "count_score_after_game"
9916 &setup.show_scores_after_game, "show_scores_after_game"
9920 &setup.time_limit, "time_limit"
9924 &setup.fullscreen, "fullscreen"
9928 &setup.window_scaling_percent, "window_scaling_percent"
9932 &setup.window_scaling_quality, "window_scaling_quality"
9936 &setup.screen_rendering_mode, "screen_rendering_mode"
9940 &setup.vsync_mode, "vsync_mode"
9944 &setup.ask_on_escape, "ask_on_escape"
9948 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9952 &setup.ask_on_game_over, "ask_on_game_over"
9956 &setup.ask_on_quit_game, "ask_on_quit_game"
9960 &setup.ask_on_quit_program, "ask_on_quit_program"
9964 &setup.quick_switch, "quick_player_switch"
9968 &setup.input_on_focus, "input_on_focus"
9972 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9976 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9980 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9984 &setup.game_speed_extended, "game_speed_extended"
9988 &setup.game_frame_delay, "game_frame_delay"
9992 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9996 &setup.bd_skip_hatching, "bd_skip_hatching"
10000 &setup.bd_scroll_delay, "bd_scroll_delay"
10004 &setup.bd_smooth_movements, "bd_smooth_movements"
10008 &setup.sp_show_border_elements, "sp_show_border_elements"
10012 &setup.small_game_graphics, "small_game_graphics"
10016 &setup.show_load_save_buttons, "show_load_save_buttons"
10020 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10024 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10028 &setup.graphics_set, "graphics_set"
10032 &setup.sounds_set, "sounds_set"
10036 &setup.music_set, "music_set"
10040 &setup.override_level_graphics, "override_level_graphics"
10044 &setup.override_level_sounds, "override_level_sounds"
10048 &setup.override_level_music, "override_level_music"
10052 &setup.volume_simple, "volume_simple"
10056 &setup.volume_loops, "volume_loops"
10060 &setup.volume_music, "volume_music"
10064 &setup.network_mode, "network_mode"
10068 &setup.network_player_nr, "network_player"
10072 &setup.network_server_hostname, "network_server_hostname"
10076 &setup.touch.control_type, "touch.control_type"
10080 &setup.touch.move_distance, "touch.move_distance"
10084 &setup.touch.drop_distance, "touch.drop_distance"
10088 &setup.touch.transparency, "touch.transparency"
10092 &setup.touch.draw_outlined, "touch.draw_outlined"
10096 &setup.touch.draw_pressed, "touch.draw_pressed"
10100 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10104 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10108 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10112 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10116 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10120 static struct TokenInfo auto_setup_tokens[] =
10124 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10128 static struct TokenInfo server_setup_tokens[] =
10132 &setup.player_uuid, "player_uuid"
10136 &setup.player_version, "player_version"
10140 &setup.use_api_server, TEST_PREFIX "use_api_server"
10144 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10148 &setup.api_server_password, TEST_PREFIX "api_server_password"
10152 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10156 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10160 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10164 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10168 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10172 static struct TokenInfo editor_setup_tokens[] =
10176 &setup.editor.el_classic, "editor.el_classic"
10180 &setup.editor.el_custom, "editor.el_custom"
10184 &setup.editor.el_user_defined, "editor.el_user_defined"
10188 &setup.editor.el_dynamic, "editor.el_dynamic"
10192 &setup.editor.el_headlines, "editor.el_headlines"
10196 &setup.editor.show_element_token, "editor.show_element_token"
10200 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10204 static struct TokenInfo editor_cascade_setup_tokens[] =
10208 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10212 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10216 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10220 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10224 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10228 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10232 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10236 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10240 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10244 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10248 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10252 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10256 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10260 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10264 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10268 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10272 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10276 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10280 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10284 static struct TokenInfo shortcut_setup_tokens[] =
10288 &setup.shortcut.save_game, "shortcut.save_game"
10292 &setup.shortcut.load_game, "shortcut.load_game"
10296 &setup.shortcut.restart_game, "shortcut.restart_game"
10300 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10304 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10308 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10312 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10316 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10320 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10324 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10328 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10332 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10336 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10340 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10344 &setup.shortcut.tape_record, "shortcut.tape_record"
10348 &setup.shortcut.tape_play, "shortcut.tape_play"
10352 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10356 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10360 &setup.shortcut.sound_music, "shortcut.sound_music"
10364 &setup.shortcut.snap_left, "shortcut.snap_left"
10368 &setup.shortcut.snap_right, "shortcut.snap_right"
10372 &setup.shortcut.snap_up, "shortcut.snap_up"
10376 &setup.shortcut.snap_down, "shortcut.snap_down"
10380 static struct SetupInputInfo setup_input;
10381 static struct TokenInfo player_setup_tokens[] =
10385 &setup_input.use_joystick, ".use_joystick"
10389 &setup_input.joy.device_name, ".joy.device_name"
10393 &setup_input.joy.xleft, ".joy.xleft"
10397 &setup_input.joy.xmiddle, ".joy.xmiddle"
10401 &setup_input.joy.xright, ".joy.xright"
10405 &setup_input.joy.yupper, ".joy.yupper"
10409 &setup_input.joy.ymiddle, ".joy.ymiddle"
10413 &setup_input.joy.ylower, ".joy.ylower"
10417 &setup_input.joy.snap, ".joy.snap_field"
10421 &setup_input.joy.drop, ".joy.place_bomb"
10425 &setup_input.key.left, ".key.move_left"
10429 &setup_input.key.right, ".key.move_right"
10433 &setup_input.key.up, ".key.move_up"
10437 &setup_input.key.down, ".key.move_down"
10441 &setup_input.key.snap, ".key.snap_field"
10445 &setup_input.key.drop, ".key.place_bomb"
10449 static struct TokenInfo system_setup_tokens[] =
10453 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10457 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10461 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10465 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10469 static struct TokenInfo internal_setup_tokens[] =
10473 &setup.internal.program_title, "program_title"
10477 &setup.internal.program_version, "program_version"
10481 &setup.internal.program_author, "program_author"
10485 &setup.internal.program_email, "program_email"
10489 &setup.internal.program_website, "program_website"
10493 &setup.internal.program_copyright, "program_copyright"
10497 &setup.internal.program_company, "program_company"
10501 &setup.internal.program_icon_file, "program_icon_file"
10505 &setup.internal.default_graphics_set, "default_graphics_set"
10509 &setup.internal.default_sounds_set, "default_sounds_set"
10513 &setup.internal.default_music_set, "default_music_set"
10517 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10521 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10525 &setup.internal.fallback_music_file, "fallback_music_file"
10529 &setup.internal.default_level_series, "default_level_series"
10533 &setup.internal.default_window_width, "default_window_width"
10537 &setup.internal.default_window_height, "default_window_height"
10541 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10545 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10549 &setup.internal.create_user_levelset, "create_user_levelset"
10553 &setup.internal.info_screens_from_main, "info_screens_from_main"
10557 &setup.internal.menu_game, "menu_game"
10561 &setup.internal.menu_engines, "menu_engines"
10565 &setup.internal.menu_editor, "menu_editor"
10569 &setup.internal.menu_graphics, "menu_graphics"
10573 &setup.internal.menu_sound, "menu_sound"
10577 &setup.internal.menu_artwork, "menu_artwork"
10581 &setup.internal.menu_input, "menu_input"
10585 &setup.internal.menu_touch, "menu_touch"
10589 &setup.internal.menu_shortcuts, "menu_shortcuts"
10593 &setup.internal.menu_exit, "menu_exit"
10597 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10601 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10605 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10609 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10613 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10617 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10621 &setup.internal.info_title, "info_title"
10625 &setup.internal.info_elements, "info_elements"
10629 &setup.internal.info_music, "info_music"
10633 &setup.internal.info_credits, "info_credits"
10637 &setup.internal.info_program, "info_program"
10641 &setup.internal.info_version, "info_version"
10645 &setup.internal.info_levelset, "info_levelset"
10649 &setup.internal.info_exit, "info_exit"
10653 static struct TokenInfo debug_setup_tokens[] =
10657 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10661 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10665 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10669 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10673 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10677 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10681 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10685 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10689 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10693 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10697 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10701 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10705 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10709 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10713 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10717 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10721 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10725 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10729 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10733 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10737 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10740 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10744 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10748 &setup.debug.xsn_mode, "debug.xsn_mode"
10752 &setup.debug.xsn_percent, "debug.xsn_percent"
10756 static struct TokenInfo options_setup_tokens[] =
10760 &setup.options.verbose, "options.verbose"
10764 &setup.options.debug, "options.debug"
10768 &setup.options.debug_mode, "options.debug_mode"
10772 static void setSetupInfoToDefaults(struct SetupInfo *si)
10776 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10778 si->multiple_users = TRUE;
10781 si->sound_loops = TRUE;
10782 si->sound_music = TRUE;
10783 si->sound_simple = TRUE;
10785 si->global_animations = TRUE;
10786 si->scroll_delay = TRUE;
10787 si->forced_scroll_delay = FALSE;
10788 si->scroll_delay_value = STD_SCROLL_DELAY;
10789 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10790 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10791 si->fade_screens = TRUE;
10792 si->autorecord = TRUE;
10793 si->autorecord_after_replay = TRUE;
10794 si->auto_pause_on_start = FALSE;
10795 si->show_titlescreen = TRUE;
10796 si->quick_doors = FALSE;
10797 si->team_mode = FALSE;
10798 si->handicap = TRUE;
10799 si->skip_levels = TRUE;
10800 si->increment_levels = TRUE;
10801 si->auto_play_next_level = TRUE;
10802 si->count_score_after_game = TRUE;
10803 si->show_scores_after_game = TRUE;
10804 si->time_limit = TRUE;
10805 si->fullscreen = FALSE;
10806 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10807 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10808 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10809 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10810 si->ask_on_escape = TRUE;
10811 si->ask_on_escape_editor = TRUE;
10812 si->ask_on_game_over = TRUE;
10813 si->ask_on_quit_game = TRUE;
10814 si->ask_on_quit_program = TRUE;
10815 si->quick_switch = FALSE;
10816 si->input_on_focus = FALSE;
10817 si->prefer_aga_graphics = TRUE;
10818 si->prefer_lowpass_sounds = FALSE;
10819 si->prefer_extra_panel_items = TRUE;
10820 si->game_speed_extended = FALSE;
10821 si->game_frame_delay = GAME_FRAME_DELAY;
10822 si->bd_skip_uncovering = FALSE;
10823 si->bd_skip_hatching = FALSE;
10824 si->bd_scroll_delay = TRUE;
10825 si->bd_smooth_movements = AUTO;
10826 si->sp_show_border_elements = FALSE;
10827 si->small_game_graphics = FALSE;
10828 si->show_load_save_buttons = FALSE;
10829 si->show_undo_redo_buttons = FALSE;
10830 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10832 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10833 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10834 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10836 si->override_level_graphics = FALSE;
10837 si->override_level_sounds = FALSE;
10838 si->override_level_music = FALSE;
10840 si->volume_simple = 100; // percent
10841 si->volume_loops = 100; // percent
10842 si->volume_music = 100; // percent
10844 si->network_mode = FALSE;
10845 si->network_player_nr = 0; // first player
10846 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10848 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10849 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10850 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10851 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10852 si->touch.draw_outlined = TRUE;
10853 si->touch.draw_pressed = TRUE;
10855 for (i = 0; i < 2; i++)
10857 char *default_grid_button[6][2] =
10863 { "111222", " vv " },
10864 { "111222", " vv " }
10866 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10867 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10868 int min_xsize = MIN(6, grid_xsize);
10869 int min_ysize = MIN(6, grid_ysize);
10870 int startx = grid_xsize - min_xsize;
10871 int starty = grid_ysize - min_ysize;
10874 // virtual buttons grid can only be set to defaults if video is initialized
10875 // (this will be repeated if virtual buttons are not loaded from setup file)
10876 if (video.initialized)
10878 si->touch.grid_xsize[i] = grid_xsize;
10879 si->touch.grid_ysize[i] = grid_ysize;
10883 si->touch.grid_xsize[i] = -1;
10884 si->touch.grid_ysize[i] = -1;
10887 for (x = 0; x < MAX_GRID_XSIZE; x++)
10888 for (y = 0; y < MAX_GRID_YSIZE; y++)
10889 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10891 for (x = 0; x < min_xsize; x++)
10892 for (y = 0; y < min_ysize; y++)
10893 si->touch.grid_button[i][x][starty + y] =
10894 default_grid_button[y][0][x];
10896 for (x = 0; x < min_xsize; x++)
10897 for (y = 0; y < min_ysize; y++)
10898 si->touch.grid_button[i][startx + x][starty + y] =
10899 default_grid_button[y][1][x];
10902 si->touch.grid_initialized = video.initialized;
10904 si->touch.overlay_buttons = FALSE;
10906 si->editor.el_boulderdash = TRUE;
10907 si->editor.el_boulderdash_native = TRUE;
10908 si->editor.el_emerald_mine = TRUE;
10909 si->editor.el_emerald_mine_club = TRUE;
10910 si->editor.el_more = TRUE;
10911 si->editor.el_sokoban = TRUE;
10912 si->editor.el_supaplex = TRUE;
10913 si->editor.el_diamond_caves = TRUE;
10914 si->editor.el_dx_boulderdash = TRUE;
10916 si->editor.el_mirror_magic = TRUE;
10917 si->editor.el_deflektor = TRUE;
10919 si->editor.el_chars = TRUE;
10920 si->editor.el_steel_chars = TRUE;
10922 si->editor.el_classic = TRUE;
10923 si->editor.el_custom = TRUE;
10925 si->editor.el_user_defined = FALSE;
10926 si->editor.el_dynamic = TRUE;
10928 si->editor.el_headlines = TRUE;
10930 si->editor.show_element_token = FALSE;
10932 si->editor.show_read_only_warning = TRUE;
10934 si->editor.use_template_for_new_levels = TRUE;
10936 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10937 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10938 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10939 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10940 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10942 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10943 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10944 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10945 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10946 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10948 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10949 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10950 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10951 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10952 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10953 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10955 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10956 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10957 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10959 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10960 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10961 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10962 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10964 for (i = 0; i < MAX_PLAYERS; i++)
10966 si->input[i].use_joystick = FALSE;
10967 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10968 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10969 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10970 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10971 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10972 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10973 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10974 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10975 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10976 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10977 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10978 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10979 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10980 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10981 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10984 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10985 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10986 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10987 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10989 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10990 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10991 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10992 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10993 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10994 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10995 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10997 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10999 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11000 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11001 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11003 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11004 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11005 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11007 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11008 si->internal.choose_from_top_leveldir = FALSE;
11009 si->internal.show_scaling_in_title = TRUE;
11010 si->internal.create_user_levelset = TRUE;
11011 si->internal.info_screens_from_main = FALSE;
11013 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11014 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11016 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11017 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11018 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11019 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11020 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11021 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11022 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11023 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11024 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11025 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11027 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11028 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11029 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11030 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11031 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11032 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11033 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11034 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11035 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11036 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11038 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11039 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11041 si->debug.show_frames_per_second = FALSE;
11043 si->debug.xsn_mode = AUTO;
11044 si->debug.xsn_percent = 0;
11046 si->options.verbose = FALSE;
11047 si->options.debug = FALSE;
11048 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11050 #if defined(PLATFORM_ANDROID)
11051 si->fullscreen = TRUE;
11052 si->touch.overlay_buttons = TRUE;
11055 setHideSetupEntry(&setup.debug.xsn_mode);
11058 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11060 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11063 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11065 si->player_uuid = NULL; // (will be set later)
11066 si->player_version = 1; // (will be set later)
11068 si->use_api_server = TRUE;
11069 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11070 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11071 si->ask_for_uploading_tapes = TRUE;
11072 si->ask_for_remaining_tapes = FALSE;
11073 si->provide_uploading_tapes = TRUE;
11074 si->ask_for_using_api_server = TRUE;
11075 si->has_remaining_tapes = FALSE;
11078 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11080 si->editor_cascade.el_bd = TRUE;
11081 si->editor_cascade.el_bd_native = TRUE;
11082 si->editor_cascade.el_em = TRUE;
11083 si->editor_cascade.el_emc = TRUE;
11084 si->editor_cascade.el_rnd = TRUE;
11085 si->editor_cascade.el_sb = TRUE;
11086 si->editor_cascade.el_sp = TRUE;
11087 si->editor_cascade.el_dc = TRUE;
11088 si->editor_cascade.el_dx = TRUE;
11090 si->editor_cascade.el_mm = TRUE;
11091 si->editor_cascade.el_df = TRUE;
11093 si->editor_cascade.el_chars = FALSE;
11094 si->editor_cascade.el_steel_chars = FALSE;
11095 si->editor_cascade.el_ce = FALSE;
11096 si->editor_cascade.el_ge = FALSE;
11097 si->editor_cascade.el_es = FALSE;
11098 si->editor_cascade.el_ref = FALSE;
11099 si->editor_cascade.el_user = FALSE;
11100 si->editor_cascade.el_dynamic = FALSE;
11103 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11105 static char *getHideSetupToken(void *setup_value)
11107 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11109 if (setup_value != NULL)
11110 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11112 return hide_setup_token;
11115 void setHideSetupEntry(void *setup_value)
11117 char *hide_setup_token = getHideSetupToken(setup_value);
11119 if (hide_setup_hash == NULL)
11120 hide_setup_hash = newSetupFileHash();
11122 if (setup_value != NULL)
11123 setHashEntry(hide_setup_hash, hide_setup_token, "");
11126 void removeHideSetupEntry(void *setup_value)
11128 char *hide_setup_token = getHideSetupToken(setup_value);
11130 if (setup_value != NULL)
11131 removeHashEntry(hide_setup_hash, hide_setup_token);
11134 boolean hideSetupEntry(void *setup_value)
11136 char *hide_setup_token = getHideSetupToken(setup_value);
11138 return (setup_value != NULL &&
11139 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11142 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11143 struct TokenInfo *token_info,
11144 int token_nr, char *token_text)
11146 char *token_hide_text = getStringCat2(token_text, ".hide");
11147 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11149 // set the value of this setup option in the setup option structure
11150 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11152 // check if this setup option should be hidden in the setup menu
11153 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11154 setHideSetupEntry(token_info[token_nr].value);
11156 free(token_hide_text);
11159 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11160 struct TokenInfo *token_info,
11163 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11164 token_info[token_nr].text);
11167 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11171 if (!setup_file_hash)
11174 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11175 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11177 setup.touch.grid_initialized = TRUE;
11178 for (i = 0; i < 2; i++)
11180 int grid_xsize = setup.touch.grid_xsize[i];
11181 int grid_ysize = setup.touch.grid_ysize[i];
11184 // if virtual buttons are not loaded from setup file, repeat initializing
11185 // virtual buttons grid with default values later when video is initialized
11186 if (grid_xsize == -1 ||
11189 setup.touch.grid_initialized = FALSE;
11194 for (y = 0; y < grid_ysize; y++)
11196 char token_string[MAX_LINE_LEN];
11198 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11200 char *value_string = getHashEntry(setup_file_hash, token_string);
11202 if (value_string == NULL)
11205 for (x = 0; x < grid_xsize; x++)
11207 char c = value_string[x];
11209 setup.touch.grid_button[i][x][y] =
11210 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11215 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11216 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11218 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11219 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11221 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11225 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11227 setup_input = setup.input[pnr];
11228 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11230 char full_token[100];
11232 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11233 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11236 setup.input[pnr] = setup_input;
11239 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11240 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11242 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11243 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11245 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11246 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11248 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11249 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11251 setHideRelatedSetupEntries();
11254 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11258 if (!setup_file_hash)
11261 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11262 setSetupInfo(auto_setup_tokens, i,
11263 getHashEntry(setup_file_hash,
11264 auto_setup_tokens[i].text));
11267 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11271 if (!setup_file_hash)
11274 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11275 setSetupInfo(server_setup_tokens, i,
11276 getHashEntry(setup_file_hash,
11277 server_setup_tokens[i].text));
11280 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11284 if (!setup_file_hash)
11287 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11288 setSetupInfo(editor_cascade_setup_tokens, i,
11289 getHashEntry(setup_file_hash,
11290 editor_cascade_setup_tokens[i].text));
11293 void LoadUserNames(void)
11295 int last_user_nr = user.nr;
11298 if (global.user_names != NULL)
11300 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11301 checked_free(global.user_names[i]);
11303 checked_free(global.user_names);
11306 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11308 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11312 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11314 if (setup_file_hash)
11316 char *player_name = getHashEntry(setup_file_hash, "player_name");
11318 global.user_names[i] = getFixedUserName(player_name);
11320 freeSetupFileHash(setup_file_hash);
11323 if (global.user_names[i] == NULL)
11324 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11327 user.nr = last_user_nr;
11330 void LoadSetupFromFilename(char *filename)
11332 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11334 if (setup_file_hash)
11336 decodeSetupFileHash_Default(setup_file_hash);
11338 freeSetupFileHash(setup_file_hash);
11342 Debug("setup", "using default setup values");
11346 static void LoadSetup_SpecialPostProcessing(void)
11348 char *player_name_new;
11350 // needed to work around problems with fixed length strings
11351 player_name_new = getFixedUserName(setup.player_name);
11352 free(setup.player_name);
11353 setup.player_name = player_name_new;
11355 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11356 if (setup.scroll_delay == FALSE)
11358 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11359 setup.scroll_delay = TRUE; // now always "on"
11362 // make sure that scroll delay value stays inside valid range
11363 setup.scroll_delay_value =
11364 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11367 void LoadSetup_Default(void)
11371 // always start with reliable default values
11372 setSetupInfoToDefaults(&setup);
11374 // try to load setup values from default setup file
11375 filename = getDefaultSetupFilename();
11377 if (fileExists(filename))
11378 LoadSetupFromFilename(filename);
11380 // try to load setup values from platform setup file
11381 filename = getPlatformSetupFilename();
11383 if (fileExists(filename))
11384 LoadSetupFromFilename(filename);
11386 // try to load setup values from user setup file
11387 filename = getSetupFilename();
11389 LoadSetupFromFilename(filename);
11391 LoadSetup_SpecialPostProcessing();
11394 void LoadSetup_AutoSetup(void)
11396 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11397 SetupFileHash *setup_file_hash = NULL;
11399 // always start with reliable default values
11400 setSetupInfoToDefaults_AutoSetup(&setup);
11402 setup_file_hash = loadSetupFileHash(filename);
11404 if (setup_file_hash)
11406 decodeSetupFileHash_AutoSetup(setup_file_hash);
11408 freeSetupFileHash(setup_file_hash);
11414 void LoadSetup_ServerSetup(void)
11416 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11417 SetupFileHash *setup_file_hash = NULL;
11419 // always start with reliable default values
11420 setSetupInfoToDefaults_ServerSetup(&setup);
11422 setup_file_hash = loadSetupFileHash(filename);
11424 if (setup_file_hash)
11426 decodeSetupFileHash_ServerSetup(setup_file_hash);
11428 freeSetupFileHash(setup_file_hash);
11433 if (setup.player_uuid == NULL)
11435 // player UUID does not yet exist in setup file
11436 setup.player_uuid = getStringCopy(getUUID());
11437 setup.player_version = 2;
11439 SaveSetup_ServerSetup();
11443 void LoadSetup_EditorCascade(void)
11445 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11446 SetupFileHash *setup_file_hash = NULL;
11448 // always start with reliable default values
11449 setSetupInfoToDefaults_EditorCascade(&setup);
11451 setup_file_hash = loadSetupFileHash(filename);
11453 if (setup_file_hash)
11455 decodeSetupFileHash_EditorCascade(setup_file_hash);
11457 freeSetupFileHash(setup_file_hash);
11463 void LoadSetup(void)
11465 LoadSetup_Default();
11466 LoadSetup_AutoSetup();
11467 LoadSetup_ServerSetup();
11468 LoadSetup_EditorCascade();
11471 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11472 char *mapping_line)
11474 char mapping_guid[MAX_LINE_LEN];
11475 char *mapping_start, *mapping_end;
11477 // get GUID from game controller mapping line: copy complete line
11478 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11479 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11481 // get GUID from game controller mapping line: cut after GUID part
11482 mapping_start = strchr(mapping_guid, ',');
11483 if (mapping_start != NULL)
11484 *mapping_start = '\0';
11486 // cut newline from game controller mapping line
11487 mapping_end = strchr(mapping_line, '\n');
11488 if (mapping_end != NULL)
11489 *mapping_end = '\0';
11491 // add mapping entry to game controller mappings hash
11492 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11495 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11500 if (!(file = fopen(filename, MODE_READ)))
11502 Warn("cannot read game controller mappings file '%s'", filename);
11507 while (!feof(file))
11509 char line[MAX_LINE_LEN];
11511 if (!fgets(line, MAX_LINE_LEN, file))
11514 addGameControllerMappingToHash(mappings_hash, line);
11520 void SaveSetup_Default(void)
11522 char *filename = getSetupFilename();
11526 InitUserDataDirectory();
11528 if (!(file = fopen(filename, MODE_WRITE)))
11530 Warn("cannot write setup file '%s'", filename);
11535 fprintFileHeader(file, SETUP_FILENAME);
11537 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11539 // just to make things nicer :)
11540 if (global_setup_tokens[i].value == &setup.multiple_users ||
11541 global_setup_tokens[i].value == &setup.sound ||
11542 global_setup_tokens[i].value == &setup.graphics_set ||
11543 global_setup_tokens[i].value == &setup.volume_simple ||
11544 global_setup_tokens[i].value == &setup.network_mode ||
11545 global_setup_tokens[i].value == &setup.touch.control_type ||
11546 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11547 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11548 fprintf(file, "\n");
11550 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11553 for (i = 0; i < 2; i++)
11555 int grid_xsize = setup.touch.grid_xsize[i];
11556 int grid_ysize = setup.touch.grid_ysize[i];
11559 fprintf(file, "\n");
11561 for (y = 0; y < grid_ysize; y++)
11563 char token_string[MAX_LINE_LEN];
11564 char value_string[MAX_LINE_LEN];
11566 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11568 for (x = 0; x < grid_xsize; x++)
11570 char c = setup.touch.grid_button[i][x][y];
11572 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11575 value_string[grid_xsize] = '\0';
11577 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11581 fprintf(file, "\n");
11582 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11583 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11585 fprintf(file, "\n");
11586 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11587 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11589 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11593 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11594 fprintf(file, "\n");
11596 setup_input = setup.input[pnr];
11597 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11598 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11601 fprintf(file, "\n");
11602 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11603 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11605 // (internal setup values not saved to user setup file)
11607 fprintf(file, "\n");
11608 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11609 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11610 setup.debug.xsn_mode != AUTO)
11611 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11613 fprintf(file, "\n");
11614 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11615 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11619 SetFilePermissions(filename, PERMS_PRIVATE);
11622 void SaveSetup_AutoSetup(void)
11624 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11628 InitUserDataDirectory();
11630 if (!(file = fopen(filename, MODE_WRITE)))
11632 Warn("cannot write auto setup file '%s'", filename);
11639 fprintFileHeader(file, AUTOSETUP_FILENAME);
11641 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11642 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11646 SetFilePermissions(filename, PERMS_PRIVATE);
11651 void SaveSetup_ServerSetup(void)
11653 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11657 InitUserDataDirectory();
11659 if (!(file = fopen(filename, MODE_WRITE)))
11661 Warn("cannot write server setup file '%s'", filename);
11668 fprintFileHeader(file, SERVERSETUP_FILENAME);
11670 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11672 // just to make things nicer :)
11673 if (server_setup_tokens[i].value == &setup.use_api_server)
11674 fprintf(file, "\n");
11676 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11681 SetFilePermissions(filename, PERMS_PRIVATE);
11686 void SaveSetup_EditorCascade(void)
11688 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11692 InitUserDataDirectory();
11694 if (!(file = fopen(filename, MODE_WRITE)))
11696 Warn("cannot write editor cascade state file '%s'", filename);
11703 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11705 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11706 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11710 SetFilePermissions(filename, PERMS_PRIVATE);
11715 void SaveSetup(void)
11717 SaveSetup_Default();
11718 SaveSetup_AutoSetup();
11719 SaveSetup_ServerSetup();
11720 SaveSetup_EditorCascade();
11723 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11728 if (!(file = fopen(filename, MODE_WRITE)))
11730 Warn("cannot write game controller mappings file '%s'", filename);
11735 BEGIN_HASH_ITERATION(mappings_hash, itr)
11737 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11739 END_HASH_ITERATION(mappings_hash, itr)
11744 void SaveSetup_AddGameControllerMapping(char *mapping)
11746 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11747 SetupFileHash *mappings_hash = newSetupFileHash();
11749 InitUserDataDirectory();
11751 // load existing personal game controller mappings
11752 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11754 // add new mapping to personal game controller mappings
11755 addGameControllerMappingToHash(mappings_hash, mapping);
11757 // save updated personal game controller mappings
11758 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11760 freeSetupFileHash(mappings_hash);
11764 void LoadCustomElementDescriptions(void)
11766 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11767 SetupFileHash *setup_file_hash;
11770 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11772 if (element_info[i].custom_description != NULL)
11774 free(element_info[i].custom_description);
11775 element_info[i].custom_description = NULL;
11779 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11782 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11784 char *token = getStringCat2(element_info[i].token_name, ".name");
11785 char *value = getHashEntry(setup_file_hash, token);
11788 element_info[i].custom_description = getStringCopy(value);
11793 freeSetupFileHash(setup_file_hash);
11796 static int getElementFromToken(char *token)
11798 char *value = getHashEntry(element_token_hash, token);
11801 return atoi(value);
11803 Warn("unknown element token '%s'", token);
11805 return EL_UNDEFINED;
11808 void FreeGlobalAnimEventInfo(void)
11810 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11812 if (gaei->event_list == NULL)
11817 for (i = 0; i < gaei->num_event_lists; i++)
11819 checked_free(gaei->event_list[i]->event_value);
11820 checked_free(gaei->event_list[i]);
11823 checked_free(gaei->event_list);
11825 gaei->event_list = NULL;
11826 gaei->num_event_lists = 0;
11829 static int AddGlobalAnimEventList(void)
11831 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11832 int list_pos = gaei->num_event_lists++;
11834 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11835 sizeof(struct GlobalAnimEventListInfo *));
11837 gaei->event_list[list_pos] =
11838 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11840 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11842 gaeli->event_value = NULL;
11843 gaeli->num_event_values = 0;
11848 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11850 // do not add empty global animation events
11851 if (event_value == ANIM_EVENT_NONE)
11854 // if list position is undefined, create new list
11855 if (list_pos == ANIM_EVENT_UNDEFINED)
11856 list_pos = AddGlobalAnimEventList();
11858 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11859 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11860 int value_pos = gaeli->num_event_values++;
11862 gaeli->event_value = checked_realloc(gaeli->event_value,
11863 gaeli->num_event_values * sizeof(int *));
11865 gaeli->event_value[value_pos] = event_value;
11870 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11872 if (list_pos == ANIM_EVENT_UNDEFINED)
11873 return ANIM_EVENT_NONE;
11875 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11876 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11878 return gaeli->event_value[value_pos];
11881 int GetGlobalAnimEventValueCount(int list_pos)
11883 if (list_pos == ANIM_EVENT_UNDEFINED)
11886 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11887 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11889 return gaeli->num_event_values;
11892 // This function checks if a string <s> of the format "string1, string2, ..."
11893 // exactly contains a string <s_contained>.
11895 static boolean string_has_parameter(char *s, char *s_contained)
11899 if (s == NULL || s_contained == NULL)
11902 if (strlen(s_contained) > strlen(s))
11905 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11907 char next_char = s[strlen(s_contained)];
11909 // check if next character is delimiter or whitespace
11910 if (next_char == ',' || next_char == '\0' ||
11911 next_char == ' ' || next_char == '\t')
11915 // check if string contains another parameter string after a comma
11916 substring = strchr(s, ',');
11917 if (substring == NULL) // string does not contain a comma
11920 // advance string pointer to next character after the comma
11923 // skip potential whitespaces after the comma
11924 while (*substring == ' ' || *substring == '\t')
11927 return string_has_parameter(substring, s_contained);
11930 static int get_anim_parameter_value_ce(char *s)
11933 char *pattern_1 = "ce_change:custom_";
11934 char *pattern_2 = ".page_";
11935 int pattern_1_len = strlen(pattern_1);
11936 char *matching_char = strstr(s_ptr, pattern_1);
11937 int result = ANIM_EVENT_NONE;
11939 if (matching_char == NULL)
11940 return ANIM_EVENT_NONE;
11942 result = ANIM_EVENT_CE_CHANGE;
11944 s_ptr = matching_char + pattern_1_len;
11946 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11947 if (*s_ptr >= '0' && *s_ptr <= '9')
11949 int gic_ce_nr = (*s_ptr++ - '0');
11951 if (*s_ptr >= '0' && *s_ptr <= '9')
11953 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11955 if (*s_ptr >= '0' && *s_ptr <= '9')
11956 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11959 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11960 return ANIM_EVENT_NONE;
11962 // custom element stored as 0 to 255
11965 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11969 // invalid custom element number specified
11971 return ANIM_EVENT_NONE;
11974 // check for change page number ("page_X" or "page_XX") (optional)
11975 if (strPrefix(s_ptr, pattern_2))
11977 s_ptr += strlen(pattern_2);
11979 if (*s_ptr >= '0' && *s_ptr <= '9')
11981 int gic_page_nr = (*s_ptr++ - '0');
11983 if (*s_ptr >= '0' && *s_ptr <= '9')
11984 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11986 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11987 return ANIM_EVENT_NONE;
11989 // change page stored as 1 to 32 (0 means "all change pages")
11991 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11995 // invalid animation part number specified
11997 return ANIM_EVENT_NONE;
12001 // discard result if next character is neither delimiter nor whitespace
12002 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12003 *s_ptr == ' ' || *s_ptr == '\t'))
12004 return ANIM_EVENT_NONE;
12009 static int get_anim_parameter_value(char *s)
12011 int event_value[] =
12019 char *pattern_1[] =
12027 char *pattern_2 = ".part_";
12028 char *matching_char = NULL;
12030 int pattern_1_len = 0;
12031 int result = ANIM_EVENT_NONE;
12034 result = get_anim_parameter_value_ce(s);
12036 if (result != ANIM_EVENT_NONE)
12039 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12041 matching_char = strstr(s_ptr, pattern_1[i]);
12042 pattern_1_len = strlen(pattern_1[i]);
12043 result = event_value[i];
12045 if (matching_char != NULL)
12049 if (matching_char == NULL)
12050 return ANIM_EVENT_NONE;
12052 s_ptr = matching_char + pattern_1_len;
12054 // check for main animation number ("anim_X" or "anim_XX")
12055 if (*s_ptr >= '0' && *s_ptr <= '9')
12057 int gic_anim_nr = (*s_ptr++ - '0');
12059 if (*s_ptr >= '0' && *s_ptr <= '9')
12060 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12062 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12063 return ANIM_EVENT_NONE;
12065 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12069 // invalid main animation number specified
12071 return ANIM_EVENT_NONE;
12074 // check for animation part number ("part_X" or "part_XX") (optional)
12075 if (strPrefix(s_ptr, pattern_2))
12077 s_ptr += strlen(pattern_2);
12079 if (*s_ptr >= '0' && *s_ptr <= '9')
12081 int gic_part_nr = (*s_ptr++ - '0');
12083 if (*s_ptr >= '0' && *s_ptr <= '9')
12084 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12086 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12087 return ANIM_EVENT_NONE;
12089 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12093 // invalid animation part number specified
12095 return ANIM_EVENT_NONE;
12099 // discard result if next character is neither delimiter nor whitespace
12100 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12101 *s_ptr == ' ' || *s_ptr == '\t'))
12102 return ANIM_EVENT_NONE;
12107 static int get_anim_parameter_values(char *s)
12109 int list_pos = ANIM_EVENT_UNDEFINED;
12110 int event_value = ANIM_EVENT_DEFAULT;
12112 if (string_has_parameter(s, "any"))
12113 event_value |= ANIM_EVENT_ANY;
12115 if (string_has_parameter(s, "click:self") ||
12116 string_has_parameter(s, "click") ||
12117 string_has_parameter(s, "self"))
12118 event_value |= ANIM_EVENT_SELF;
12120 if (string_has_parameter(s, "unclick:any"))
12121 event_value |= ANIM_EVENT_UNCLICK_ANY;
12123 // if animation event found, add it to global animation event list
12124 if (event_value != ANIM_EVENT_NONE)
12125 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12129 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12130 event_value = get_anim_parameter_value(s);
12132 // if animation event found, add it to global animation event list
12133 if (event_value != ANIM_EVENT_NONE)
12134 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12136 // continue with next part of the string, starting with next comma
12137 s = strchr(s + 1, ',');
12143 static int get_anim_action_parameter_value(char *token)
12145 // check most common default case first to massively speed things up
12146 if (strEqual(token, ARG_UNDEFINED))
12147 return ANIM_EVENT_ACTION_NONE;
12149 int result = getImageIDFromToken(token);
12153 char *gfx_token = getStringCat2("gfx.", token);
12155 result = getImageIDFromToken(gfx_token);
12157 checked_free(gfx_token);
12162 Key key = getKeyFromX11KeyName(token);
12164 if (key != KSYM_UNDEFINED)
12165 result = -(int)key;
12172 result = get_hash_from_string(token); // unsigned int => int
12173 result = ABS(result); // may be negative now
12174 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12176 setHashEntry(anim_url_hash, int2str(result, 0), token);
12181 result = ANIM_EVENT_ACTION_NONE;
12186 int get_parameter_value(char *value_raw, char *suffix, int type)
12188 char *value = getStringToLower(value_raw);
12189 int result = 0; // probably a save default value
12191 if (strEqual(suffix, ".direction"))
12193 result = (strEqual(value, "left") ? MV_LEFT :
12194 strEqual(value, "right") ? MV_RIGHT :
12195 strEqual(value, "up") ? MV_UP :
12196 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12198 else if (strEqual(suffix, ".position"))
12200 result = (strEqual(value, "left") ? POS_LEFT :
12201 strEqual(value, "right") ? POS_RIGHT :
12202 strEqual(value, "top") ? POS_TOP :
12203 strEqual(value, "upper") ? POS_UPPER :
12204 strEqual(value, "middle") ? POS_MIDDLE :
12205 strEqual(value, "lower") ? POS_LOWER :
12206 strEqual(value, "bottom") ? POS_BOTTOM :
12207 strEqual(value, "any") ? POS_ANY :
12208 strEqual(value, "ce") ? POS_CE :
12209 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12210 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12212 else if (strEqual(suffix, ".align"))
12214 result = (strEqual(value, "left") ? ALIGN_LEFT :
12215 strEqual(value, "right") ? ALIGN_RIGHT :
12216 strEqual(value, "center") ? ALIGN_CENTER :
12217 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12219 else if (strEqual(suffix, ".valign"))
12221 result = (strEqual(value, "top") ? VALIGN_TOP :
12222 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12223 strEqual(value, "middle") ? VALIGN_MIDDLE :
12224 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12226 else if (strEqual(suffix, ".anim_mode"))
12228 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12229 string_has_parameter(value, "loop") ? ANIM_LOOP :
12230 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12231 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12232 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12233 string_has_parameter(value, "random") ? ANIM_RANDOM :
12234 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12235 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12236 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12237 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12238 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12239 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12240 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12241 string_has_parameter(value, "all") ? ANIM_ALL :
12242 string_has_parameter(value, "tiled") ? ANIM_TILED :
12243 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12246 if (string_has_parameter(value, "once"))
12247 result |= ANIM_ONCE;
12249 if (string_has_parameter(value, "reverse"))
12250 result |= ANIM_REVERSE;
12252 if (string_has_parameter(value, "opaque_player"))
12253 result |= ANIM_OPAQUE_PLAYER;
12255 if (string_has_parameter(value, "static_panel"))
12256 result |= ANIM_STATIC_PANEL;
12258 else if (strEqual(suffix, ".init_event") ||
12259 strEqual(suffix, ".anim_event"))
12261 result = get_anim_parameter_values(value);
12263 else if (strEqual(suffix, ".init_delay_action") ||
12264 strEqual(suffix, ".anim_delay_action") ||
12265 strEqual(suffix, ".post_delay_action") ||
12266 strEqual(suffix, ".init_event_action") ||
12267 strEqual(suffix, ".anim_event_action"))
12269 result = get_anim_action_parameter_value(value_raw);
12271 else if (strEqual(suffix, ".class"))
12273 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12274 get_hash_from_string(value));
12276 else if (strEqual(suffix, ".style"))
12278 result = STYLE_DEFAULT;
12280 if (string_has_parameter(value, "accurate_borders"))
12281 result |= STYLE_ACCURATE_BORDERS;
12283 if (string_has_parameter(value, "inner_corners"))
12284 result |= STYLE_INNER_CORNERS;
12286 if (string_has_parameter(value, "reverse"))
12287 result |= STYLE_REVERSE;
12289 if (string_has_parameter(value, "leftmost_position"))
12290 result |= STYLE_LEFTMOST_POSITION;
12292 if (string_has_parameter(value, "block_clicks"))
12293 result |= STYLE_BLOCK;
12295 if (string_has_parameter(value, "passthrough_clicks"))
12296 result |= STYLE_PASSTHROUGH;
12298 if (string_has_parameter(value, "multiple_actions"))
12299 result |= STYLE_MULTIPLE_ACTIONS;
12301 if (string_has_parameter(value, "consume_ce_event"))
12302 result |= STYLE_CONSUME_CE_EVENT;
12304 else if (strEqual(suffix, ".fade_mode"))
12306 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12307 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12308 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12309 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12310 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12311 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12312 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12313 FADE_MODE_DEFAULT);
12315 else if (strEqual(suffix, ".auto_delay_unit"))
12317 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12318 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12319 AUTO_DELAY_UNIT_DEFAULT);
12321 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12323 result = gfx.get_font_from_token_function(value);
12325 else // generic parameter of type integer or boolean
12327 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12328 type == TYPE_INTEGER ? get_integer_from_string(value) :
12329 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12330 ARG_UNDEFINED_VALUE);
12338 static int get_token_parameter_value(char *token, char *value_raw)
12342 if (token == NULL || value_raw == NULL)
12343 return ARG_UNDEFINED_VALUE;
12345 suffix = strrchr(token, '.');
12346 if (suffix == NULL)
12349 if (strEqual(suffix, ".element"))
12350 return getElementFromToken(value_raw);
12352 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12353 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12356 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12357 boolean ignore_defaults)
12361 for (i = 0; image_config_vars[i].token != NULL; i++)
12363 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12365 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12366 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12370 *image_config_vars[i].value =
12371 get_token_parameter_value(image_config_vars[i].token, value);
12375 void InitMenuDesignSettings_Static(void)
12377 // always start with reliable default values from static default config
12378 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12381 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12385 // the following initializes hierarchical values from static configuration
12387 // special case: initialize "ARG_DEFAULT" values in static default config
12388 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12389 titlescreen_initial_first_default.fade_mode =
12390 title_initial_first_default.fade_mode;
12391 titlescreen_initial_first_default.fade_delay =
12392 title_initial_first_default.fade_delay;
12393 titlescreen_initial_first_default.post_delay =
12394 title_initial_first_default.post_delay;
12395 titlescreen_initial_first_default.auto_delay =
12396 title_initial_first_default.auto_delay;
12397 titlescreen_initial_first_default.auto_delay_unit =
12398 title_initial_first_default.auto_delay_unit;
12399 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12400 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12401 titlescreen_first_default.post_delay = title_first_default.post_delay;
12402 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12403 titlescreen_first_default.auto_delay_unit =
12404 title_first_default.auto_delay_unit;
12405 titlemessage_initial_first_default.fade_mode =
12406 title_initial_first_default.fade_mode;
12407 titlemessage_initial_first_default.fade_delay =
12408 title_initial_first_default.fade_delay;
12409 titlemessage_initial_first_default.post_delay =
12410 title_initial_first_default.post_delay;
12411 titlemessage_initial_first_default.auto_delay =
12412 title_initial_first_default.auto_delay;
12413 titlemessage_initial_first_default.auto_delay_unit =
12414 title_initial_first_default.auto_delay_unit;
12415 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12416 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12417 titlemessage_first_default.post_delay = title_first_default.post_delay;
12418 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12419 titlemessage_first_default.auto_delay_unit =
12420 title_first_default.auto_delay_unit;
12422 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12423 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12424 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12425 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12426 titlescreen_initial_default.auto_delay_unit =
12427 title_initial_default.auto_delay_unit;
12428 titlescreen_default.fade_mode = title_default.fade_mode;
12429 titlescreen_default.fade_delay = title_default.fade_delay;
12430 titlescreen_default.post_delay = title_default.post_delay;
12431 titlescreen_default.auto_delay = title_default.auto_delay;
12432 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12433 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12434 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12435 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12436 titlemessage_initial_default.auto_delay_unit =
12437 title_initial_default.auto_delay_unit;
12438 titlemessage_default.fade_mode = title_default.fade_mode;
12439 titlemessage_default.fade_delay = title_default.fade_delay;
12440 titlemessage_default.post_delay = title_default.post_delay;
12441 titlemessage_default.auto_delay = title_default.auto_delay;
12442 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12444 // special case: initialize "ARG_DEFAULT" values in static default config
12445 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12446 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12448 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12449 titlescreen_first[i] = titlescreen_first_default;
12450 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12451 titlemessage_first[i] = titlemessage_first_default;
12453 titlescreen_initial[i] = titlescreen_initial_default;
12454 titlescreen[i] = titlescreen_default;
12455 titlemessage_initial[i] = titlemessage_initial_default;
12456 titlemessage[i] = titlemessage_default;
12459 // special case: initialize "ARG_DEFAULT" values in static default config
12460 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12461 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12463 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12466 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12467 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12468 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12471 // special case: initialize "ARG_DEFAULT" values in static default config
12472 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12473 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12475 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12476 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12477 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12479 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12482 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12486 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12490 struct XY *dst, *src;
12492 game_buttons_xy[] =
12494 { &game.button.save, &game.button.stop },
12495 { &game.button.pause2, &game.button.pause },
12496 { &game.button.load, &game.button.play },
12497 { &game.button.undo, &game.button.stop },
12498 { &game.button.redo, &game.button.play },
12504 // special case: initialize later added SETUP list size from LEVELS value
12505 if (menu.list_size[GAME_MODE_SETUP] == -1)
12506 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12508 // set default position for snapshot buttons to stop/pause/play buttons
12509 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12510 if ((*game_buttons_xy[i].dst).x == -1 &&
12511 (*game_buttons_xy[i].dst).y == -1)
12512 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12514 // --------------------------------------------------------------------------
12515 // dynamic viewports (including playfield margins, borders and alignments)
12516 // --------------------------------------------------------------------------
12518 // dynamic viewports currently only supported for landscape mode
12519 int display_width = MAX(video.display_width, video.display_height);
12520 int display_height = MIN(video.display_width, video.display_height);
12522 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12524 struct RectWithBorder *vp_window = &viewport.window[i];
12525 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12526 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12527 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12528 boolean dynamic_window_width = (vp_window->min_width != -1);
12529 boolean dynamic_window_height = (vp_window->min_height != -1);
12530 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12531 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12533 // adjust window size if min/max width/height is specified
12535 if (vp_window->min_width != -1)
12537 int window_width = display_width;
12539 // when using static window height, use aspect ratio of display
12540 if (vp_window->min_height == -1)
12541 window_width = vp_window->height * display_width / display_height;
12543 vp_window->width = MAX(vp_window->min_width, window_width);
12546 if (vp_window->min_height != -1)
12548 int window_height = display_height;
12550 // when using static window width, use aspect ratio of display
12551 if (vp_window->min_width == -1)
12552 window_height = vp_window->width * display_height / display_width;
12554 vp_window->height = MAX(vp_window->min_height, window_height);
12557 if (vp_window->max_width != -1)
12558 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12560 if (vp_window->max_height != -1)
12561 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12563 int playfield_width = vp_window->width;
12564 int playfield_height = vp_window->height;
12566 // adjust playfield size and position according to specified margins
12568 playfield_width -= vp_playfield->margin_left;
12569 playfield_width -= vp_playfield->margin_right;
12571 playfield_height -= vp_playfield->margin_top;
12572 playfield_height -= vp_playfield->margin_bottom;
12574 // adjust playfield size if min/max width/height is specified
12576 if (vp_playfield->min_width != -1)
12577 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12579 if (vp_playfield->min_height != -1)
12580 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12582 if (vp_playfield->max_width != -1)
12583 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12585 if (vp_playfield->max_height != -1)
12586 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12588 // adjust playfield position according to specified alignment
12590 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12591 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12592 else if (vp_playfield->align == ALIGN_CENTER)
12593 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12594 else if (vp_playfield->align == ALIGN_RIGHT)
12595 vp_playfield->x += playfield_width - vp_playfield->width;
12597 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12598 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12599 else if (vp_playfield->valign == VALIGN_MIDDLE)
12600 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12601 else if (vp_playfield->valign == VALIGN_BOTTOM)
12602 vp_playfield->y += playfield_height - vp_playfield->height;
12604 vp_playfield->x += vp_playfield->margin_left;
12605 vp_playfield->y += vp_playfield->margin_top;
12607 // adjust individual playfield borders if only default border is specified
12609 if (vp_playfield->border_left == -1)
12610 vp_playfield->border_left = vp_playfield->border_size;
12611 if (vp_playfield->border_right == -1)
12612 vp_playfield->border_right = vp_playfield->border_size;
12613 if (vp_playfield->border_top == -1)
12614 vp_playfield->border_top = vp_playfield->border_size;
12615 if (vp_playfield->border_bottom == -1)
12616 vp_playfield->border_bottom = vp_playfield->border_size;
12618 // set dynamic playfield borders if borders are specified as undefined
12619 // (but only if window size was dynamic and playfield size was static)
12621 if (dynamic_window_width && !dynamic_playfield_width)
12623 if (vp_playfield->border_left == -1)
12625 vp_playfield->border_left = (vp_playfield->x -
12626 vp_playfield->margin_left);
12627 vp_playfield->x -= vp_playfield->border_left;
12628 vp_playfield->width += vp_playfield->border_left;
12631 if (vp_playfield->border_right == -1)
12633 vp_playfield->border_right = (vp_window->width -
12635 vp_playfield->width -
12636 vp_playfield->margin_right);
12637 vp_playfield->width += vp_playfield->border_right;
12641 if (dynamic_window_height && !dynamic_playfield_height)
12643 if (vp_playfield->border_top == -1)
12645 vp_playfield->border_top = (vp_playfield->y -
12646 vp_playfield->margin_top);
12647 vp_playfield->y -= vp_playfield->border_top;
12648 vp_playfield->height += vp_playfield->border_top;
12651 if (vp_playfield->border_bottom == -1)
12653 vp_playfield->border_bottom = (vp_window->height -
12655 vp_playfield->height -
12656 vp_playfield->margin_bottom);
12657 vp_playfield->height += vp_playfield->border_bottom;
12661 // adjust playfield size to be a multiple of a defined alignment tile size
12663 int align_size = vp_playfield->align_size;
12664 int playfield_xtiles = vp_playfield->width / align_size;
12665 int playfield_ytiles = vp_playfield->height / align_size;
12666 int playfield_width_corrected = playfield_xtiles * align_size;
12667 int playfield_height_corrected = playfield_ytiles * align_size;
12668 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12669 i == GFX_SPECIAL_ARG_EDITOR);
12671 if (is_playfield_mode &&
12672 dynamic_playfield_width &&
12673 vp_playfield->width != playfield_width_corrected)
12675 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12677 vp_playfield->width = playfield_width_corrected;
12679 if (vp_playfield->align == ALIGN_LEFT)
12681 vp_playfield->border_left += playfield_xdiff;
12683 else if (vp_playfield->align == ALIGN_RIGHT)
12685 vp_playfield->border_right += playfield_xdiff;
12687 else if (vp_playfield->align == ALIGN_CENTER)
12689 int border_left_diff = playfield_xdiff / 2;
12690 int border_right_diff = playfield_xdiff - border_left_diff;
12692 vp_playfield->border_left += border_left_diff;
12693 vp_playfield->border_right += border_right_diff;
12697 if (is_playfield_mode &&
12698 dynamic_playfield_height &&
12699 vp_playfield->height != playfield_height_corrected)
12701 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12703 vp_playfield->height = playfield_height_corrected;
12705 if (vp_playfield->valign == VALIGN_TOP)
12707 vp_playfield->border_top += playfield_ydiff;
12709 else if (vp_playfield->align == VALIGN_BOTTOM)
12711 vp_playfield->border_right += playfield_ydiff;
12713 else if (vp_playfield->align == VALIGN_MIDDLE)
12715 int border_top_diff = playfield_ydiff / 2;
12716 int border_bottom_diff = playfield_ydiff - border_top_diff;
12718 vp_playfield->border_top += border_top_diff;
12719 vp_playfield->border_bottom += border_bottom_diff;
12723 // adjust door positions according to specified alignment
12725 for (j = 0; j < 2; j++)
12727 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12729 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12730 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12731 else if (vp_door->align == ALIGN_CENTER)
12732 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12733 else if (vp_door->align == ALIGN_RIGHT)
12734 vp_door->x += vp_window->width - vp_door->width;
12736 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12737 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12738 else if (vp_door->valign == VALIGN_MIDDLE)
12739 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12740 else if (vp_door->valign == VALIGN_BOTTOM)
12741 vp_door->y += vp_window->height - vp_door->height;
12746 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12750 struct XYTileSize *dst, *src;
12753 editor_buttons_xy[] =
12756 &editor.button.element_left, &editor.palette.element_left,
12757 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12760 &editor.button.element_middle, &editor.palette.element_middle,
12761 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12764 &editor.button.element_right, &editor.palette.element_right,
12765 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12772 // set default position for element buttons to element graphics
12773 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12775 if ((*editor_buttons_xy[i].dst).x == -1 &&
12776 (*editor_buttons_xy[i].dst).y == -1)
12778 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12780 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12782 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12786 // adjust editor palette rows and columns if specified to be dynamic
12788 if (editor.palette.cols == -1)
12790 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12791 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12792 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12794 editor.palette.cols = (vp_width - sc_width) / bt_width;
12796 if (editor.palette.x == -1)
12798 int palette_width = editor.palette.cols * bt_width + sc_width;
12800 editor.palette.x = (vp_width - palette_width) / 2;
12804 if (editor.palette.rows == -1)
12806 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12807 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12808 int tx_height = getFontHeight(FONT_TEXT_2);
12810 editor.palette.rows = (vp_height - tx_height) / bt_height;
12812 if (editor.palette.y == -1)
12814 int palette_height = editor.palette.rows * bt_height + tx_height;
12816 editor.palette.y = (vp_height - palette_height) / 2;
12821 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12822 boolean initialize)
12824 // special case: check if network and preview player positions are redefined,
12825 // to compare this later against the main menu level preview being redefined
12826 struct TokenIntPtrInfo menu_config_players[] =
12828 { "main.network_players.x", &menu.main.network_players.redefined },
12829 { "main.network_players.y", &menu.main.network_players.redefined },
12830 { "main.preview_players.x", &menu.main.preview_players.redefined },
12831 { "main.preview_players.y", &menu.main.preview_players.redefined },
12832 { "preview.x", &preview.redefined },
12833 { "preview.y", &preview.redefined }
12839 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12840 *menu_config_players[i].value = FALSE;
12844 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12845 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12846 *menu_config_players[i].value = TRUE;
12850 static void InitMenuDesignSettings_PreviewPlayers(void)
12852 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12855 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12857 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12860 static void LoadMenuDesignSettingsFromFilename(char *filename)
12862 static struct TitleFadingInfo tfi;
12863 static struct TitleMessageInfo tmi;
12864 static struct TokenInfo title_tokens[] =
12866 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12867 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12868 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12869 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12870 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12874 static struct TokenInfo titlemessage_tokens[] =
12876 { TYPE_INTEGER, &tmi.x, ".x" },
12877 { TYPE_INTEGER, &tmi.y, ".y" },
12878 { TYPE_INTEGER, &tmi.width, ".width" },
12879 { TYPE_INTEGER, &tmi.height, ".height" },
12880 { TYPE_INTEGER, &tmi.chars, ".chars" },
12881 { TYPE_INTEGER, &tmi.lines, ".lines" },
12882 { TYPE_INTEGER, &tmi.align, ".align" },
12883 { TYPE_INTEGER, &tmi.valign, ".valign" },
12884 { TYPE_INTEGER, &tmi.font, ".font" },
12885 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12886 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12887 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12888 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12889 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12890 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12891 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12892 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12893 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12899 struct TitleFadingInfo *info;
12904 // initialize first titles from "enter screen" definitions, if defined
12905 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12906 { &title_first_default, "menu.enter_screen.TITLE" },
12908 // initialize title screens from "next screen" definitions, if defined
12909 { &title_initial_default, "menu.next_screen.TITLE" },
12910 { &title_default, "menu.next_screen.TITLE" },
12916 struct TitleMessageInfo *array;
12919 titlemessage_arrays[] =
12921 // initialize first titles from "enter screen" definitions, if defined
12922 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12923 { titlescreen_first, "menu.enter_screen.TITLE" },
12924 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12925 { titlemessage_first, "menu.enter_screen.TITLE" },
12927 // initialize titles from "next screen" definitions, if defined
12928 { titlescreen_initial, "menu.next_screen.TITLE" },
12929 { titlescreen, "menu.next_screen.TITLE" },
12930 { titlemessage_initial, "menu.next_screen.TITLE" },
12931 { titlemessage, "menu.next_screen.TITLE" },
12933 // overwrite titles with title definitions, if defined
12934 { titlescreen_initial_first, "[title_initial]" },
12935 { titlescreen_first, "[title]" },
12936 { titlemessage_initial_first, "[title_initial]" },
12937 { titlemessage_first, "[title]" },
12939 { titlescreen_initial, "[title_initial]" },
12940 { titlescreen, "[title]" },
12941 { titlemessage_initial, "[title_initial]" },
12942 { titlemessage, "[title]" },
12944 // overwrite titles with title screen/message definitions, if defined
12945 { titlescreen_initial_first, "[titlescreen_initial]" },
12946 { titlescreen_first, "[titlescreen]" },
12947 { titlemessage_initial_first, "[titlemessage_initial]" },
12948 { titlemessage_first, "[titlemessage]" },
12950 { titlescreen_initial, "[titlescreen_initial]" },
12951 { titlescreen, "[titlescreen]" },
12952 { titlemessage_initial, "[titlemessage_initial]" },
12953 { titlemessage, "[titlemessage]" },
12957 SetupFileHash *setup_file_hash;
12960 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12963 // the following initializes hierarchical values from dynamic configuration
12965 // special case: initialize with default values that may be overwritten
12966 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12967 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12969 struct TokenIntPtrInfo menu_config[] =
12971 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12972 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12973 { "menu.list_size", &menu.list_size[i] }
12976 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12978 char *token = menu_config[j].token;
12979 char *value = getHashEntry(setup_file_hash, token);
12982 *menu_config[j].value = get_integer_from_string(value);
12986 // special case: initialize with default values that may be overwritten
12987 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12988 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12990 struct TokenIntPtrInfo menu_config[] =
12992 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12993 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12994 { "menu.list_size.INFO", &menu.list_size_info[i] },
12995 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12996 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12999 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13001 char *token = menu_config[j].token;
13002 char *value = getHashEntry(setup_file_hash, token);
13005 *menu_config[j].value = get_integer_from_string(value);
13009 // special case: initialize with default values that may be overwritten
13010 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13011 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13013 struct TokenIntPtrInfo menu_config[] =
13015 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13016 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13019 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13021 char *token = menu_config[j].token;
13022 char *value = getHashEntry(setup_file_hash, token);
13025 *menu_config[j].value = get_integer_from_string(value);
13029 // special case: initialize with default values that may be overwritten
13030 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13031 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13033 struct TokenIntPtrInfo menu_config[] =
13035 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13036 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13037 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13038 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13039 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13040 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13041 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13042 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13043 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13044 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13047 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13049 char *token = menu_config[j].token;
13050 char *value = getHashEntry(setup_file_hash, token);
13053 *menu_config[j].value = get_integer_from_string(value);
13057 // special case: initialize with default values that may be overwritten
13058 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13059 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13061 struct TokenIntPtrInfo menu_config[] =
13063 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13064 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13065 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13066 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13067 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13068 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13069 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13070 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13071 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13074 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13076 char *token = menu_config[j].token;
13077 char *value = getHashEntry(setup_file_hash, token);
13080 *menu_config[j].value = get_token_parameter_value(token, value);
13084 // special case: initialize with default values that may be overwritten
13085 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13086 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13090 char *token_prefix;
13091 struct RectWithBorder *struct_ptr;
13095 { "viewport.window", &viewport.window[i] },
13096 { "viewport.playfield", &viewport.playfield[i] },
13097 { "viewport.door_1", &viewport.door_1[i] },
13098 { "viewport.door_2", &viewport.door_2[i] }
13101 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13103 struct TokenIntPtrInfo vp_config[] =
13105 { ".x", &vp_struct[j].struct_ptr->x },
13106 { ".y", &vp_struct[j].struct_ptr->y },
13107 { ".width", &vp_struct[j].struct_ptr->width },
13108 { ".height", &vp_struct[j].struct_ptr->height },
13109 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13110 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13111 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13112 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13113 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13114 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13115 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13116 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13117 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13118 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13119 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13120 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13121 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13122 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13123 { ".align", &vp_struct[j].struct_ptr->align },
13124 { ".valign", &vp_struct[j].struct_ptr->valign }
13127 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13129 char *token = getStringCat2(vp_struct[j].token_prefix,
13130 vp_config[k].token);
13131 char *value = getHashEntry(setup_file_hash, token);
13134 *vp_config[k].value = get_token_parameter_value(token, value);
13141 // special case: initialize with default values that may be overwritten
13142 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13143 for (i = 0; title_info[i].info != NULL; i++)
13145 struct TitleFadingInfo *info = title_info[i].info;
13146 char *base_token = title_info[i].text;
13148 for (j = 0; title_tokens[j].type != -1; j++)
13150 char *token = getStringCat2(base_token, title_tokens[j].text);
13151 char *value = getHashEntry(setup_file_hash, token);
13155 int parameter_value = get_token_parameter_value(token, value);
13159 *(int *)title_tokens[j].value = (int)parameter_value;
13168 // special case: initialize with default values that may be overwritten
13169 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13170 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13172 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13173 char *base_token = titlemessage_arrays[i].text;
13175 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13177 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13178 char *value = getHashEntry(setup_file_hash, token);
13182 int parameter_value = get_token_parameter_value(token, value);
13184 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13188 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13189 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13191 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13201 // read (and overwrite with) values that may be specified in config file
13202 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13204 // special case: check if network and preview player positions are redefined
13205 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13207 freeSetupFileHash(setup_file_hash);
13210 void LoadMenuDesignSettings(void)
13212 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13214 InitMenuDesignSettings_Static();
13215 InitMenuDesignSettings_SpecialPreProcessing();
13216 InitMenuDesignSettings_PreviewPlayers();
13218 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13220 // first look for special settings configured in level series config
13221 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13223 if (fileExists(filename_base))
13224 LoadMenuDesignSettingsFromFilename(filename_base);
13227 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13229 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13230 LoadMenuDesignSettingsFromFilename(filename_local);
13232 InitMenuDesignSettings_SpecialPostProcessing();
13235 void LoadMenuDesignSettings_AfterGraphics(void)
13237 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13240 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13241 boolean ignore_defaults)
13245 for (i = 0; sound_config_vars[i].token != NULL; i++)
13247 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13249 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13250 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13254 *sound_config_vars[i].value =
13255 get_token_parameter_value(sound_config_vars[i].token, value);
13259 void InitSoundSettings_Static(void)
13261 // always start with reliable default values from static default config
13262 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13265 static void LoadSoundSettingsFromFilename(char *filename)
13267 SetupFileHash *setup_file_hash;
13269 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13272 // read (and overwrite with) values that may be specified in config file
13273 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13275 freeSetupFileHash(setup_file_hash);
13278 void LoadSoundSettings(void)
13280 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13282 InitSoundSettings_Static();
13284 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13286 // first look for special settings configured in level series config
13287 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13289 if (fileExists(filename_base))
13290 LoadSoundSettingsFromFilename(filename_base);
13293 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13295 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13296 LoadSoundSettingsFromFilename(filename_local);
13299 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13301 char *filename = getEditorSetupFilename();
13302 SetupFileList *setup_file_list, *list;
13303 SetupFileHash *element_hash;
13304 int num_unknown_tokens = 0;
13307 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13310 element_hash = newSetupFileHash();
13312 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13313 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13315 // determined size may be larger than needed (due to unknown elements)
13317 for (list = setup_file_list; list != NULL; list = list->next)
13320 // add space for up to 3 more elements for padding that may be needed
13321 *num_elements += 3;
13323 // free memory for old list of elements, if needed
13324 checked_free(*elements);
13326 // allocate memory for new list of elements
13327 *elements = checked_malloc(*num_elements * sizeof(int));
13330 for (list = setup_file_list; list != NULL; list = list->next)
13332 char *value = getHashEntry(element_hash, list->token);
13334 if (value == NULL) // try to find obsolete token mapping
13336 char *mapped_token = get_mapped_token(list->token);
13338 if (mapped_token != NULL)
13340 value = getHashEntry(element_hash, mapped_token);
13342 free(mapped_token);
13348 (*elements)[(*num_elements)++] = atoi(value);
13352 if (num_unknown_tokens == 0)
13355 Warn("unknown token(s) found in config file:");
13356 Warn("- config file: '%s'", filename);
13358 num_unknown_tokens++;
13361 Warn("- token: '%s'", list->token);
13365 if (num_unknown_tokens > 0)
13368 while (*num_elements % 4) // pad with empty elements, if needed
13369 (*elements)[(*num_elements)++] = EL_EMPTY;
13371 freeSetupFileList(setup_file_list);
13372 freeSetupFileHash(element_hash);
13375 for (i = 0; i < *num_elements; i++)
13376 Debug("editor", "element '%s' [%d]\n",
13377 element_info[(*elements)[i]].token_name, (*elements)[i]);
13381 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13384 SetupFileHash *setup_file_hash = NULL;
13385 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13386 char *filename_music, *filename_prefix, *filename_info;
13392 token_to_value_ptr[] =
13394 { "title_header", &tmp_music_file_info.title_header },
13395 { "artist_header", &tmp_music_file_info.artist_header },
13396 { "album_header", &tmp_music_file_info.album_header },
13397 { "year_header", &tmp_music_file_info.year_header },
13398 { "played_header", &tmp_music_file_info.played_header },
13400 { "title", &tmp_music_file_info.title },
13401 { "artist", &tmp_music_file_info.artist },
13402 { "album", &tmp_music_file_info.album },
13403 { "year", &tmp_music_file_info.year },
13404 { "played", &tmp_music_file_info.played },
13410 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13411 getCustomMusicFilename(basename));
13413 if (filename_music == NULL)
13416 // ---------- try to replace file extension ----------
13418 filename_prefix = getStringCopy(filename_music);
13419 if (strrchr(filename_prefix, '.') != NULL)
13420 *strrchr(filename_prefix, '.') = '\0';
13421 filename_info = getStringCat2(filename_prefix, ".txt");
13423 if (fileExists(filename_info))
13424 setup_file_hash = loadSetupFileHash(filename_info);
13426 free(filename_prefix);
13427 free(filename_info);
13429 if (setup_file_hash == NULL)
13431 // ---------- try to add file extension ----------
13433 filename_prefix = getStringCopy(filename_music);
13434 filename_info = getStringCat2(filename_prefix, ".txt");
13436 if (fileExists(filename_info))
13437 setup_file_hash = loadSetupFileHash(filename_info);
13439 free(filename_prefix);
13440 free(filename_info);
13443 if (setup_file_hash == NULL)
13446 // ---------- music file info found ----------
13448 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13450 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13452 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13454 *token_to_value_ptr[i].value_ptr =
13455 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13458 tmp_music_file_info.basename = getStringCopy(basename);
13459 tmp_music_file_info.music = music;
13460 tmp_music_file_info.is_sound = is_sound;
13462 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13463 *new_music_file_info = tmp_music_file_info;
13465 return new_music_file_info;
13468 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13470 return get_music_file_info_ext(basename, music, FALSE);
13473 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13475 return get_music_file_info_ext(basename, sound, TRUE);
13478 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13479 char *basename, boolean is_sound)
13481 for (; list != NULL; list = list->next)
13482 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13488 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13490 return music_info_listed_ext(list, basename, FALSE);
13493 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13495 return music_info_listed_ext(list, basename, TRUE);
13498 void LoadMusicInfo(void)
13500 int num_music_noconf = getMusicListSize_NoConf();
13501 int num_music = getMusicListSize();
13502 int num_sounds = getSoundListSize();
13503 struct FileInfo *music, *sound;
13504 struct MusicFileInfo *next, **new;
13508 while (music_file_info != NULL)
13510 next = music_file_info->next;
13512 checked_free(music_file_info->basename);
13514 checked_free(music_file_info->title_header);
13515 checked_free(music_file_info->artist_header);
13516 checked_free(music_file_info->album_header);
13517 checked_free(music_file_info->year_header);
13518 checked_free(music_file_info->played_header);
13520 checked_free(music_file_info->title);
13521 checked_free(music_file_info->artist);
13522 checked_free(music_file_info->album);
13523 checked_free(music_file_info->year);
13524 checked_free(music_file_info->played);
13526 free(music_file_info);
13528 music_file_info = next;
13531 new = &music_file_info;
13533 // get (configured or unconfigured) music file info for all levels
13534 for (i = leveldir_current->first_level;
13535 i <= leveldir_current->last_level; i++)
13539 if (levelset.music[i] != MUS_UNDEFINED)
13541 // get music file info for configured level music
13542 music_nr = levelset.music[i];
13544 else if (num_music_noconf > 0)
13546 // get music file info for unconfigured level music
13547 int level_pos = i - leveldir_current->first_level;
13549 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13556 char *basename = getMusicInfoEntryFilename(music_nr);
13558 if (basename == NULL)
13561 if (!music_info_listed(music_file_info, basename))
13563 *new = get_music_file_info(basename, music_nr);
13566 new = &(*new)->next;
13570 // get music file info for all remaining configured music files
13571 for (i = 0; i < num_music; i++)
13573 music = getMusicListEntry(i);
13575 if (music->filename == NULL)
13578 if (strEqual(music->filename, UNDEFINED_FILENAME))
13581 // a configured file may be not recognized as music
13582 if (!FileIsMusic(music->filename))
13585 if (!music_info_listed(music_file_info, music->filename))
13587 *new = get_music_file_info(music->filename, i);
13590 new = &(*new)->next;
13594 // get sound file info for all configured sound files
13595 for (i = 0; i < num_sounds; i++)
13597 sound = getSoundListEntry(i);
13599 if (sound->filename == NULL)
13602 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13605 // a configured file may be not recognized as sound
13606 if (!FileIsSound(sound->filename))
13609 if (!sound_info_listed(music_file_info, sound->filename))
13611 *new = get_sound_file_info(sound->filename, i);
13613 new = &(*new)->next;
13617 // add pointers to previous list nodes
13619 struct MusicFileInfo *node = music_file_info;
13621 while (node != NULL)
13624 node->next->prev = node;
13630 static void add_helpanim_entry(int element, int action, int direction,
13631 int delay, int *num_list_entries)
13633 struct HelpAnimInfo *new_list_entry;
13634 (*num_list_entries)++;
13637 checked_realloc(helpanim_info,
13638 *num_list_entries * sizeof(struct HelpAnimInfo));
13639 new_list_entry = &helpanim_info[*num_list_entries - 1];
13641 new_list_entry->element = element;
13642 new_list_entry->action = action;
13643 new_list_entry->direction = direction;
13644 new_list_entry->delay = delay;
13647 static void print_unknown_token(char *filename, char *token, int token_nr)
13652 Warn("unknown token(s) found in config file:");
13653 Warn("- config file: '%s'", filename);
13656 Warn("- token: '%s'", token);
13659 static void print_unknown_token_end(int token_nr)
13665 void LoadHelpAnimInfo(void)
13667 char *filename = getHelpAnimFilename();
13668 SetupFileList *setup_file_list = NULL, *list;
13669 SetupFileHash *element_hash, *action_hash, *direction_hash;
13670 int num_list_entries = 0;
13671 int num_unknown_tokens = 0;
13674 if (fileExists(filename))
13675 setup_file_list = loadSetupFileList(filename);
13677 if (setup_file_list == NULL)
13679 // use reliable default values from static configuration
13680 SetupFileList *insert_ptr;
13682 insert_ptr = setup_file_list =
13683 newSetupFileList(helpanim_config[0].token,
13684 helpanim_config[0].value);
13686 for (i = 1; helpanim_config[i].token; i++)
13687 insert_ptr = addListEntry(insert_ptr,
13688 helpanim_config[i].token,
13689 helpanim_config[i].value);
13692 element_hash = newSetupFileHash();
13693 action_hash = newSetupFileHash();
13694 direction_hash = newSetupFileHash();
13696 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13697 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13699 for (i = 0; i < NUM_ACTIONS; i++)
13700 setHashEntry(action_hash, element_action_info[i].suffix,
13701 i_to_a(element_action_info[i].value));
13703 // do not store direction index (bit) here, but direction value!
13704 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13705 setHashEntry(direction_hash, element_direction_info[i].suffix,
13706 i_to_a(1 << element_direction_info[i].value));
13708 for (list = setup_file_list; list != NULL; list = list->next)
13710 char *element_token, *action_token, *direction_token;
13711 char *element_value, *action_value, *direction_value;
13712 int delay = atoi(list->value);
13714 if (strEqual(list->token, "end"))
13716 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13721 /* first try to break element into element/action/direction parts;
13722 if this does not work, also accept combined "element[.act][.dir]"
13723 elements (like "dynamite.active"), which are unique elements */
13725 if (strchr(list->token, '.') == NULL) // token contains no '.'
13727 element_value = getHashEntry(element_hash, list->token);
13728 if (element_value != NULL) // element found
13729 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13730 &num_list_entries);
13733 // no further suffixes found -- this is not an element
13734 print_unknown_token(filename, list->token, num_unknown_tokens++);
13740 // token has format "<prefix>.<something>"
13742 action_token = strchr(list->token, '.'); // suffix may be action ...
13743 direction_token = action_token; // ... or direction
13745 element_token = getStringCopy(list->token);
13746 *strchr(element_token, '.') = '\0';
13748 element_value = getHashEntry(element_hash, element_token);
13750 if (element_value == NULL) // this is no element
13752 element_value = getHashEntry(element_hash, list->token);
13753 if (element_value != NULL) // combined element found
13754 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13755 &num_list_entries);
13757 print_unknown_token(filename, list->token, num_unknown_tokens++);
13759 free(element_token);
13764 action_value = getHashEntry(action_hash, action_token);
13766 if (action_value != NULL) // action found
13768 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13769 &num_list_entries);
13771 free(element_token);
13776 direction_value = getHashEntry(direction_hash, direction_token);
13778 if (direction_value != NULL) // direction found
13780 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13781 &num_list_entries);
13783 free(element_token);
13788 if (strchr(action_token + 1, '.') == NULL)
13790 // no further suffixes found -- this is not an action nor direction
13792 element_value = getHashEntry(element_hash, list->token);
13793 if (element_value != NULL) // combined element found
13794 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13795 &num_list_entries);
13797 print_unknown_token(filename, list->token, num_unknown_tokens++);
13799 free(element_token);
13804 // token has format "<prefix>.<suffix>.<something>"
13806 direction_token = strchr(action_token + 1, '.');
13808 action_token = getStringCopy(action_token);
13809 *strchr(action_token + 1, '.') = '\0';
13811 action_value = getHashEntry(action_hash, action_token);
13813 if (action_value == NULL) // this is no action
13815 element_value = getHashEntry(element_hash, list->token);
13816 if (element_value != NULL) // combined element found
13817 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13818 &num_list_entries);
13820 print_unknown_token(filename, list->token, num_unknown_tokens++);
13822 free(element_token);
13823 free(action_token);
13828 direction_value = getHashEntry(direction_hash, direction_token);
13830 if (direction_value != NULL) // direction found
13832 add_helpanim_entry(atoi(element_value), atoi(action_value),
13833 atoi(direction_value), delay, &num_list_entries);
13835 free(element_token);
13836 free(action_token);
13841 // this is no direction
13843 element_value = getHashEntry(element_hash, list->token);
13844 if (element_value != NULL) // combined element found
13845 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13846 &num_list_entries);
13848 print_unknown_token(filename, list->token, num_unknown_tokens++);
13850 free(element_token);
13851 free(action_token);
13854 print_unknown_token_end(num_unknown_tokens);
13856 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13857 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13859 freeSetupFileList(setup_file_list);
13860 freeSetupFileHash(element_hash);
13861 freeSetupFileHash(action_hash);
13862 freeSetupFileHash(direction_hash);
13865 for (i = 0; i < num_list_entries; i++)
13866 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13867 EL_NAME(helpanim_info[i].element),
13868 helpanim_info[i].element,
13869 helpanim_info[i].action,
13870 helpanim_info[i].direction,
13871 helpanim_info[i].delay);
13875 void LoadHelpTextInfo(void)
13877 char *filename = getHelpTextFilename();
13880 if (helptext_info != NULL)
13882 freeSetupFileHash(helptext_info);
13883 helptext_info = NULL;
13886 if (fileExists(filename))
13887 helptext_info = loadSetupFileHash(filename);
13889 if (helptext_info == NULL)
13891 // use reliable default values from static configuration
13892 helptext_info = newSetupFileHash();
13894 for (i = 0; helptext_config[i].token; i++)
13895 setHashEntry(helptext_info,
13896 helptext_config[i].token,
13897 helptext_config[i].value);
13901 BEGIN_HASH_ITERATION(helptext_info, itr)
13903 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13904 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13906 END_HASH_ITERATION(hash, itr)
13911 // ----------------------------------------------------------------------------
13913 // ----------------------------------------------------------------------------
13915 #define MAX_NUM_CONVERT_LEVELS 1000
13917 void ConvertLevels(void)
13919 static LevelDirTree *convert_leveldir = NULL;
13920 static int convert_level_nr = -1;
13921 static int num_levels_handled = 0;
13922 static int num_levels_converted = 0;
13923 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13926 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13927 global.convert_leveldir);
13929 if (convert_leveldir == NULL)
13930 Fail("no such level identifier: '%s'", global.convert_leveldir);
13932 leveldir_current = convert_leveldir;
13934 if (global.convert_level_nr != -1)
13936 convert_leveldir->first_level = global.convert_level_nr;
13937 convert_leveldir->last_level = global.convert_level_nr;
13940 convert_level_nr = convert_leveldir->first_level;
13942 PrintLine("=", 79);
13943 Print("Converting levels\n");
13944 PrintLine("-", 79);
13945 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13946 Print("Level series name: '%s'\n", convert_leveldir->name);
13947 Print("Level series author: '%s'\n", convert_leveldir->author);
13948 Print("Number of levels: %d\n", convert_leveldir->levels);
13949 PrintLine("=", 79);
13952 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13953 levels_failed[i] = FALSE;
13955 while (convert_level_nr <= convert_leveldir->last_level)
13957 char *level_filename;
13960 level_nr = convert_level_nr++;
13962 Print("Level %03d: ", level_nr);
13964 LoadLevel(level_nr);
13965 if (level.no_level_file || level.no_valid_file)
13967 Print("(no level)\n");
13971 Print("converting level ... ");
13974 // special case: conversion of some EMC levels as requested by ACME
13975 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13978 level_filename = getDefaultLevelFilename(level_nr);
13979 new_level = !fileExists(level_filename);
13983 SaveLevel(level_nr);
13985 num_levels_converted++;
13987 Print("converted.\n");
13991 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13992 levels_failed[level_nr] = TRUE;
13994 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13997 num_levels_handled++;
14001 PrintLine("=", 79);
14002 Print("Number of levels handled: %d\n", num_levels_handled);
14003 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14004 (num_levels_handled ?
14005 num_levels_converted * 100 / num_levels_handled : 0));
14006 PrintLine("-", 79);
14007 Print("Summary (for automatic parsing by scripts):\n");
14008 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14009 convert_leveldir->identifier, num_levels_converted,
14010 num_levels_handled,
14011 (num_levels_handled ?
14012 num_levels_converted * 100 / num_levels_handled : 0));
14014 if (num_levels_handled != num_levels_converted)
14016 Print(", FAILED:");
14017 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14018 if (levels_failed[i])
14023 PrintLine("=", 79);
14025 CloseAllAndExit(0);
14029 // ----------------------------------------------------------------------------
14030 // create and save images for use in level sketches (raw BMP format)
14031 // ----------------------------------------------------------------------------
14033 void CreateLevelSketchImages(void)
14039 InitElementPropertiesGfxElement();
14041 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14042 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14044 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14046 int element = getMappedElement(i);
14047 char basename1[16];
14048 char basename2[16];
14052 sprintf(basename1, "%04d.bmp", i);
14053 sprintf(basename2, "%04ds.bmp", i);
14055 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14056 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14058 DrawSizedElement(0, 0, element, TILESIZE);
14059 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14061 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14062 Fail("cannot save level sketch image file '%s'", filename1);
14064 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14065 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14067 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14068 Fail("cannot save level sketch image file '%s'", filename2);
14073 // create corresponding SQL statements (for normal and small images)
14076 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14077 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14080 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14081 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14083 // optional: create content for forum level sketch demonstration post
14085 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14088 FreeBitmap(bitmap1);
14089 FreeBitmap(bitmap2);
14092 fprintf(stderr, "\n");
14094 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14096 CloseAllAndExit(0);
14100 // ----------------------------------------------------------------------------
14101 // create and save images for element collecting animations (raw BMP format)
14102 // ----------------------------------------------------------------------------
14104 static boolean createCollectImage(int element)
14106 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14109 void CreateCollectElementImages(void)
14113 int anim_frames = num_steps - 1;
14114 int tile_size = TILESIZE;
14115 int anim_width = tile_size * anim_frames;
14116 int anim_height = tile_size;
14117 int num_collect_images = 0;
14118 int pos_collect_images = 0;
14120 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14121 if (createCollectImage(i))
14122 num_collect_images++;
14124 Info("Creating %d element collecting animation images ...",
14125 num_collect_images);
14127 int dst_width = anim_width * 2;
14128 int dst_height = anim_height * num_collect_images / 2;
14129 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14130 char *basename_bmp = "RocksCollect.bmp";
14131 char *basename_png = "RocksCollect.png";
14132 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14133 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14134 int len_filename_bmp = strlen(filename_bmp);
14135 int len_filename_png = strlen(filename_png);
14136 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14137 char cmd_convert[max_command_len];
14139 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14143 // force using RGBA surface for destination bitmap
14144 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14145 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14147 dst_bitmap->surface =
14148 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14150 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14152 if (!createCollectImage(i))
14155 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14156 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14157 int graphic = el2img(i);
14158 char *token_name = element_info[i].token_name;
14159 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14160 Bitmap *src_bitmap;
14163 Info("- creating collecting image for '%s' ...", token_name);
14165 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14167 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14168 tile_size, tile_size, 0, 0);
14170 // force using RGBA surface for temporary bitmap (using transparent black)
14171 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14172 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14174 tmp_bitmap->surface =
14175 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14177 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14179 for (j = 0; j < anim_frames; j++)
14181 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14182 int frame_size = frame_size_final * num_steps;
14183 int offset = (tile_size - frame_size_final) / 2;
14184 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14186 while (frame_size > frame_size_final)
14190 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14192 FreeBitmap(frame_bitmap);
14194 frame_bitmap = half_bitmap;
14197 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14198 frame_size_final, frame_size_final,
14199 dst_x + j * tile_size + offset, dst_y + offset);
14201 FreeBitmap(frame_bitmap);
14204 tmp_bitmap->surface_masked = NULL;
14206 FreeBitmap(tmp_bitmap);
14208 pos_collect_images++;
14211 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14212 Fail("cannot save element collecting image file '%s'", filename_bmp);
14214 FreeBitmap(dst_bitmap);
14216 Info("Converting image file from BMP to PNG ...");
14218 if (system(cmd_convert) != 0)
14219 Fail("converting image file failed");
14221 unlink(filename_bmp);
14225 CloseAllAndExit(0);
14229 // ----------------------------------------------------------------------------
14230 // create and save images for custom and group elements (raw BMP format)
14231 // ----------------------------------------------------------------------------
14233 void CreateCustomElementImages(char *directory)
14235 char *src_basename = "RocksCE-template.ilbm";
14236 char *dst_basename = "RocksCE.bmp";
14237 char *src_filename = getPath2(directory, src_basename);
14238 char *dst_filename = getPath2(directory, dst_basename);
14239 Bitmap *src_bitmap;
14241 int yoffset_ce = 0;
14242 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14245 InitVideoDefaults();
14247 ReCreateBitmap(&backbuffer, video.width, video.height);
14249 src_bitmap = LoadImage(src_filename);
14251 bitmap = CreateBitmap(TILEX * 16 * 2,
14252 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14255 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14262 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14263 TILEX * x, TILEY * y + yoffset_ce);
14265 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14267 TILEX * x + TILEX * 16,
14268 TILEY * y + yoffset_ce);
14270 for (j = 2; j >= 0; j--)
14274 BlitBitmap(src_bitmap, bitmap,
14275 TILEX + c * 7, 0, 6, 10,
14276 TILEX * x + 6 + j * 7,
14277 TILEY * y + 11 + yoffset_ce);
14279 BlitBitmap(src_bitmap, bitmap,
14280 TILEX + c * 8, TILEY, 6, 10,
14281 TILEX * 16 + TILEX * x + 6 + j * 8,
14282 TILEY * y + 10 + yoffset_ce);
14288 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14295 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14296 TILEX * x, TILEY * y + yoffset_ge);
14298 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14300 TILEX * x + TILEX * 16,
14301 TILEY * y + yoffset_ge);
14303 for (j = 1; j >= 0; j--)
14307 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14308 TILEX * x + 6 + j * 10,
14309 TILEY * y + 11 + yoffset_ge);
14311 BlitBitmap(src_bitmap, bitmap,
14312 TILEX + c * 8, TILEY + 12, 6, 10,
14313 TILEX * 16 + TILEX * x + 10 + j * 8,
14314 TILEY * y + 10 + yoffset_ge);
14320 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14321 Fail("cannot save CE graphics file '%s'", dst_filename);
14323 FreeBitmap(bitmap);
14325 CloseAllAndExit(0);