1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_wraparound_objects, FALSE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_scan_first_and_last_row, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_short_explosions, TRUE
315 TYPE_BOOLEAN, CONF_VALUE_8_BIT(24),
316 &li.bd_gravity_affects_all, TRUE
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
328 // (these values are the same for each player)
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
332 &li.block_last_field, FALSE // default case for EM levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
337 &li.sp_block_last_field, TRUE // default case for SP levels
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
342 &li.instant_relocation, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
347 &li.can_pass_to_walkable, FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
352 &li.block_snap_field, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
357 &li.continuous_snapping, TRUE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
362 &li.shifted_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
367 &li.lazy_relocation, FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
372 &li.finish_dig_collect, TRUE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
377 &li.keep_walkable_ce, FALSE
380 // (these values are different for each player)
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[0], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[0], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[0], EL_PLAYER_1
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[0], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[0], EL_PLAYER_1
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[0], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[0], EL_PLAYER_1
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[0], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[0], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[1], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[1], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[1], EL_PLAYER_2
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[1], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[1], EL_PLAYER_2
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[1], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[1], EL_PLAYER_2
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[1], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[1], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[2], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[2], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[2], EL_PLAYER_3
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[2], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[2], EL_PLAYER_3
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[2], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[2], EL_PLAYER_3
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[2], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[2], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
555 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
560 &li.initial_player_gravity[3], FALSE
564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
565 &li.use_start_element[3], FALSE
569 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
570 &li.start_element[3], EL_PLAYER_4
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
575 &li.use_artwork_element[3], FALSE
579 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
580 &li.artwork_element[3], EL_PLAYER_4
584 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
585 &li.use_explosion_element[3], FALSE
589 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
590 &li.explosion_element[3], EL_PLAYER_4
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
595 &li.use_initial_inventory[3], FALSE
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
600 &li.initial_inventory_size[3], 1
604 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
605 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
609 // (these values are only valid for BD style levels)
612 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
613 &li.bd_diagonal_movements, FALSE
617 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
618 &li.bd_topmost_player_active, TRUE
623 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
624 &li.score[SC_DIAMOND_EXTRA], 20
627 // (the following values are related to various game elements)
631 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
632 &li.score[SC_EMERALD], 10
637 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
638 &li.score[SC_DIAMOND], 10
643 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
644 &li.score[SC_BUG], 10
649 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
650 &li.score[SC_SPACESHIP], 10
655 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
656 &li.score[SC_PACMAN], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
662 &li.score[SC_NUT], 10
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
668 &li.score[SC_DYNAMITE], 10
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.score[SC_KEY], 10
679 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
680 &li.score[SC_PEARL], 10
685 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
686 &li.score[SC_CRYSTAL], 10
691 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
692 &li.amoeba_content, EL_DIAMOND
696 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
701 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
702 &li.grow_into_diggable, TRUE
707 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
708 &li.yamyam_content, EL_ROCK, NULL,
709 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
713 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
714 &li.score[SC_YAMYAM], 10
719 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
720 &li.score[SC_ROBOT], 10
724 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
730 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
736 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
737 &li.time_magic_wall, 10
742 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
743 &li.game_of_life[0], 2
747 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
748 &li.game_of_life[1], 3
752 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
753 &li.game_of_life[2], 3
757 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
758 &li.game_of_life[3], 3
762 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
763 &li.use_life_bugs, FALSE
768 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
773 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
778 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
783 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
788 EL_TIMEGATE_SWITCH, -1,
789 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
790 &li.time_timegate, 10
794 EL_LIGHT_SWITCH_ACTIVE, -1,
795 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
800 EL_SHIELD_NORMAL, -1,
801 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
802 &li.shield_normal_time, 10
805 EL_SHIELD_NORMAL, -1,
806 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
807 &li.score[SC_SHIELD], 10
811 EL_SHIELD_DEADLY, -1,
812 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
813 &li.shield_deadly_time, 10
816 EL_SHIELD_DEADLY, -1,
817 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
818 &li.score[SC_SHIELD], 10
823 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
828 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
829 &li.extra_time_score, 10
833 EL_TIME_ORB_FULL, -1,
834 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
835 &li.time_orb_time, 10
838 EL_TIME_ORB_FULL, -1,
839 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
840 &li.use_time_orb_bug, FALSE
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
846 &li.use_spring_bug, FALSE
851 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
852 &li.android_move_time, 10
856 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
857 &li.android_clone_time, 10
860 EL_EMC_ANDROID, SAVE_CONF_NEVER,
861 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
862 &li.android_clone_element[0], EL_EMPTY, NULL,
863 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
867 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
868 &li.android_clone_element[0], EL_EMPTY, NULL,
869 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
874 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
879 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
884 EL_EMC_MAGNIFIER, -1,
885 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
886 &li.magnify_score, 10
889 EL_EMC_MAGNIFIER, -1,
890 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
895 EL_EMC_MAGIC_BALL, -1,
896 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
900 EL_EMC_MAGIC_BALL, -1,
901 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
902 &li.ball_random, FALSE
905 EL_EMC_MAGIC_BALL, -1,
906 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
907 &li.ball_active_initial, FALSE
910 EL_EMC_MAGIC_BALL, -1,
911 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
912 &li.ball_content, EL_EMPTY, NULL,
913 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
917 EL_SOKOBAN_FIELD_EMPTY, -1,
918 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
919 &li.sb_fields_needed, TRUE
923 EL_SOKOBAN_OBJECT, -1,
924 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
925 &li.sb_objects_needed, TRUE
930 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
931 &li.mm_laser_red, FALSE
935 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
936 &li.mm_laser_green, FALSE
940 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
941 &li.mm_laser_blue, TRUE
946 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
947 &li.df_laser_red, TRUE
951 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
952 &li.df_laser_green, TRUE
956 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
957 &li.df_laser_blue, FALSE
961 EL_MM_FUSE_ACTIVE, -1,
962 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
967 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
973 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
978 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
979 &li.mm_ball_choice_mode, ANIM_RANDOM
983 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
984 &li.mm_ball_content, EL_EMPTY, NULL,
985 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
989 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
990 &li.rotate_mm_ball_content, TRUE
994 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
995 &li.explode_mm_ball, FALSE
999 EL_MM_STEEL_BLOCK, -1,
1000 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1001 &li.mm_time_block, 75
1004 EL_MM_LIGHTBALL, -1,
1005 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1006 &li.score[SC_ELEM_BONUS], 10
1016 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1020 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1021 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1025 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1026 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1031 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1032 &xx_envelope.autowrap, FALSE
1036 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1037 &xx_envelope.centered, FALSE
1042 TYPE_STRING, CONF_VALUE_BYTES(1),
1043 &xx_envelope.text, -1, NULL,
1044 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1045 &xx_default_string_empty[0]
1055 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1059 TYPE_STRING, CONF_VALUE_BYTES(1),
1060 &xx_ei.description[0], -1,
1061 &yy_ei.description[0],
1062 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1063 &xx_default_description[0]
1068 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1069 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1070 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1072 #if ENABLE_RESERVED_CODE
1073 // (reserved for later use)
1076 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1077 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1078 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1084 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1085 &xx_ei.use_gfx_element, FALSE,
1086 &yy_ei.use_gfx_element
1090 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1091 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1092 &yy_ei.gfx_element_initial
1097 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1098 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1099 &yy_ei.access_direction
1104 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1105 &xx_ei.collect_score_initial, 10,
1106 &yy_ei.collect_score_initial
1110 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1111 &xx_ei.collect_count_initial, 1,
1112 &yy_ei.collect_count_initial
1117 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1118 &xx_ei.ce_value_fixed_initial, 0,
1119 &yy_ei.ce_value_fixed_initial
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1124 &xx_ei.ce_value_random_initial, 0,
1125 &yy_ei.ce_value_random_initial
1129 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1130 &xx_ei.use_last_ce_value, FALSE,
1131 &yy_ei.use_last_ce_value
1136 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1137 &xx_ei.push_delay_fixed, 8,
1138 &yy_ei.push_delay_fixed
1142 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1143 &xx_ei.push_delay_random, 8,
1144 &yy_ei.push_delay_random
1148 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1149 &xx_ei.drop_delay_fixed, 0,
1150 &yy_ei.drop_delay_fixed
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1155 &xx_ei.drop_delay_random, 0,
1156 &yy_ei.drop_delay_random
1160 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1161 &xx_ei.move_delay_fixed, 0,
1162 &yy_ei.move_delay_fixed
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1167 &xx_ei.move_delay_random, 0,
1168 &yy_ei.move_delay_random
1172 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1173 &xx_ei.step_delay_fixed, 0,
1174 &yy_ei.step_delay_fixed
1178 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1179 &xx_ei.step_delay_random, 0,
1180 &yy_ei.step_delay_random
1185 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1186 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1191 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1192 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1193 &yy_ei.move_direction_initial
1197 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1198 &xx_ei.move_stepsize, TILEX / 8,
1199 &yy_ei.move_stepsize
1204 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1205 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1206 &yy_ei.move_enter_element
1210 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1211 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1212 &yy_ei.move_leave_element
1216 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1217 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1218 &yy_ei.move_leave_type
1223 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1224 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1225 &yy_ei.slippery_type
1230 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1231 &xx_ei.explosion_type, EXPLODES_3X3,
1232 &yy_ei.explosion_type
1236 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1237 &xx_ei.explosion_delay, 16,
1238 &yy_ei.explosion_delay
1242 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1243 &xx_ei.ignition_delay, 8,
1244 &yy_ei.ignition_delay
1249 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1250 &xx_ei.content, EL_EMPTY_SPACE,
1252 &xx_num_contents, 1, 1
1255 // ---------- "num_change_pages" must be the last entry ---------------------
1258 -1, SAVE_CONF_ALWAYS,
1259 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1260 &xx_ei.num_change_pages, 1,
1261 &yy_ei.num_change_pages
1272 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1274 // ---------- "current_change_page" must be the first entry -----------------
1277 -1, SAVE_CONF_ALWAYS,
1278 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1279 &xx_current_change_page, -1
1282 // ---------- (the remaining entries can be in any order) -------------------
1286 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1287 &xx_change.can_change, FALSE
1292 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1293 &xx_event_bits[0], 0
1297 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1298 &xx_event_bits[1], 0
1303 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1304 &xx_change.trigger_player, CH_PLAYER_ANY
1308 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1309 &xx_change.trigger_side, CH_SIDE_ANY
1313 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1314 &xx_change.trigger_page, CH_PAGE_ANY
1319 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1320 &xx_change.target_element, EL_EMPTY_SPACE
1325 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1326 &xx_change.delay_fixed, 0
1330 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1331 &xx_change.delay_random, 0
1335 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1336 &xx_change.delay_frames, FRAMES_PER_SECOND
1341 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1342 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1347 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1348 &xx_change.explode, FALSE
1352 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1353 &xx_change.use_target_content, FALSE
1357 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1358 &xx_change.only_if_complete, FALSE
1362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1363 &xx_change.use_random_replace, FALSE
1367 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1368 &xx_change.random_percentage, 100
1372 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1373 &xx_change.replace_when, CP_WHEN_EMPTY
1378 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1379 &xx_change.has_action, FALSE
1383 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1384 &xx_change.action_type, CA_NO_ACTION
1388 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1389 &xx_change.action_mode, CA_MODE_UNDEFINED
1393 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1394 &xx_change.action_arg, CA_ARG_UNDEFINED
1399 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1400 &xx_change.action_element, EL_EMPTY_SPACE
1405 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1406 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1407 &xx_num_contents, 1, 1
1417 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1421 TYPE_STRING, CONF_VALUE_BYTES(1),
1422 &xx_ei.description[0], -1, NULL,
1423 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1424 &xx_default_description[0]
1429 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1430 &xx_ei.use_gfx_element, FALSE
1434 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1435 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1440 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1441 &xx_group.choice_mode, ANIM_RANDOM
1446 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1447 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1448 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1458 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1463 &xx_ei.use_gfx_element, FALSE
1467 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1468 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1478 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1482 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1483 &li.block_snap_field, TRUE
1487 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1488 &li.continuous_snapping, TRUE
1492 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1493 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1497 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1498 &li.use_start_element[0], FALSE
1502 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1503 &li.start_element[0], EL_PLAYER_1
1507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1508 &li.use_artwork_element[0], FALSE
1512 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1513 &li.artwork_element[0], EL_PLAYER_1
1517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1518 &li.use_explosion_element[0], FALSE
1522 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1523 &li.explosion_element[0], EL_PLAYER_1
1538 filetype_id_list[] =
1540 { LEVEL_FILE_TYPE_RND, "RND" },
1541 { LEVEL_FILE_TYPE_BD, "BD" },
1542 { LEVEL_FILE_TYPE_EM, "EM" },
1543 { LEVEL_FILE_TYPE_SP, "SP" },
1544 { LEVEL_FILE_TYPE_DX, "DX" },
1545 { LEVEL_FILE_TYPE_SB, "SB" },
1546 { LEVEL_FILE_TYPE_DC, "DC" },
1547 { LEVEL_FILE_TYPE_MM, "MM" },
1548 { LEVEL_FILE_TYPE_MM, "DF" },
1553 // ============================================================================
1554 // level file functions
1555 // ============================================================================
1557 static boolean check_special_flags(char *flag)
1559 if (strEqual(options.special_flags, flag) ||
1560 strEqual(leveldir_current->special_flags, flag))
1566 static struct DateInfo getCurrentDate(void)
1568 time_t epoch_seconds = time(NULL);
1569 struct tm *now = localtime(&epoch_seconds);
1570 struct DateInfo date;
1572 date.year = now->tm_year + 1900;
1573 date.month = now->tm_mon + 1;
1574 date.day = now->tm_mday;
1576 date.src = DATE_SRC_CLOCK;
1581 static void resetEventFlags(struct ElementChangeInfo *change)
1585 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1586 change->has_event[i] = FALSE;
1589 static void resetEventBits(void)
1593 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1594 xx_event_bits[i] = 0;
1597 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1601 /* important: only change event flag if corresponding event bit is set
1602 (this is because all xx_event_bits[] values are loaded separately,
1603 and all xx_event_bits[] values are set back to zero before loading
1604 another value xx_event_bits[x] (each value representing 32 flags)) */
1606 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1607 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1608 change->has_event[i] = TRUE;
1611 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1615 /* in contrast to the above function setEventFlagsFromEventBits(), it
1616 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1617 depending on the corresponding change->has_event[i] values here, as
1618 all xx_event_bits[] values are reset in resetEventBits() before */
1620 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1621 if (change->has_event[i])
1622 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1625 static char *getDefaultElementDescription(struct ElementInfo *ei)
1627 static char description[MAX_ELEMENT_NAME_LEN + 1];
1628 char *default_description = (ei->custom_description != NULL ?
1629 ei->custom_description :
1630 ei->editor_description);
1633 // always start with reliable default values
1634 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1635 description[i] = '\0';
1637 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1638 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1640 return &description[0];
1643 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1645 char *default_description = getDefaultElementDescription(ei);
1648 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1649 ei->description[i] = default_description[i];
1652 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1656 for (i = 0; conf[i].data_type != -1; i++)
1658 int default_value = conf[i].default_value;
1659 int data_type = conf[i].data_type;
1660 int conf_type = conf[i].conf_type;
1661 int byte_mask = conf_type & CONF_MASK_BYTES;
1663 if (byte_mask == CONF_MASK_MULTI_BYTES)
1665 int default_num_entities = conf[i].default_num_entities;
1666 int max_num_entities = conf[i].max_num_entities;
1668 *(int *)(conf[i].num_entities) = default_num_entities;
1670 if (data_type == TYPE_STRING)
1672 char *default_string = conf[i].default_string;
1673 char *string = (char *)(conf[i].value);
1675 strncpy(string, default_string, max_num_entities);
1677 else if (data_type == TYPE_ELEMENT_LIST)
1679 int *element_array = (int *)(conf[i].value);
1682 for (j = 0; j < max_num_entities; j++)
1683 element_array[j] = default_value;
1685 else if (data_type == TYPE_CONTENT_LIST)
1687 struct Content *content = (struct Content *)(conf[i].value);
1690 for (c = 0; c < max_num_entities; c++)
1691 for (y = 0; y < 3; y++)
1692 for (x = 0; x < 3; x++)
1693 content[c].e[x][y] = default_value;
1696 else // constant size configuration data (1, 2 or 4 bytes)
1698 if (data_type == TYPE_BOOLEAN)
1699 *(boolean *)(conf[i].value) = default_value;
1701 *(int *) (conf[i].value) = default_value;
1706 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1710 for (i = 0; conf[i].data_type != -1; i++)
1712 int data_type = conf[i].data_type;
1713 int conf_type = conf[i].conf_type;
1714 int byte_mask = conf_type & CONF_MASK_BYTES;
1716 if (byte_mask == CONF_MASK_MULTI_BYTES)
1718 int max_num_entities = conf[i].max_num_entities;
1720 if (data_type == TYPE_STRING)
1722 char *string = (char *)(conf[i].value);
1723 char *string_copy = (char *)(conf[i].value_copy);
1725 strncpy(string_copy, string, max_num_entities);
1727 else if (data_type == TYPE_ELEMENT_LIST)
1729 int *element_array = (int *)(conf[i].value);
1730 int *element_array_copy = (int *)(conf[i].value_copy);
1733 for (j = 0; j < max_num_entities; j++)
1734 element_array_copy[j] = element_array[j];
1736 else if (data_type == TYPE_CONTENT_LIST)
1738 struct Content *content = (struct Content *)(conf[i].value);
1739 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1742 for (c = 0; c < max_num_entities; c++)
1743 for (y = 0; y < 3; y++)
1744 for (x = 0; x < 3; x++)
1745 content_copy[c].e[x][y] = content[c].e[x][y];
1748 else // constant size configuration data (1, 2 or 4 bytes)
1750 if (data_type == TYPE_BOOLEAN)
1751 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1753 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1758 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1762 xx_ei = *ei_from; // copy element data into temporary buffer
1763 yy_ei = *ei_to; // copy element data into temporary buffer
1765 copyConfigFromConfigList(chunk_config_CUSX_base);
1770 // ---------- reinitialize and copy change pages ----------
1772 ei_to->num_change_pages = ei_from->num_change_pages;
1773 ei_to->current_change_page = ei_from->current_change_page;
1775 setElementChangePages(ei_to, ei_to->num_change_pages);
1777 for (i = 0; i < ei_to->num_change_pages; i++)
1778 ei_to->change_page[i] = ei_from->change_page[i];
1780 // ---------- copy group element info ----------
1781 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1782 *ei_to->group = *ei_from->group;
1784 // mark this custom element as modified
1785 ei_to->modified_settings = TRUE;
1788 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1790 int change_page_size = sizeof(struct ElementChangeInfo);
1792 ei->num_change_pages = MAX(1, change_pages);
1795 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1797 if (ei->current_change_page >= ei->num_change_pages)
1798 ei->current_change_page = ei->num_change_pages - 1;
1800 ei->change = &ei->change_page[ei->current_change_page];
1803 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1805 xx_change = *change; // copy change data into temporary buffer
1807 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1809 *change = xx_change;
1811 resetEventFlags(change);
1813 change->direct_action = 0;
1814 change->other_action = 0;
1816 change->pre_change_function = NULL;
1817 change->change_function = NULL;
1818 change->post_change_function = NULL;
1821 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1825 li = *level; // copy level data into temporary buffer
1826 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1827 *level = li; // copy temporary buffer back to level data
1829 setLevelInfoToDefaults_BD();
1830 setLevelInfoToDefaults_EM();
1831 setLevelInfoToDefaults_SP();
1832 setLevelInfoToDefaults_MM();
1834 level->native_bd_level = &native_bd_level;
1835 level->native_em_level = &native_em_level;
1836 level->native_sp_level = &native_sp_level;
1837 level->native_mm_level = &native_mm_level;
1839 level->file_version = FILE_VERSION_ACTUAL;
1840 level->game_version = GAME_VERSION_ACTUAL;
1842 level->creation_date = getCurrentDate();
1844 level->encoding_16bit_field = TRUE;
1845 level->encoding_16bit_yamyam = TRUE;
1846 level->encoding_16bit_amoeba = TRUE;
1848 // clear level name and level author string buffers
1849 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1850 level->name[i] = '\0';
1851 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1852 level->author[i] = '\0';
1854 // set level name and level author to default values
1855 strcpy(level->name, NAMELESS_LEVEL_NAME);
1856 strcpy(level->author, ANONYMOUS_NAME);
1858 // set level playfield to playable default level with player and exit
1859 for (x = 0; x < MAX_LEV_FIELDX; x++)
1860 for (y = 0; y < MAX_LEV_FIELDY; y++)
1861 level->field[x][y] = EL_SAND;
1863 level->field[0][0] = EL_PLAYER_1;
1864 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1866 BorderElement = EL_STEELWALL;
1868 // detect custom elements when loading them
1869 level->file_has_custom_elements = FALSE;
1871 // set all bug compatibility flags to "false" => do not emulate this bug
1872 level->use_action_after_change_bug = FALSE;
1874 if (leveldir_current)
1876 // try to determine better author name than 'anonymous'
1877 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1879 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1880 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1884 switch (LEVELCLASS(leveldir_current))
1886 case LEVELCLASS_TUTORIAL:
1887 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1890 case LEVELCLASS_CONTRIB:
1891 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1892 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1895 case LEVELCLASS_PRIVATE:
1896 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1897 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1901 // keep default value
1908 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1910 static boolean clipboard_elements_initialized = FALSE;
1913 InitElementPropertiesStatic();
1915 li = *level; // copy level data into temporary buffer
1916 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1917 *level = li; // copy temporary buffer back to level data
1919 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1922 struct ElementInfo *ei = &element_info[element];
1924 if (element == EL_MM_GRAY_BALL)
1926 struct LevelInfo_MM *level_mm = level->native_mm_level;
1929 for (j = 0; j < level->num_mm_ball_contents; j++)
1930 level->mm_ball_content[j] =
1931 map_element_MM_to_RND(level_mm->ball_content[j]);
1934 // never initialize clipboard elements after the very first time
1935 // (to be able to use clipboard elements between several levels)
1936 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1939 if (IS_ENVELOPE(element))
1941 int envelope_nr = element - EL_ENVELOPE_1;
1943 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1945 level->envelope[envelope_nr] = xx_envelope;
1948 if (IS_CUSTOM_ELEMENT(element) ||
1949 IS_GROUP_ELEMENT(element) ||
1950 IS_INTERNAL_ELEMENT(element))
1952 xx_ei = *ei; // copy element data into temporary buffer
1954 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1959 setElementChangePages(ei, 1);
1960 setElementChangeInfoToDefaults(ei->change);
1962 if (IS_CUSTOM_ELEMENT(element) ||
1963 IS_GROUP_ELEMENT(element))
1965 setElementDescriptionToDefault(ei);
1967 ei->modified_settings = FALSE;
1970 if (IS_CUSTOM_ELEMENT(element) ||
1971 IS_INTERNAL_ELEMENT(element))
1973 // internal values used in level editor
1975 ei->access_type = 0;
1976 ei->access_layer = 0;
1977 ei->access_protected = 0;
1978 ei->walk_to_action = 0;
1979 ei->smash_targets = 0;
1982 ei->can_explode_by_fire = FALSE;
1983 ei->can_explode_smashed = FALSE;
1984 ei->can_explode_impact = FALSE;
1986 ei->current_change_page = 0;
1989 if (IS_GROUP_ELEMENT(element) ||
1990 IS_INTERNAL_ELEMENT(element))
1992 struct ElementGroupInfo *group;
1994 // initialize memory for list of elements in group
1995 if (ei->group == NULL)
1996 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2000 xx_group = *group; // copy group data into temporary buffer
2002 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2007 if (IS_EMPTY_ELEMENT(element) ||
2008 IS_INTERNAL_ELEMENT(element))
2010 xx_ei = *ei; // copy element data into temporary buffer
2012 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2018 clipboard_elements_initialized = TRUE;
2021 static void setLevelInfoToDefaults(struct LevelInfo *level,
2022 boolean level_info_only,
2023 boolean reset_file_status)
2025 setLevelInfoToDefaults_Level(level);
2027 if (!level_info_only)
2028 setLevelInfoToDefaults_Elements(level);
2030 if (reset_file_status)
2032 level->no_valid_file = FALSE;
2033 level->no_level_file = FALSE;
2036 level->changed = FALSE;
2039 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2041 level_file_info->nr = 0;
2042 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2043 level_file_info->packed = FALSE;
2045 setString(&level_file_info->basename, NULL);
2046 setString(&level_file_info->filename, NULL);
2049 int getMappedElement_SB(int, boolean);
2051 static void ActivateLevelTemplate(void)
2055 if (check_special_flags("load_xsb_to_ces"))
2057 // fill smaller playfields with padding "beyond border wall" elements
2058 if (level.fieldx < level_template.fieldx ||
2059 level.fieldy < level_template.fieldy)
2061 short field[level.fieldx][level.fieldy];
2062 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2063 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2064 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2065 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2067 // copy old playfield (which is smaller than the visible area)
2068 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2069 field[x][y] = level.field[x][y];
2071 // fill new, larger playfield with "beyond border wall" elements
2072 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2073 level.field[x][y] = getMappedElement_SB('_', TRUE);
2075 // copy the old playfield to the middle of the new playfield
2076 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2077 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2079 level.fieldx = new_fieldx;
2080 level.fieldy = new_fieldy;
2084 // Currently there is no special action needed to activate the template
2085 // data, because 'element_info' property settings overwrite the original
2086 // level data, while all other variables do not change.
2088 // Exception: 'from_level_template' elements in the original level playfield
2089 // are overwritten with the corresponding elements at the same position in
2090 // playfield from the level template.
2092 for (x = 0; x < level.fieldx; x++)
2093 for (y = 0; y < level.fieldy; y++)
2094 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2095 level.field[x][y] = level_template.field[x][y];
2097 if (check_special_flags("load_xsb_to_ces"))
2099 struct LevelInfo level_backup = level;
2101 // overwrite all individual level settings from template level settings
2102 level = level_template;
2104 // restore level file info
2105 level.file_info = level_backup.file_info;
2107 // restore playfield size
2108 level.fieldx = level_backup.fieldx;
2109 level.fieldy = level_backup.fieldy;
2111 // restore playfield content
2112 for (x = 0; x < level.fieldx; x++)
2113 for (y = 0; y < level.fieldy; y++)
2114 level.field[x][y] = level_backup.field[x][y];
2116 // restore name and author from individual level
2117 strcpy(level.name, level_backup.name);
2118 strcpy(level.author, level_backup.author);
2120 // restore flag "use_custom_template"
2121 level.use_custom_template = level_backup.use_custom_template;
2125 static boolean checkForPackageFromBasename_BD(char *basename)
2127 // check for native BD level file extensions
2128 if (!strSuffixLower(basename, ".bd") &&
2129 !strSuffixLower(basename, ".bdr") &&
2130 !strSuffixLower(basename, ".brc") &&
2131 !strSuffixLower(basename, ".gds"))
2134 // check for standard single-level BD files (like "001.bd")
2135 if (strSuffixLower(basename, ".bd") &&
2136 strlen(basename) == 6 &&
2137 basename[0] >= '0' && basename[0] <= '9' &&
2138 basename[1] >= '0' && basename[1] <= '9' &&
2139 basename[2] >= '0' && basename[2] <= '9')
2142 // this is a level package in native BD file format
2146 static char *getLevelFilenameFromBasename(char *basename)
2148 static char *filename = NULL;
2150 checked_free(filename);
2152 filename = getPath2(getCurrentLevelDir(), basename);
2157 static int getFileTypeFromBasename(char *basename)
2159 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2161 static char *filename = NULL;
2162 struct stat file_status;
2164 // ---------- try to determine file type from filename ----------
2166 // check for typical filename of a Supaplex level package file
2167 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2168 return LEVEL_FILE_TYPE_SP;
2170 // check for typical filename of a Diamond Caves II level package file
2171 if (strSuffixLower(basename, ".dc") ||
2172 strSuffixLower(basename, ".dc2"))
2173 return LEVEL_FILE_TYPE_DC;
2175 // check for typical filename of a Sokoban level package file
2176 if (strSuffixLower(basename, ".xsb") &&
2177 strchr(basename, '%') == NULL)
2178 return LEVEL_FILE_TYPE_SB;
2180 // check for typical filename of a Boulder Dash (GDash) level package file
2181 if (checkForPackageFromBasename_BD(basename))
2182 return LEVEL_FILE_TYPE_BD;
2184 // ---------- try to determine file type from filesize ----------
2186 checked_free(filename);
2187 filename = getPath2(getCurrentLevelDir(), basename);
2189 if (stat(filename, &file_status) == 0)
2191 // check for typical filesize of a Supaplex level package file
2192 if (file_status.st_size == 170496)
2193 return LEVEL_FILE_TYPE_SP;
2196 return LEVEL_FILE_TYPE_UNKNOWN;
2199 static int getFileTypeFromMagicBytes(char *filename, int type)
2203 if ((file = openFile(filename, MODE_READ)))
2205 char chunk_name[CHUNK_ID_LEN + 1];
2207 getFileChunkBE(file, chunk_name, NULL);
2209 if (strEqual(chunk_name, "MMII") ||
2210 strEqual(chunk_name, "MIRR"))
2211 type = LEVEL_FILE_TYPE_MM;
2219 static boolean checkForPackageFromBasename(char *basename)
2221 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2222 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2224 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2227 static char *getSingleLevelBasenameExt(int nr, char *extension)
2229 static char basename[MAX_FILENAME_LEN];
2232 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2234 sprintf(basename, "%03d.%s", nr, extension);
2239 static char *getSingleLevelBasename(int nr)
2241 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2244 static char *getPackedLevelBasename(int type)
2246 static char basename[MAX_FILENAME_LEN];
2247 char *directory = getCurrentLevelDir();
2249 DirectoryEntry *dir_entry;
2251 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2253 if ((dir = openDirectory(directory)) == NULL)
2255 Warn("cannot read current level directory '%s'", directory);
2260 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2262 char *entry_basename = dir_entry->basename;
2263 int entry_type = getFileTypeFromBasename(entry_basename);
2265 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2267 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2270 strcpy(basename, entry_basename);
2277 closeDirectory(dir);
2282 static char *getSingleLevelFilename(int nr)
2284 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2287 #if ENABLE_UNUSED_CODE
2288 static char *getPackedLevelFilename(int type)
2290 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2294 char *getDefaultLevelFilename(int nr)
2296 return getSingleLevelFilename(nr);
2299 #if ENABLE_UNUSED_CODE
2300 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2304 lfi->packed = FALSE;
2306 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2307 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2311 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2312 int type, char *format, ...)
2314 static char basename[MAX_FILENAME_LEN];
2317 va_start(ap, format);
2318 vsprintf(basename, format, ap);
2322 lfi->packed = FALSE;
2324 setString(&lfi->basename, basename);
2325 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2328 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2334 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2335 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2338 static int getFiletypeFromID(char *filetype_id)
2340 char *filetype_id_lower;
2341 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2344 if (filetype_id == NULL)
2345 return LEVEL_FILE_TYPE_UNKNOWN;
2347 filetype_id_lower = getStringToLower(filetype_id);
2349 for (i = 0; filetype_id_list[i].id != NULL; i++)
2351 char *id_lower = getStringToLower(filetype_id_list[i].id);
2353 if (strEqual(filetype_id_lower, id_lower))
2354 filetype = filetype_id_list[i].filetype;
2358 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2362 free(filetype_id_lower);
2367 char *getLocalLevelTemplateFilename(void)
2369 return getDefaultLevelFilename(-1);
2372 char *getGlobalLevelTemplateFilename(void)
2374 // global variable "leveldir_current" must be modified in the loop below
2375 LevelDirTree *leveldir_current_last = leveldir_current;
2376 char *filename = NULL;
2378 // check for template level in path from current to topmost tree node
2380 while (leveldir_current != NULL)
2382 filename = getDefaultLevelFilename(-1);
2384 if (fileExists(filename))
2387 leveldir_current = leveldir_current->node_parent;
2390 // restore global variable "leveldir_current" modified in above loop
2391 leveldir_current = leveldir_current_last;
2396 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2400 // special case: level number is negative => check for level template file
2403 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2404 getSingleLevelBasename(-1));
2406 // replace local level template filename with global template filename
2407 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2409 // no fallback if template file not existing
2413 // special case: check for file name/pattern specified in "levelinfo.conf"
2414 if (leveldir_current->level_filename != NULL)
2416 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2418 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2419 leveldir_current->level_filename, nr);
2421 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2423 if (fileExists(lfi->filename))
2426 else if (leveldir_current->level_filetype != NULL)
2428 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2430 // check for specified native level file with standard file name
2431 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2432 "%03d.%s", nr, LEVELFILE_EXTENSION);
2433 if (fileExists(lfi->filename))
2437 // check for native Rocks'n'Diamonds level file
2438 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2439 "%03d.%s", nr, LEVELFILE_EXTENSION);
2440 if (fileExists(lfi->filename))
2443 // check for native Boulder Dash level file
2444 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2445 if (fileExists(lfi->filename))
2448 // check for Emerald Mine level file (V1)
2449 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2450 'a' + (nr / 10) % 26, '0' + nr % 10);
2451 if (fileExists(lfi->filename))
2453 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2454 'A' + (nr / 10) % 26, '0' + nr % 10);
2455 if (fileExists(lfi->filename))
2458 // check for Emerald Mine level file (V2 to V5)
2459 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2460 if (fileExists(lfi->filename))
2463 // check for Emerald Mine level file (V6 / single mode)
2464 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2465 if (fileExists(lfi->filename))
2467 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2468 if (fileExists(lfi->filename))
2471 // check for Emerald Mine level file (V6 / teamwork mode)
2472 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2473 if (fileExists(lfi->filename))
2475 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2476 if (fileExists(lfi->filename))
2479 // check for various packed level file formats
2480 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2481 if (fileExists(lfi->filename))
2484 // no known level file found -- use default values (and fail later)
2485 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2486 "%03d.%s", nr, LEVELFILE_EXTENSION);
2489 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2491 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2492 lfi->type = getFileTypeFromBasename(lfi->basename);
2494 if (lfi->type == LEVEL_FILE_TYPE_RND)
2495 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2498 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2500 // always start with reliable default values
2501 setFileInfoToDefaults(level_file_info);
2503 level_file_info->nr = nr; // set requested level number
2505 determineLevelFileInfo_Filename(level_file_info);
2506 determineLevelFileInfo_Filetype(level_file_info);
2509 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2510 struct LevelFileInfo *lfi_to)
2512 lfi_to->nr = lfi_from->nr;
2513 lfi_to->type = lfi_from->type;
2514 lfi_to->packed = lfi_from->packed;
2516 setString(&lfi_to->basename, lfi_from->basename);
2517 setString(&lfi_to->filename, lfi_from->filename);
2520 // ----------------------------------------------------------------------------
2521 // functions for loading R'n'D level
2522 // ----------------------------------------------------------------------------
2524 int getMappedElement(int element)
2526 // remap some (historic, now obsolete) elements
2530 case EL_PLAYER_OBSOLETE:
2531 element = EL_PLAYER_1;
2534 case EL_KEY_OBSOLETE:
2538 case EL_EM_KEY_1_FILE_OBSOLETE:
2539 element = EL_EM_KEY_1;
2542 case EL_EM_KEY_2_FILE_OBSOLETE:
2543 element = EL_EM_KEY_2;
2546 case EL_EM_KEY_3_FILE_OBSOLETE:
2547 element = EL_EM_KEY_3;
2550 case EL_EM_KEY_4_FILE_OBSOLETE:
2551 element = EL_EM_KEY_4;
2554 case EL_ENVELOPE_OBSOLETE:
2555 element = EL_ENVELOPE_1;
2563 if (element >= NUM_FILE_ELEMENTS)
2565 Warn("invalid level element %d", element);
2567 element = EL_UNKNOWN;
2575 static int getMappedElementByVersion(int element, int game_version)
2577 // remap some elements due to certain game version
2579 if (game_version <= VERSION_IDENT(2,2,0,0))
2581 // map game font elements
2582 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2583 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2584 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2585 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2588 if (game_version < VERSION_IDENT(3,0,0,0))
2590 // map Supaplex gravity tube elements
2591 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2592 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2593 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2594 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2601 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2603 level->file_version = getFileVersion(file);
2604 level->game_version = getFileVersion(file);
2609 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2611 level->creation_date.year = getFile16BitBE(file);
2612 level->creation_date.month = getFile8Bit(file);
2613 level->creation_date.day = getFile8Bit(file);
2615 level->creation_date.src = DATE_SRC_LEVELFILE;
2620 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2622 int initial_player_stepsize;
2623 int initial_player_gravity;
2626 level->fieldx = getFile8Bit(file);
2627 level->fieldy = getFile8Bit(file);
2629 level->time = getFile16BitBE(file);
2630 level->gems_needed = getFile16BitBE(file);
2632 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2633 level->name[i] = getFile8Bit(file);
2634 level->name[MAX_LEVEL_NAME_LEN] = 0;
2636 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2637 level->score[i] = getFile8Bit(file);
2639 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2640 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2641 for (y = 0; y < 3; y++)
2642 for (x = 0; x < 3; x++)
2643 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2645 level->amoeba_speed = getFile8Bit(file);
2646 level->time_magic_wall = getFile8Bit(file);
2647 level->time_wheel = getFile8Bit(file);
2648 level->amoeba_content = getMappedElement(getFile8Bit(file));
2650 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2653 for (i = 0; i < MAX_PLAYERS; i++)
2654 level->initial_player_stepsize[i] = initial_player_stepsize;
2656 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2658 for (i = 0; i < MAX_PLAYERS; i++)
2659 level->initial_player_gravity[i] = initial_player_gravity;
2661 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2662 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2664 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2666 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2667 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2668 level->can_move_into_acid_bits = getFile32BitBE(file);
2669 level->dont_collide_with_bits = getFile8Bit(file);
2671 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2672 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2674 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2675 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2676 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2678 level->game_engine_type = getFile8Bit(file);
2680 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2685 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2689 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2690 level->name[i] = getFile8Bit(file);
2691 level->name[MAX_LEVEL_NAME_LEN] = 0;
2696 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2700 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2701 level->author[i] = getFile8Bit(file);
2702 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2707 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2710 int chunk_size_expected = level->fieldx * level->fieldy;
2712 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2713 stored with 16-bit encoding (and should be twice as big then).
2714 Even worse, playfield data was stored 16-bit when only yamyam content
2715 contained 16-bit elements and vice versa. */
2717 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2718 chunk_size_expected *= 2;
2720 if (chunk_size_expected != chunk_size)
2722 ReadUnusedBytesFromFile(file, chunk_size);
2723 return chunk_size_expected;
2726 for (y = 0; y < level->fieldy; y++)
2727 for (x = 0; x < level->fieldx; x++)
2728 level->field[x][y] =
2729 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2734 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2737 int header_size = 4;
2738 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2739 int chunk_size_expected = header_size + content_size;
2741 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2742 stored with 16-bit encoding (and should be twice as big then).
2743 Even worse, playfield data was stored 16-bit when only yamyam content
2744 contained 16-bit elements and vice versa. */
2746 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2747 chunk_size_expected += content_size;
2749 if (chunk_size_expected != chunk_size)
2751 ReadUnusedBytesFromFile(file, chunk_size);
2752 return chunk_size_expected;
2756 level->num_yamyam_contents = getFile8Bit(file);
2760 // correct invalid number of content fields -- should never happen
2761 if (level->num_yamyam_contents < 1 ||
2762 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2763 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2765 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2766 for (y = 0; y < 3; y++)
2767 for (x = 0; x < 3; x++)
2768 level->yamyam_content[i].e[x][y] =
2769 getMappedElement(level->encoding_16bit_field ?
2770 getFile16BitBE(file) : getFile8Bit(file));
2774 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2779 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2781 element = getMappedElement(getFile16BitBE(file));
2782 num_contents = getFile8Bit(file);
2784 getFile8Bit(file); // content x size (unused)
2785 getFile8Bit(file); // content y size (unused)
2787 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2789 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2790 for (y = 0; y < 3; y++)
2791 for (x = 0; x < 3; x++)
2792 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2794 // correct invalid number of content fields -- should never happen
2795 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2796 num_contents = STD_ELEMENT_CONTENTS;
2798 if (element == EL_YAMYAM)
2800 level->num_yamyam_contents = num_contents;
2802 for (i = 0; i < num_contents; i++)
2803 for (y = 0; y < 3; y++)
2804 for (x = 0; x < 3; x++)
2805 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2807 else if (element == EL_BD_AMOEBA)
2809 level->amoeba_content = content_array[0][0][0];
2813 Warn("cannot load content for element '%d'", element);
2819 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2825 int chunk_size_expected;
2827 element = getMappedElement(getFile16BitBE(file));
2828 if (!IS_ENVELOPE(element))
2829 element = EL_ENVELOPE_1;
2831 envelope_nr = element - EL_ENVELOPE_1;
2833 envelope_len = getFile16BitBE(file);
2835 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2836 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2838 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2840 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2841 if (chunk_size_expected != chunk_size)
2843 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2844 return chunk_size_expected;
2847 for (i = 0; i < envelope_len; i++)
2848 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2853 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2855 int num_changed_custom_elements = getFile16BitBE(file);
2856 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2859 if (chunk_size_expected != chunk_size)
2861 ReadUnusedBytesFromFile(file, chunk_size - 2);
2862 return chunk_size_expected;
2865 for (i = 0; i < num_changed_custom_elements; i++)
2867 int element = getMappedElement(getFile16BitBE(file));
2868 int properties = getFile32BitBE(file);
2870 if (IS_CUSTOM_ELEMENT(element))
2871 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2873 Warn("invalid custom element number %d", element);
2875 // older game versions that wrote level files with CUS1 chunks used
2876 // different default push delay values (not yet stored in level file)
2877 element_info[element].push_delay_fixed = 2;
2878 element_info[element].push_delay_random = 8;
2881 level->file_has_custom_elements = TRUE;
2886 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2888 int num_changed_custom_elements = getFile16BitBE(file);
2889 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2892 if (chunk_size_expected != chunk_size)
2894 ReadUnusedBytesFromFile(file, chunk_size - 2);
2895 return chunk_size_expected;
2898 for (i = 0; i < num_changed_custom_elements; i++)
2900 int element = getMappedElement(getFile16BitBE(file));
2901 int custom_target_element = getMappedElement(getFile16BitBE(file));
2903 if (IS_CUSTOM_ELEMENT(element))
2904 element_info[element].change->target_element = custom_target_element;
2906 Warn("invalid custom element number %d", element);
2909 level->file_has_custom_elements = TRUE;
2914 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2916 int num_changed_custom_elements = getFile16BitBE(file);
2917 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2920 if (chunk_size_expected != chunk_size)
2922 ReadUnusedBytesFromFile(file, chunk_size - 2);
2923 return chunk_size_expected;
2926 for (i = 0; i < num_changed_custom_elements; i++)
2928 int element = getMappedElement(getFile16BitBE(file));
2929 struct ElementInfo *ei = &element_info[element];
2930 unsigned int event_bits;
2932 if (!IS_CUSTOM_ELEMENT(element))
2934 Warn("invalid custom element number %d", element);
2936 element = EL_INTERNAL_DUMMY;
2939 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2940 ei->description[j] = getFile8Bit(file);
2941 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2943 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2945 // some free bytes for future properties and padding
2946 ReadUnusedBytesFromFile(file, 7);
2948 ei->use_gfx_element = getFile8Bit(file);
2949 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2951 ei->collect_score_initial = getFile8Bit(file);
2952 ei->collect_count_initial = getFile8Bit(file);
2954 ei->push_delay_fixed = getFile16BitBE(file);
2955 ei->push_delay_random = getFile16BitBE(file);
2956 ei->move_delay_fixed = getFile16BitBE(file);
2957 ei->move_delay_random = getFile16BitBE(file);
2959 ei->move_pattern = getFile16BitBE(file);
2960 ei->move_direction_initial = getFile8Bit(file);
2961 ei->move_stepsize = getFile8Bit(file);
2963 for (y = 0; y < 3; y++)
2964 for (x = 0; x < 3; x++)
2965 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2967 // bits 0 - 31 of "has_event[]"
2968 event_bits = getFile32BitBE(file);
2969 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2970 if (event_bits & (1u << j))
2971 ei->change->has_event[j] = TRUE;
2973 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2975 ei->change->delay_fixed = getFile16BitBE(file);
2976 ei->change->delay_random = getFile16BitBE(file);
2977 ei->change->delay_frames = getFile16BitBE(file);
2979 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2981 ei->change->explode = getFile8Bit(file);
2982 ei->change->use_target_content = getFile8Bit(file);
2983 ei->change->only_if_complete = getFile8Bit(file);
2984 ei->change->use_random_replace = getFile8Bit(file);
2986 ei->change->random_percentage = getFile8Bit(file);
2987 ei->change->replace_when = getFile8Bit(file);
2989 for (y = 0; y < 3; y++)
2990 for (x = 0; x < 3; x++)
2991 ei->change->target_content.e[x][y] =
2992 getMappedElement(getFile16BitBE(file));
2994 ei->slippery_type = getFile8Bit(file);
2996 // some free bytes for future properties and padding
2997 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2999 // mark that this custom element has been modified
3000 ei->modified_settings = TRUE;
3003 level->file_has_custom_elements = TRUE;
3008 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3010 struct ElementInfo *ei;
3011 int chunk_size_expected;
3015 // ---------- custom element base property values (96 bytes) ----------------
3017 element = getMappedElement(getFile16BitBE(file));
3019 if (!IS_CUSTOM_ELEMENT(element))
3021 Warn("invalid custom element number %d", element);
3023 ReadUnusedBytesFromFile(file, chunk_size - 2);
3028 ei = &element_info[element];
3030 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3031 ei->description[i] = getFile8Bit(file);
3032 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3034 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3036 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3038 ei->num_change_pages = getFile8Bit(file);
3040 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3041 if (chunk_size_expected != chunk_size)
3043 ReadUnusedBytesFromFile(file, chunk_size - 43);
3044 return chunk_size_expected;
3047 ei->ce_value_fixed_initial = getFile16BitBE(file);
3048 ei->ce_value_random_initial = getFile16BitBE(file);
3049 ei->use_last_ce_value = getFile8Bit(file);
3051 ei->use_gfx_element = getFile8Bit(file);
3052 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3054 ei->collect_score_initial = getFile8Bit(file);
3055 ei->collect_count_initial = getFile8Bit(file);
3057 ei->drop_delay_fixed = getFile8Bit(file);
3058 ei->push_delay_fixed = getFile8Bit(file);
3059 ei->drop_delay_random = getFile8Bit(file);
3060 ei->push_delay_random = getFile8Bit(file);
3061 ei->move_delay_fixed = getFile16BitBE(file);
3062 ei->move_delay_random = getFile16BitBE(file);
3064 // bits 0 - 15 of "move_pattern" ...
3065 ei->move_pattern = getFile16BitBE(file);
3066 ei->move_direction_initial = getFile8Bit(file);
3067 ei->move_stepsize = getFile8Bit(file);
3069 ei->slippery_type = getFile8Bit(file);
3071 for (y = 0; y < 3; y++)
3072 for (x = 0; x < 3; x++)
3073 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3075 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3076 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3077 ei->move_leave_type = getFile8Bit(file);
3079 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3080 ei->move_pattern |= (getFile16BitBE(file) << 16);
3082 ei->access_direction = getFile8Bit(file);
3084 ei->explosion_delay = getFile8Bit(file);
3085 ei->ignition_delay = getFile8Bit(file);
3086 ei->explosion_type = getFile8Bit(file);
3088 // some free bytes for future custom property values and padding
3089 ReadUnusedBytesFromFile(file, 1);
3091 // ---------- change page property values (48 bytes) ------------------------
3093 setElementChangePages(ei, ei->num_change_pages);
3095 for (i = 0; i < ei->num_change_pages; i++)
3097 struct ElementChangeInfo *change = &ei->change_page[i];
3098 unsigned int event_bits;
3100 // always start with reliable default values
3101 setElementChangeInfoToDefaults(change);
3103 // bits 0 - 31 of "has_event[]" ...
3104 event_bits = getFile32BitBE(file);
3105 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3106 if (event_bits & (1u << j))
3107 change->has_event[j] = TRUE;
3109 change->target_element = getMappedElement(getFile16BitBE(file));
3111 change->delay_fixed = getFile16BitBE(file);
3112 change->delay_random = getFile16BitBE(file);
3113 change->delay_frames = getFile16BitBE(file);
3115 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3117 change->explode = getFile8Bit(file);
3118 change->use_target_content = getFile8Bit(file);
3119 change->only_if_complete = getFile8Bit(file);
3120 change->use_random_replace = getFile8Bit(file);
3122 change->random_percentage = getFile8Bit(file);
3123 change->replace_when = getFile8Bit(file);
3125 for (y = 0; y < 3; y++)
3126 for (x = 0; x < 3; x++)
3127 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3129 change->can_change = getFile8Bit(file);
3131 change->trigger_side = getFile8Bit(file);
3133 change->trigger_player = getFile8Bit(file);
3134 change->trigger_page = getFile8Bit(file);
3136 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3137 CH_PAGE_ANY : (1 << change->trigger_page));
3139 change->has_action = getFile8Bit(file);
3140 change->action_type = getFile8Bit(file);
3141 change->action_mode = getFile8Bit(file);
3142 change->action_arg = getFile16BitBE(file);
3144 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3145 event_bits = getFile8Bit(file);
3146 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3147 if (event_bits & (1u << (j - 32)))
3148 change->has_event[j] = TRUE;
3151 // mark this custom element as modified
3152 ei->modified_settings = TRUE;
3154 level->file_has_custom_elements = TRUE;
3159 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3161 struct ElementInfo *ei;
3162 struct ElementGroupInfo *group;
3166 element = getMappedElement(getFile16BitBE(file));
3168 if (!IS_GROUP_ELEMENT(element))
3170 Warn("invalid group element number %d", element);
3172 ReadUnusedBytesFromFile(file, chunk_size - 2);
3177 ei = &element_info[element];
3179 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3180 ei->description[i] = getFile8Bit(file);
3181 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3183 group = element_info[element].group;
3185 group->num_elements = getFile8Bit(file);
3187 ei->use_gfx_element = getFile8Bit(file);
3188 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3190 group->choice_mode = getFile8Bit(file);
3192 // some free bytes for future values and padding
3193 ReadUnusedBytesFromFile(file, 3);
3195 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3196 group->element[i] = getMappedElement(getFile16BitBE(file));
3198 // mark this group element as modified
3199 element_info[element].modified_settings = TRUE;
3201 level->file_has_custom_elements = TRUE;
3206 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3207 int element, int real_element)
3209 int micro_chunk_size = 0;
3210 int conf_type = getFile8Bit(file);
3211 int byte_mask = conf_type & CONF_MASK_BYTES;
3212 boolean element_found = FALSE;
3215 micro_chunk_size += 1;
3217 if (byte_mask == CONF_MASK_MULTI_BYTES)
3219 int num_bytes = getFile16BitBE(file);
3220 byte *buffer = checked_malloc(num_bytes);
3222 ReadBytesFromFile(file, buffer, num_bytes);
3224 for (i = 0; conf[i].data_type != -1; i++)
3226 if (conf[i].element == element &&
3227 conf[i].conf_type == conf_type)
3229 int data_type = conf[i].data_type;
3230 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3231 int max_num_entities = conf[i].max_num_entities;
3233 if (num_entities > max_num_entities)
3235 Warn("truncating number of entities for element %d from %d to %d",
3236 element, num_entities, max_num_entities);
3238 num_entities = max_num_entities;
3241 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3242 data_type == TYPE_CONTENT_LIST))
3244 // for element and content lists, zero entities are not allowed
3245 Warn("found empty list of entities for element %d", element);
3247 // do not set "num_entities" here to prevent reading behind buffer
3249 *(int *)(conf[i].num_entities) = 1; // at least one is required
3253 *(int *)(conf[i].num_entities) = num_entities;
3256 element_found = TRUE;
3258 if (data_type == TYPE_STRING)
3260 char *string = (char *)(conf[i].value);
3263 for (j = 0; j < max_num_entities; j++)
3264 string[j] = (j < num_entities ? buffer[j] : '\0');
3266 else if (data_type == TYPE_ELEMENT_LIST)
3268 int *element_array = (int *)(conf[i].value);
3271 for (j = 0; j < num_entities; j++)
3273 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3275 else if (data_type == TYPE_CONTENT_LIST)
3277 struct Content *content= (struct Content *)(conf[i].value);
3280 for (c = 0; c < num_entities; c++)
3281 for (y = 0; y < 3; y++)
3282 for (x = 0; x < 3; x++)
3283 content[c].e[x][y] =
3284 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3287 element_found = FALSE;
3293 checked_free(buffer);
3295 micro_chunk_size += 2 + num_bytes;
3297 else // constant size configuration data (1, 2 or 4 bytes)
3299 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3300 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3301 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3303 for (i = 0; conf[i].data_type != -1; i++)
3305 if (conf[i].element == element &&
3306 conf[i].conf_type == conf_type)
3308 int data_type = conf[i].data_type;
3310 if (data_type == TYPE_ELEMENT)
3311 value = getMappedElement(value);
3313 if (data_type == TYPE_BOOLEAN)
3314 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3316 *(int *) (conf[i].value) = value;
3318 element_found = TRUE;
3324 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3329 char *error_conf_chunk_bytes =
3330 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3331 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3332 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3333 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3334 int error_element = real_element;
3336 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3337 error_conf_chunk_bytes, error_conf_chunk_token,
3338 error_element, EL_NAME(error_element));
3341 return micro_chunk_size;
3344 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3346 int real_chunk_size = 0;
3348 li = *level; // copy level data into temporary buffer
3350 while (!checkEndOfFile(file))
3352 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3354 if (real_chunk_size >= chunk_size)
3358 *level = li; // copy temporary buffer back to level data
3360 return real_chunk_size;
3363 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3365 int real_chunk_size = 0;
3367 li = *level; // copy level data into temporary buffer
3369 while (!checkEndOfFile(file))
3371 int element = getMappedElement(getFile16BitBE(file));
3373 real_chunk_size += 2;
3374 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3376 if (real_chunk_size >= chunk_size)
3380 *level = li; // copy temporary buffer back to level data
3382 return real_chunk_size;
3385 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3387 int real_chunk_size = 0;
3389 li = *level; // copy level data into temporary buffer
3391 while (!checkEndOfFile(file))
3393 int element = getMappedElement(getFile16BitBE(file));
3395 real_chunk_size += 2;
3396 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3398 if (real_chunk_size >= chunk_size)
3402 *level = li; // copy temporary buffer back to level data
3404 return real_chunk_size;
3407 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3409 int element = getMappedElement(getFile16BitBE(file));
3410 int envelope_nr = element - EL_ENVELOPE_1;
3411 int real_chunk_size = 2;
3413 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3415 while (!checkEndOfFile(file))
3417 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3420 if (real_chunk_size >= chunk_size)
3424 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3426 return real_chunk_size;
3429 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3431 int element = getMappedElement(getFile16BitBE(file));
3432 int real_chunk_size = 2;
3433 struct ElementInfo *ei = &element_info[element];
3436 xx_ei = *ei; // copy element data into temporary buffer
3438 xx_ei.num_change_pages = -1;
3440 while (!checkEndOfFile(file))
3442 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3444 if (xx_ei.num_change_pages != -1)
3447 if (real_chunk_size >= chunk_size)
3453 if (ei->num_change_pages == -1)
3455 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3458 ei->num_change_pages = 1;
3460 setElementChangePages(ei, 1);
3461 setElementChangeInfoToDefaults(ei->change);
3463 return real_chunk_size;
3466 // initialize number of change pages stored for this custom element
3467 setElementChangePages(ei, ei->num_change_pages);
3468 for (i = 0; i < ei->num_change_pages; i++)
3469 setElementChangeInfoToDefaults(&ei->change_page[i]);
3471 // start with reading properties for the first change page
3472 xx_current_change_page = 0;
3474 while (!checkEndOfFile(file))
3476 // level file might contain invalid change page number
3477 if (xx_current_change_page >= ei->num_change_pages)
3480 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3482 xx_change = *change; // copy change data into temporary buffer
3484 resetEventBits(); // reset bits; change page might have changed
3486 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3489 *change = xx_change;
3491 setEventFlagsFromEventBits(change);
3493 if (real_chunk_size >= chunk_size)
3497 level->file_has_custom_elements = TRUE;
3499 return real_chunk_size;
3502 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3504 int element = getMappedElement(getFile16BitBE(file));
3505 int real_chunk_size = 2;
3506 struct ElementInfo *ei = &element_info[element];
3507 struct ElementGroupInfo *group = ei->group;
3512 xx_ei = *ei; // copy element data into temporary buffer
3513 xx_group = *group; // copy group data into temporary buffer
3515 while (!checkEndOfFile(file))
3517 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3520 if (real_chunk_size >= chunk_size)
3527 level->file_has_custom_elements = TRUE;
3529 return real_chunk_size;
3532 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3534 int element = getMappedElement(getFile16BitBE(file));
3535 int real_chunk_size = 2;
3536 struct ElementInfo *ei = &element_info[element];
3538 xx_ei = *ei; // copy element data into temporary buffer
3540 while (!checkEndOfFile(file))
3542 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3545 if (real_chunk_size >= chunk_size)
3551 level->file_has_custom_elements = TRUE;
3553 return real_chunk_size;
3556 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3557 struct LevelFileInfo *level_file_info,
3558 boolean level_info_only)
3560 char *filename = level_file_info->filename;
3561 char cookie[MAX_LINE_LEN];
3562 char chunk_name[CHUNK_ID_LEN + 1];
3566 if (!(file = openFile(filename, MODE_READ)))
3568 level->no_valid_file = TRUE;
3569 level->no_level_file = TRUE;
3571 if (level_info_only)
3574 Warn("cannot read level '%s' -- using empty level", filename);
3576 if (!setup.editor.use_template_for_new_levels)
3579 // if level file not found, try to initialize level data from template
3580 filename = getGlobalLevelTemplateFilename();
3582 if (!(file = openFile(filename, MODE_READ)))
3585 // default: for empty levels, use level template for custom elements
3586 level->use_custom_template = TRUE;
3588 level->no_valid_file = FALSE;
3591 getFileChunkBE(file, chunk_name, NULL);
3592 if (strEqual(chunk_name, "RND1"))
3594 getFile32BitBE(file); // not used
3596 getFileChunkBE(file, chunk_name, NULL);
3597 if (!strEqual(chunk_name, "CAVE"))
3599 level->no_valid_file = TRUE;
3601 Warn("unknown format of level file '%s'", filename);
3608 else // check for pre-2.0 file format with cookie string
3610 strcpy(cookie, chunk_name);
3611 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3613 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3614 cookie[strlen(cookie) - 1] = '\0';
3616 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3618 level->no_valid_file = TRUE;
3620 Warn("unknown format of level file '%s'", filename);
3627 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3629 level->no_valid_file = TRUE;
3631 Warn("unsupported version of level file '%s'", filename);
3638 // pre-2.0 level files have no game version, so use file version here
3639 level->game_version = level->file_version;
3642 if (level->file_version < FILE_VERSION_1_2)
3644 // level files from versions before 1.2.0 without chunk structure
3645 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3646 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3654 int (*loader)(File *, int, struct LevelInfo *);
3658 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3659 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3660 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3661 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3662 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3663 { "INFO", -1, LoadLevel_INFO },
3664 { "BODY", -1, LoadLevel_BODY },
3665 { "CONT", -1, LoadLevel_CONT },
3666 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3667 { "CNT3", -1, LoadLevel_CNT3 },
3668 { "CUS1", -1, LoadLevel_CUS1 },
3669 { "CUS2", -1, LoadLevel_CUS2 },
3670 { "CUS3", -1, LoadLevel_CUS3 },
3671 { "CUS4", -1, LoadLevel_CUS4 },
3672 { "GRP1", -1, LoadLevel_GRP1 },
3673 { "CONF", -1, LoadLevel_CONF },
3674 { "ELEM", -1, LoadLevel_ELEM },
3675 { "NOTE", -1, LoadLevel_NOTE },
3676 { "CUSX", -1, LoadLevel_CUSX },
3677 { "GRPX", -1, LoadLevel_GRPX },
3678 { "EMPX", -1, LoadLevel_EMPX },
3683 while (getFileChunkBE(file, chunk_name, &chunk_size))
3687 while (chunk_info[i].name != NULL &&
3688 !strEqual(chunk_name, chunk_info[i].name))
3691 if (chunk_info[i].name == NULL)
3693 Warn("unknown chunk '%s' in level file '%s'",
3694 chunk_name, filename);
3696 ReadUnusedBytesFromFile(file, chunk_size);
3698 else if (chunk_info[i].size != -1 &&
3699 chunk_info[i].size != chunk_size)
3701 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3702 chunk_size, chunk_name, filename);
3704 ReadUnusedBytesFromFile(file, chunk_size);
3708 // call function to load this level chunk
3709 int chunk_size_expected =
3710 (chunk_info[i].loader)(file, chunk_size, level);
3712 if (chunk_size_expected < 0)
3714 Warn("error reading chunk '%s' in level file '%s'",
3715 chunk_name, filename);
3720 // the size of some chunks cannot be checked before reading other
3721 // chunks first (like "HEAD" and "BODY") that contain some header
3722 // information, so check them here
3723 if (chunk_size_expected != chunk_size)
3725 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3726 chunk_size, chunk_name, filename);
3738 // ----------------------------------------------------------------------------
3739 // functions for loading BD level
3740 // ----------------------------------------------------------------------------
3742 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3744 struct LevelInfo_BD *level_bd = level->native_bd_level;
3745 GdCave *cave = NULL; // will be changed below
3746 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3747 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3750 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3752 // cave and map newly allocated when set to defaults above
3753 cave = level_bd->cave;
3755 for (i = 0; i < 5; i++)
3757 cave->level_time[i] = level->time;
3758 cave->level_diamonds[i] = level->gems_needed;
3759 cave->level_magic_wall_time[i] = level->time_magic_wall;
3761 cave->level_speed[i] = level->bd_cycle_delay_ms;
3762 cave->level_ckdelay[i] = level->bd_cycle_delay_c64;
3763 cave->level_hatching_delay_frame[i] = level->bd_hatching_delay_cycles;
3764 cave->level_hatching_delay_time[i] = level->bd_hatching_delay_seconds;
3766 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3769 cave->diamond_value = level->score[SC_EMERALD];
3770 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3772 cave->scheduling = level->bd_scheduling_type;
3773 cave->pal_timing = level->bd_pal_timing;
3774 cave->intermission = level->bd_intermission;
3775 cave->diagonal_movements = level->bd_diagonal_movements;
3776 cave->active_is_first_found = level->bd_topmost_player_active;
3778 cave->lineshift = level->bd_line_shifting_borders;
3779 cave->wraparound_objects = level->bd_wraparound_objects;
3780 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3781 cave->short_explosions = level->bd_short_explosions;
3782 cave->gravity_affects_all = level->bd_gravity_affects_all;
3784 strncpy(cave->name, level->name, sizeof(GdString));
3785 cave->name[sizeof(GdString) - 1] = '\0';
3787 for (x = 0; x < cave->w; x++)
3788 for (y = 0; y < cave->h; y++)
3789 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3792 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3794 struct LevelInfo_BD *level_bd = level->native_bd_level;
3795 GdCave *cave = level_bd->cave;
3796 int bd_level_nr = level_bd->level_nr;
3799 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3800 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3802 level->time = cave->level_time[bd_level_nr];
3803 level->gems_needed = cave->level_diamonds[bd_level_nr];
3804 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3806 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3807 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3808 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3809 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3811 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3812 level->score[SC_EMERALD] = cave->diamond_value;
3813 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3815 level->bd_scheduling_type = cave->scheduling;
3816 level->bd_pal_timing = cave->pal_timing;
3817 level->bd_intermission = cave->intermission;
3818 level->bd_diagonal_movements = cave->diagonal_movements;
3819 level->bd_topmost_player_active = cave->active_is_first_found;
3821 level->bd_line_shifting_borders = cave->lineshift;
3822 level->bd_wraparound_objects = cave->wraparound_objects;
3823 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
3824 level->bd_short_explosions = cave->short_explosions;
3825 level->bd_gravity_affects_all = cave->gravity_affects_all;
3827 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3829 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3830 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3832 for (x = 0; x < level->fieldx; x++)
3833 for (y = 0; y < level->fieldy; y++)
3834 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3836 checked_free(cave_name);
3839 static void setTapeInfoToDefaults(void);
3841 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3843 struct LevelInfo_BD *level_bd = level->native_bd_level;
3844 GdCave *cave = level_bd->cave;
3845 GdReplay *replay = level_bd->replay;
3851 // always start with reliable default values
3852 setTapeInfoToDefaults();
3854 tape.level_nr = level_nr; // (currently not used)
3855 tape.random_seed = replay->seed;
3857 TapeSetDateFromIsoDateString(replay->date);
3860 tape.pos[tape.counter].delay = 0;
3862 tape.bd_replay = TRUE;
3864 // all time calculations only used to display approximate tape time
3865 int cave_speed = cave->speed;
3866 int milliseconds_game = 0;
3867 int milliseconds_elapsed = 20;
3869 for (i = 0; i < replay->movements->len; i++)
3871 int replay_action = replay->movements->data[i];
3872 int tape_action = map_action_BD_to_RND(replay_action);
3873 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3874 boolean success = 0;
3878 success = TapeAddAction(action);
3880 milliseconds_game += milliseconds_elapsed;
3882 if (milliseconds_game >= cave_speed)
3884 milliseconds_game -= cave_speed;
3891 tape.pos[tape.counter].delay = 0;
3892 tape.pos[tape.counter].action[0] = 0;
3896 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3902 TapeHaltRecording();
3906 // ----------------------------------------------------------------------------
3907 // functions for loading EM level
3908 // ----------------------------------------------------------------------------
3910 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3912 static int ball_xy[8][2] =
3923 struct LevelInfo_EM *level_em = level->native_em_level;
3924 struct CAVE *cav = level_em->cav;
3927 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3928 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3930 cav->time_seconds = level->time;
3931 cav->gems_needed = level->gems_needed;
3933 cav->emerald_score = level->score[SC_EMERALD];
3934 cav->diamond_score = level->score[SC_DIAMOND];
3935 cav->alien_score = level->score[SC_ROBOT];
3936 cav->tank_score = level->score[SC_SPACESHIP];
3937 cav->bug_score = level->score[SC_BUG];
3938 cav->eater_score = level->score[SC_YAMYAM];
3939 cav->nut_score = level->score[SC_NUT];
3940 cav->dynamite_score = level->score[SC_DYNAMITE];
3941 cav->key_score = level->score[SC_KEY];
3942 cav->exit_score = level->score[SC_TIME_BONUS];
3944 cav->num_eater_arrays = level->num_yamyam_contents;
3946 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3947 for (y = 0; y < 3; y++)
3948 for (x = 0; x < 3; x++)
3949 cav->eater_array[i][y * 3 + x] =
3950 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3952 cav->amoeba_time = level->amoeba_speed;
3953 cav->wonderwall_time = level->time_magic_wall;
3954 cav->wheel_time = level->time_wheel;
3956 cav->android_move_time = level->android_move_time;
3957 cav->android_clone_time = level->android_clone_time;
3958 cav->ball_random = level->ball_random;
3959 cav->ball_active = level->ball_active_initial;
3960 cav->ball_time = level->ball_time;
3961 cav->num_ball_arrays = level->num_ball_contents;
3963 cav->lenses_score = level->lenses_score;
3964 cav->magnify_score = level->magnify_score;
3965 cav->slurp_score = level->slurp_score;
3967 cav->lenses_time = level->lenses_time;
3968 cav->magnify_time = level->magnify_time;
3970 cav->wind_time = 9999;
3971 cav->wind_direction =
3972 map_direction_RND_to_EM(level->wind_direction_initial);
3974 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3975 for (j = 0; j < 8; j++)
3976 cav->ball_array[i][j] =
3977 map_element_RND_to_EM_cave(level->ball_content[i].
3978 e[ball_xy[j][0]][ball_xy[j][1]]);
3980 map_android_clone_elements_RND_to_EM(level);
3982 // first fill the complete playfield with the empty space element
3983 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3984 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3985 cav->cave[x][y] = Cblank;
3987 // then copy the real level contents from level file into the playfield
3988 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3990 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3992 if (level->field[x][y] == EL_AMOEBA_DEAD)
3993 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3995 cav->cave[x][y] = new_element;
3998 for (i = 0; i < MAX_PLAYERS; i++)
4000 cav->player_x[i] = -1;
4001 cav->player_y[i] = -1;
4004 // initialize player positions and delete players from the playfield
4005 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4007 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4009 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4011 cav->player_x[player_nr] = x;
4012 cav->player_y[player_nr] = y;
4014 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4019 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4021 static int ball_xy[8][2] =
4032 struct LevelInfo_EM *level_em = level->native_em_level;
4033 struct CAVE *cav = level_em->cav;
4036 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4037 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4039 level->time = cav->time_seconds;
4040 level->gems_needed = cav->gems_needed;
4042 sprintf(level->name, "Level %d", level->file_info.nr);
4044 level->score[SC_EMERALD] = cav->emerald_score;
4045 level->score[SC_DIAMOND] = cav->diamond_score;
4046 level->score[SC_ROBOT] = cav->alien_score;
4047 level->score[SC_SPACESHIP] = cav->tank_score;
4048 level->score[SC_BUG] = cav->bug_score;
4049 level->score[SC_YAMYAM] = cav->eater_score;
4050 level->score[SC_NUT] = cav->nut_score;
4051 level->score[SC_DYNAMITE] = cav->dynamite_score;
4052 level->score[SC_KEY] = cav->key_score;
4053 level->score[SC_TIME_BONUS] = cav->exit_score;
4055 level->num_yamyam_contents = cav->num_eater_arrays;
4057 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4058 for (y = 0; y < 3; y++)
4059 for (x = 0; x < 3; x++)
4060 level->yamyam_content[i].e[x][y] =
4061 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4063 level->amoeba_speed = cav->amoeba_time;
4064 level->time_magic_wall = cav->wonderwall_time;
4065 level->time_wheel = cav->wheel_time;
4067 level->android_move_time = cav->android_move_time;
4068 level->android_clone_time = cav->android_clone_time;
4069 level->ball_random = cav->ball_random;
4070 level->ball_active_initial = cav->ball_active;
4071 level->ball_time = cav->ball_time;
4072 level->num_ball_contents = cav->num_ball_arrays;
4074 level->lenses_score = cav->lenses_score;
4075 level->magnify_score = cav->magnify_score;
4076 level->slurp_score = cav->slurp_score;
4078 level->lenses_time = cav->lenses_time;
4079 level->magnify_time = cav->magnify_time;
4081 level->wind_direction_initial =
4082 map_direction_EM_to_RND(cav->wind_direction);
4084 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4085 for (j = 0; j < 8; j++)
4086 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4087 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4089 map_android_clone_elements_EM_to_RND(level);
4091 // convert the playfield (some elements need special treatment)
4092 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4094 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4096 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4097 new_element = EL_AMOEBA_DEAD;
4099 level->field[x][y] = new_element;
4102 for (i = 0; i < MAX_PLAYERS; i++)
4104 // in case of all players set to the same field, use the first player
4105 int nr = MAX_PLAYERS - i - 1;
4106 int jx = cav->player_x[nr];
4107 int jy = cav->player_y[nr];
4109 if (jx != -1 && jy != -1)
4110 level->field[jx][jy] = EL_PLAYER_1 + nr;
4113 // time score is counted for each 10 seconds left in Emerald Mine levels
4114 level->time_score_base = 10;
4118 // ----------------------------------------------------------------------------
4119 // functions for loading SP level
4120 // ----------------------------------------------------------------------------
4122 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4124 struct LevelInfo_SP *level_sp = level->native_sp_level;
4125 LevelInfoType *header = &level_sp->header;
4128 level_sp->width = level->fieldx;
4129 level_sp->height = level->fieldy;
4131 for (x = 0; x < level->fieldx; x++)
4132 for (y = 0; y < level->fieldy; y++)
4133 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4135 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4137 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4138 header->LevelTitle[i] = level->name[i];
4139 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4141 header->InfotronsNeeded = level->gems_needed;
4143 header->SpecialPortCount = 0;
4145 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4147 boolean gravity_port_found = FALSE;
4148 boolean gravity_port_valid = FALSE;
4149 int gravity_port_flag;
4150 int gravity_port_base_element;
4151 int element = level->field[x][y];
4153 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4154 element <= EL_SP_GRAVITY_ON_PORT_UP)
4156 gravity_port_found = TRUE;
4157 gravity_port_valid = TRUE;
4158 gravity_port_flag = 1;
4159 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4161 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4162 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4164 gravity_port_found = TRUE;
4165 gravity_port_valid = TRUE;
4166 gravity_port_flag = 0;
4167 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4169 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4170 element <= EL_SP_GRAVITY_PORT_UP)
4172 // change R'n'D style gravity inverting special port to normal port
4173 // (there are no gravity inverting ports in native Supaplex engine)
4175 gravity_port_found = TRUE;
4176 gravity_port_valid = FALSE;
4177 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4180 if (gravity_port_found)
4182 if (gravity_port_valid &&
4183 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4185 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4187 port->PortLocation = (y * level->fieldx + x) * 2;
4188 port->Gravity = gravity_port_flag;
4190 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4192 header->SpecialPortCount++;
4196 // change special gravity port to normal port
4198 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4201 level_sp->playfield[x][y] = element - EL_SP_START;
4206 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4208 struct LevelInfo_SP *level_sp = level->native_sp_level;
4209 LevelInfoType *header = &level_sp->header;
4210 boolean num_invalid_elements = 0;
4213 level->fieldx = level_sp->width;
4214 level->fieldy = level_sp->height;
4216 for (x = 0; x < level->fieldx; x++)
4218 for (y = 0; y < level->fieldy; y++)
4220 int element_old = level_sp->playfield[x][y];
4221 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4223 if (element_new == EL_UNKNOWN)
4225 num_invalid_elements++;
4227 Debug("level:native:SP", "invalid element %d at position %d, %d",
4231 level->field[x][y] = element_new;
4235 if (num_invalid_elements > 0)
4236 Warn("found %d invalid elements%s", num_invalid_elements,
4237 (!options.debug ? " (use '--debug' for more details)" : ""));
4239 for (i = 0; i < MAX_PLAYERS; i++)
4240 level->initial_player_gravity[i] =
4241 (header->InitialGravity == 1 ? TRUE : FALSE);
4243 // skip leading spaces
4244 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4245 if (header->LevelTitle[i] != ' ')
4249 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4250 level->name[j] = header->LevelTitle[i];
4251 level->name[j] = '\0';
4253 // cut trailing spaces
4255 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4256 level->name[j - 1] = '\0';
4258 level->gems_needed = header->InfotronsNeeded;
4260 for (i = 0; i < header->SpecialPortCount; i++)
4262 SpecialPortType *port = &header->SpecialPort[i];
4263 int port_location = port->PortLocation;
4264 int gravity = port->Gravity;
4265 int port_x, port_y, port_element;
4267 port_x = (port_location / 2) % level->fieldx;
4268 port_y = (port_location / 2) / level->fieldx;
4270 if (port_x < 0 || port_x >= level->fieldx ||
4271 port_y < 0 || port_y >= level->fieldy)
4273 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4278 port_element = level->field[port_x][port_y];
4280 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4281 port_element > EL_SP_GRAVITY_PORT_UP)
4283 Warn("no special port at position (%d, %d)", port_x, port_y);
4288 // change previous (wrong) gravity inverting special port to either
4289 // gravity enabling special port or gravity disabling special port
4290 level->field[port_x][port_y] +=
4291 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4292 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4295 // change special gravity ports without database entries to normal ports
4296 for (x = 0; x < level->fieldx; x++)
4297 for (y = 0; y < level->fieldy; y++)
4298 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4299 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4300 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4302 level->time = 0; // no time limit
4303 level->amoeba_speed = 0;
4304 level->time_magic_wall = 0;
4305 level->time_wheel = 0;
4306 level->amoeba_content = EL_EMPTY;
4308 // original Supaplex does not use score values -- rate by playing time
4309 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4310 level->score[i] = 0;
4312 level->rate_time_over_score = TRUE;
4314 // there are no yamyams in supaplex levels
4315 for (i = 0; i < level->num_yamyam_contents; i++)
4316 for (x = 0; x < 3; x++)
4317 for (y = 0; y < 3; y++)
4318 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4321 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4323 struct LevelInfo_SP *level_sp = level->native_sp_level;
4324 struct DemoInfo_SP *demo = &level_sp->demo;
4327 // always start with reliable default values
4328 demo->is_available = FALSE;
4331 if (TAPE_IS_EMPTY(tape))
4334 demo->level_nr = tape.level_nr; // (currently not used)
4336 level_sp->header.DemoRandomSeed = tape.random_seed;
4340 for (i = 0; i < tape.length; i++)
4342 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4343 int demo_repeat = tape.pos[i].delay;
4344 int demo_entries = (demo_repeat + 15) / 16;
4346 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4348 Warn("tape truncated: size exceeds maximum SP demo size %d",
4354 for (j = 0; j < demo_repeat / 16; j++)
4355 demo->data[demo->length++] = 0xf0 | demo_action;
4357 if (demo_repeat % 16)
4358 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4361 demo->is_available = TRUE;
4364 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4366 struct LevelInfo_SP *level_sp = level->native_sp_level;
4367 struct DemoInfo_SP *demo = &level_sp->demo;
4368 char *filename = level->file_info.filename;
4371 // always start with reliable default values
4372 setTapeInfoToDefaults();
4374 if (!demo->is_available)
4377 tape.level_nr = demo->level_nr; // (currently not used)
4378 tape.random_seed = level_sp->header.DemoRandomSeed;
4380 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4383 tape.pos[tape.counter].delay = 0;
4385 for (i = 0; i < demo->length; i++)
4387 int demo_action = demo->data[i] & 0x0f;
4388 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4389 int tape_action = map_key_SP_to_RND(demo_action);
4390 int tape_repeat = demo_repeat + 1;
4391 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4392 boolean success = 0;
4395 for (j = 0; j < tape_repeat; j++)
4396 success = TapeAddAction(action);
4400 Warn("SP demo truncated: size exceeds maximum tape size %d",
4407 TapeHaltRecording();
4411 // ----------------------------------------------------------------------------
4412 // functions for loading MM level
4413 // ----------------------------------------------------------------------------
4415 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4417 struct LevelInfo_MM *level_mm = level->native_mm_level;
4420 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4421 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4423 level_mm->time = level->time;
4424 level_mm->kettles_needed = level->gems_needed;
4425 level_mm->auto_count_kettles = level->auto_count_gems;
4427 level_mm->mm_laser_red = level->mm_laser_red;
4428 level_mm->mm_laser_green = level->mm_laser_green;
4429 level_mm->mm_laser_blue = level->mm_laser_blue;
4431 level_mm->df_laser_red = level->df_laser_red;
4432 level_mm->df_laser_green = level->df_laser_green;
4433 level_mm->df_laser_blue = level->df_laser_blue;
4435 strcpy(level_mm->name, level->name);
4436 strcpy(level_mm->author, level->author);
4438 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4439 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4440 level_mm->score[SC_KEY] = level->score[SC_KEY];
4441 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4442 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4444 level_mm->amoeba_speed = level->amoeba_speed;
4445 level_mm->time_fuse = level->mm_time_fuse;
4446 level_mm->time_bomb = level->mm_time_bomb;
4447 level_mm->time_ball = level->mm_time_ball;
4448 level_mm->time_block = level->mm_time_block;
4450 level_mm->num_ball_contents = level->num_mm_ball_contents;
4451 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4452 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4453 level_mm->explode_ball = level->explode_mm_ball;
4455 for (i = 0; i < level->num_mm_ball_contents; i++)
4456 level_mm->ball_content[i] =
4457 map_element_RND_to_MM(level->mm_ball_content[i]);
4459 for (x = 0; x < level->fieldx; x++)
4460 for (y = 0; y < level->fieldy; y++)
4462 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4465 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4467 struct LevelInfo_MM *level_mm = level->native_mm_level;
4470 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4471 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4473 level->time = level_mm->time;
4474 level->gems_needed = level_mm->kettles_needed;
4475 level->auto_count_gems = level_mm->auto_count_kettles;
4477 level->mm_laser_red = level_mm->mm_laser_red;
4478 level->mm_laser_green = level_mm->mm_laser_green;
4479 level->mm_laser_blue = level_mm->mm_laser_blue;
4481 level->df_laser_red = level_mm->df_laser_red;
4482 level->df_laser_green = level_mm->df_laser_green;
4483 level->df_laser_blue = level_mm->df_laser_blue;
4485 strcpy(level->name, level_mm->name);
4487 // only overwrite author from 'levelinfo.conf' if author defined in level
4488 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4489 strcpy(level->author, level_mm->author);
4491 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4492 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4493 level->score[SC_KEY] = level_mm->score[SC_KEY];
4494 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4495 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4497 level->amoeba_speed = level_mm->amoeba_speed;
4498 level->mm_time_fuse = level_mm->time_fuse;
4499 level->mm_time_bomb = level_mm->time_bomb;
4500 level->mm_time_ball = level_mm->time_ball;
4501 level->mm_time_block = level_mm->time_block;
4503 level->num_mm_ball_contents = level_mm->num_ball_contents;
4504 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4505 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4506 level->explode_mm_ball = level_mm->explode_ball;
4508 for (i = 0; i < level->num_mm_ball_contents; i++)
4509 level->mm_ball_content[i] =
4510 map_element_MM_to_RND(level_mm->ball_content[i]);
4512 for (x = 0; x < level->fieldx; x++)
4513 for (y = 0; y < level->fieldy; y++)
4514 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4518 // ----------------------------------------------------------------------------
4519 // functions for loading DC level
4520 // ----------------------------------------------------------------------------
4522 #define DC_LEVEL_HEADER_SIZE 344
4524 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4527 static int last_data_encoded;
4531 int diff_hi, diff_lo;
4532 int data_hi, data_lo;
4533 unsigned short data_decoded;
4537 last_data_encoded = 0;
4544 diff = data_encoded - last_data_encoded;
4545 diff_hi = diff & ~0xff;
4546 diff_lo = diff & 0xff;
4550 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4551 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4552 data_hi = data_hi & 0xff00;
4554 data_decoded = data_hi | data_lo;
4556 last_data_encoded = data_encoded;
4558 offset1 = (offset1 + 1) % 31;
4559 offset2 = offset2 & 0xff;
4561 return data_decoded;
4564 static int getMappedElement_DC(int element)
4572 // 0x0117 - 0x036e: (?)
4575 // 0x042d - 0x0684: (?)
4591 element = EL_CRYSTAL;
4594 case 0x0e77: // quicksand (boulder)
4595 element = EL_QUICKSAND_FAST_FULL;
4598 case 0x0e99: // slow quicksand (boulder)
4599 element = EL_QUICKSAND_FULL;
4603 element = EL_EM_EXIT_OPEN;
4607 element = EL_EM_EXIT_CLOSED;
4611 element = EL_EM_STEEL_EXIT_OPEN;
4615 element = EL_EM_STEEL_EXIT_CLOSED;
4618 case 0x0f4f: // dynamite (lit 1)
4619 element = EL_EM_DYNAMITE_ACTIVE;
4622 case 0x0f57: // dynamite (lit 2)
4623 element = EL_EM_DYNAMITE_ACTIVE;
4626 case 0x0f5f: // dynamite (lit 3)
4627 element = EL_EM_DYNAMITE_ACTIVE;
4630 case 0x0f67: // dynamite (lit 4)
4631 element = EL_EM_DYNAMITE_ACTIVE;
4638 element = EL_AMOEBA_WET;
4642 element = EL_AMOEBA_DROP;
4646 element = EL_DC_MAGIC_WALL;
4650 element = EL_SPACESHIP_UP;
4654 element = EL_SPACESHIP_DOWN;
4658 element = EL_SPACESHIP_LEFT;
4662 element = EL_SPACESHIP_RIGHT;
4666 element = EL_BUG_UP;
4670 element = EL_BUG_DOWN;
4674 element = EL_BUG_LEFT;
4678 element = EL_BUG_RIGHT;
4682 element = EL_MOLE_UP;
4686 element = EL_MOLE_DOWN;
4690 element = EL_MOLE_LEFT;
4694 element = EL_MOLE_RIGHT;
4702 element = EL_YAMYAM_UP;
4706 element = EL_SWITCHGATE_OPEN;
4710 element = EL_SWITCHGATE_CLOSED;
4714 element = EL_DC_SWITCHGATE_SWITCH_UP;
4718 element = EL_TIMEGATE_CLOSED;
4721 case 0x144c: // conveyor belt switch (green)
4722 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4725 case 0x144f: // conveyor belt switch (red)
4726 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4729 case 0x1452: // conveyor belt switch (blue)
4730 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4734 element = EL_CONVEYOR_BELT_3_MIDDLE;
4738 element = EL_CONVEYOR_BELT_3_LEFT;
4742 element = EL_CONVEYOR_BELT_3_RIGHT;
4746 element = EL_CONVEYOR_BELT_1_MIDDLE;
4750 element = EL_CONVEYOR_BELT_1_LEFT;
4754 element = EL_CONVEYOR_BELT_1_RIGHT;
4758 element = EL_CONVEYOR_BELT_4_MIDDLE;
4762 element = EL_CONVEYOR_BELT_4_LEFT;
4766 element = EL_CONVEYOR_BELT_4_RIGHT;
4770 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4774 element = EL_EXPANDABLE_WALL_VERTICAL;
4778 element = EL_EXPANDABLE_WALL_ANY;
4781 case 0x14ce: // growing steel wall (left/right)
4782 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4785 case 0x14df: // growing steel wall (up/down)
4786 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4789 case 0x14e8: // growing steel wall (up/down/left/right)
4790 element = EL_EXPANDABLE_STEELWALL_ANY;
4794 element = EL_SHIELD_DEADLY;
4798 element = EL_EXTRA_TIME;
4806 element = EL_EMPTY_SPACE;
4809 case 0x1578: // quicksand (empty)
4810 element = EL_QUICKSAND_FAST_EMPTY;
4813 case 0x1579: // slow quicksand (empty)
4814 element = EL_QUICKSAND_EMPTY;
4824 element = EL_EM_DYNAMITE;
4827 case 0x15a1: // key (red)
4828 element = EL_EM_KEY_1;
4831 case 0x15a2: // key (yellow)
4832 element = EL_EM_KEY_2;
4835 case 0x15a3: // key (blue)
4836 element = EL_EM_KEY_4;
4839 case 0x15a4: // key (green)
4840 element = EL_EM_KEY_3;
4843 case 0x15a5: // key (white)
4844 element = EL_DC_KEY_WHITE;
4848 element = EL_WALL_SLIPPERY;
4855 case 0x15a8: // wall (not round)
4859 case 0x15a9: // (blue)
4860 element = EL_CHAR_A;
4863 case 0x15aa: // (blue)
4864 element = EL_CHAR_B;
4867 case 0x15ab: // (blue)
4868 element = EL_CHAR_C;
4871 case 0x15ac: // (blue)
4872 element = EL_CHAR_D;
4875 case 0x15ad: // (blue)
4876 element = EL_CHAR_E;
4879 case 0x15ae: // (blue)
4880 element = EL_CHAR_F;
4883 case 0x15af: // (blue)
4884 element = EL_CHAR_G;
4887 case 0x15b0: // (blue)
4888 element = EL_CHAR_H;
4891 case 0x15b1: // (blue)
4892 element = EL_CHAR_I;
4895 case 0x15b2: // (blue)
4896 element = EL_CHAR_J;
4899 case 0x15b3: // (blue)
4900 element = EL_CHAR_K;
4903 case 0x15b4: // (blue)
4904 element = EL_CHAR_L;
4907 case 0x15b5: // (blue)
4908 element = EL_CHAR_M;
4911 case 0x15b6: // (blue)
4912 element = EL_CHAR_N;
4915 case 0x15b7: // (blue)
4916 element = EL_CHAR_O;
4919 case 0x15b8: // (blue)
4920 element = EL_CHAR_P;
4923 case 0x15b9: // (blue)
4924 element = EL_CHAR_Q;
4927 case 0x15ba: // (blue)
4928 element = EL_CHAR_R;
4931 case 0x15bb: // (blue)
4932 element = EL_CHAR_S;
4935 case 0x15bc: // (blue)
4936 element = EL_CHAR_T;
4939 case 0x15bd: // (blue)
4940 element = EL_CHAR_U;
4943 case 0x15be: // (blue)
4944 element = EL_CHAR_V;
4947 case 0x15bf: // (blue)
4948 element = EL_CHAR_W;
4951 case 0x15c0: // (blue)
4952 element = EL_CHAR_X;
4955 case 0x15c1: // (blue)
4956 element = EL_CHAR_Y;
4959 case 0x15c2: // (blue)
4960 element = EL_CHAR_Z;
4963 case 0x15c3: // (blue)
4964 element = EL_CHAR_AUMLAUT;
4967 case 0x15c4: // (blue)
4968 element = EL_CHAR_OUMLAUT;
4971 case 0x15c5: // (blue)
4972 element = EL_CHAR_UUMLAUT;
4975 case 0x15c6: // (blue)
4976 element = EL_CHAR_0;
4979 case 0x15c7: // (blue)
4980 element = EL_CHAR_1;
4983 case 0x15c8: // (blue)
4984 element = EL_CHAR_2;
4987 case 0x15c9: // (blue)
4988 element = EL_CHAR_3;
4991 case 0x15ca: // (blue)
4992 element = EL_CHAR_4;
4995 case 0x15cb: // (blue)
4996 element = EL_CHAR_5;
4999 case 0x15cc: // (blue)
5000 element = EL_CHAR_6;
5003 case 0x15cd: // (blue)
5004 element = EL_CHAR_7;
5007 case 0x15ce: // (blue)
5008 element = EL_CHAR_8;
5011 case 0x15cf: // (blue)
5012 element = EL_CHAR_9;
5015 case 0x15d0: // (blue)
5016 element = EL_CHAR_PERIOD;
5019 case 0x15d1: // (blue)
5020 element = EL_CHAR_EXCLAM;
5023 case 0x15d2: // (blue)
5024 element = EL_CHAR_COLON;
5027 case 0x15d3: // (blue)
5028 element = EL_CHAR_LESS;
5031 case 0x15d4: // (blue)
5032 element = EL_CHAR_GREATER;
5035 case 0x15d5: // (blue)
5036 element = EL_CHAR_QUESTION;
5039 case 0x15d6: // (blue)
5040 element = EL_CHAR_COPYRIGHT;
5043 case 0x15d7: // (blue)
5044 element = EL_CHAR_UP;
5047 case 0x15d8: // (blue)
5048 element = EL_CHAR_DOWN;
5051 case 0x15d9: // (blue)
5052 element = EL_CHAR_BUTTON;
5055 case 0x15da: // (blue)
5056 element = EL_CHAR_PLUS;
5059 case 0x15db: // (blue)
5060 element = EL_CHAR_MINUS;
5063 case 0x15dc: // (blue)
5064 element = EL_CHAR_APOSTROPHE;
5067 case 0x15dd: // (blue)
5068 element = EL_CHAR_PARENLEFT;
5071 case 0x15de: // (blue)
5072 element = EL_CHAR_PARENRIGHT;
5075 case 0x15df: // (green)
5076 element = EL_CHAR_A;
5079 case 0x15e0: // (green)
5080 element = EL_CHAR_B;
5083 case 0x15e1: // (green)
5084 element = EL_CHAR_C;
5087 case 0x15e2: // (green)
5088 element = EL_CHAR_D;
5091 case 0x15e3: // (green)
5092 element = EL_CHAR_E;
5095 case 0x15e4: // (green)
5096 element = EL_CHAR_F;
5099 case 0x15e5: // (green)
5100 element = EL_CHAR_G;
5103 case 0x15e6: // (green)
5104 element = EL_CHAR_H;
5107 case 0x15e7: // (green)
5108 element = EL_CHAR_I;
5111 case 0x15e8: // (green)
5112 element = EL_CHAR_J;
5115 case 0x15e9: // (green)
5116 element = EL_CHAR_K;
5119 case 0x15ea: // (green)
5120 element = EL_CHAR_L;
5123 case 0x15eb: // (green)
5124 element = EL_CHAR_M;
5127 case 0x15ec: // (green)
5128 element = EL_CHAR_N;
5131 case 0x15ed: // (green)
5132 element = EL_CHAR_O;
5135 case 0x15ee: // (green)
5136 element = EL_CHAR_P;
5139 case 0x15ef: // (green)
5140 element = EL_CHAR_Q;
5143 case 0x15f0: // (green)
5144 element = EL_CHAR_R;
5147 case 0x15f1: // (green)
5148 element = EL_CHAR_S;
5151 case 0x15f2: // (green)
5152 element = EL_CHAR_T;
5155 case 0x15f3: // (green)
5156 element = EL_CHAR_U;
5159 case 0x15f4: // (green)
5160 element = EL_CHAR_V;
5163 case 0x15f5: // (green)
5164 element = EL_CHAR_W;
5167 case 0x15f6: // (green)
5168 element = EL_CHAR_X;
5171 case 0x15f7: // (green)
5172 element = EL_CHAR_Y;
5175 case 0x15f8: // (green)
5176 element = EL_CHAR_Z;
5179 case 0x15f9: // (green)
5180 element = EL_CHAR_AUMLAUT;
5183 case 0x15fa: // (green)
5184 element = EL_CHAR_OUMLAUT;
5187 case 0x15fb: // (green)
5188 element = EL_CHAR_UUMLAUT;
5191 case 0x15fc: // (green)
5192 element = EL_CHAR_0;
5195 case 0x15fd: // (green)
5196 element = EL_CHAR_1;
5199 case 0x15fe: // (green)
5200 element = EL_CHAR_2;
5203 case 0x15ff: // (green)
5204 element = EL_CHAR_3;
5207 case 0x1600: // (green)
5208 element = EL_CHAR_4;
5211 case 0x1601: // (green)
5212 element = EL_CHAR_5;
5215 case 0x1602: // (green)
5216 element = EL_CHAR_6;
5219 case 0x1603: // (green)
5220 element = EL_CHAR_7;
5223 case 0x1604: // (green)
5224 element = EL_CHAR_8;
5227 case 0x1605: // (green)
5228 element = EL_CHAR_9;
5231 case 0x1606: // (green)
5232 element = EL_CHAR_PERIOD;
5235 case 0x1607: // (green)
5236 element = EL_CHAR_EXCLAM;
5239 case 0x1608: // (green)
5240 element = EL_CHAR_COLON;
5243 case 0x1609: // (green)
5244 element = EL_CHAR_LESS;
5247 case 0x160a: // (green)
5248 element = EL_CHAR_GREATER;
5251 case 0x160b: // (green)
5252 element = EL_CHAR_QUESTION;
5255 case 0x160c: // (green)
5256 element = EL_CHAR_COPYRIGHT;
5259 case 0x160d: // (green)
5260 element = EL_CHAR_UP;
5263 case 0x160e: // (green)
5264 element = EL_CHAR_DOWN;
5267 case 0x160f: // (green)
5268 element = EL_CHAR_BUTTON;
5271 case 0x1610: // (green)
5272 element = EL_CHAR_PLUS;
5275 case 0x1611: // (green)
5276 element = EL_CHAR_MINUS;
5279 case 0x1612: // (green)
5280 element = EL_CHAR_APOSTROPHE;
5283 case 0x1613: // (green)
5284 element = EL_CHAR_PARENLEFT;
5287 case 0x1614: // (green)
5288 element = EL_CHAR_PARENRIGHT;
5291 case 0x1615: // (blue steel)
5292 element = EL_STEEL_CHAR_A;
5295 case 0x1616: // (blue steel)
5296 element = EL_STEEL_CHAR_B;
5299 case 0x1617: // (blue steel)
5300 element = EL_STEEL_CHAR_C;
5303 case 0x1618: // (blue steel)
5304 element = EL_STEEL_CHAR_D;
5307 case 0x1619: // (blue steel)
5308 element = EL_STEEL_CHAR_E;
5311 case 0x161a: // (blue steel)
5312 element = EL_STEEL_CHAR_F;
5315 case 0x161b: // (blue steel)
5316 element = EL_STEEL_CHAR_G;
5319 case 0x161c: // (blue steel)
5320 element = EL_STEEL_CHAR_H;
5323 case 0x161d: // (blue steel)
5324 element = EL_STEEL_CHAR_I;
5327 case 0x161e: // (blue steel)
5328 element = EL_STEEL_CHAR_J;
5331 case 0x161f: // (blue steel)
5332 element = EL_STEEL_CHAR_K;
5335 case 0x1620: // (blue steel)
5336 element = EL_STEEL_CHAR_L;
5339 case 0x1621: // (blue steel)
5340 element = EL_STEEL_CHAR_M;
5343 case 0x1622: // (blue steel)
5344 element = EL_STEEL_CHAR_N;
5347 case 0x1623: // (blue steel)
5348 element = EL_STEEL_CHAR_O;
5351 case 0x1624: // (blue steel)
5352 element = EL_STEEL_CHAR_P;
5355 case 0x1625: // (blue steel)
5356 element = EL_STEEL_CHAR_Q;
5359 case 0x1626: // (blue steel)
5360 element = EL_STEEL_CHAR_R;
5363 case 0x1627: // (blue steel)
5364 element = EL_STEEL_CHAR_S;
5367 case 0x1628: // (blue steel)
5368 element = EL_STEEL_CHAR_T;
5371 case 0x1629: // (blue steel)
5372 element = EL_STEEL_CHAR_U;
5375 case 0x162a: // (blue steel)
5376 element = EL_STEEL_CHAR_V;
5379 case 0x162b: // (blue steel)
5380 element = EL_STEEL_CHAR_W;
5383 case 0x162c: // (blue steel)
5384 element = EL_STEEL_CHAR_X;
5387 case 0x162d: // (blue steel)
5388 element = EL_STEEL_CHAR_Y;
5391 case 0x162e: // (blue steel)
5392 element = EL_STEEL_CHAR_Z;
5395 case 0x162f: // (blue steel)
5396 element = EL_STEEL_CHAR_AUMLAUT;
5399 case 0x1630: // (blue steel)
5400 element = EL_STEEL_CHAR_OUMLAUT;
5403 case 0x1631: // (blue steel)
5404 element = EL_STEEL_CHAR_UUMLAUT;
5407 case 0x1632: // (blue steel)
5408 element = EL_STEEL_CHAR_0;
5411 case 0x1633: // (blue steel)
5412 element = EL_STEEL_CHAR_1;
5415 case 0x1634: // (blue steel)
5416 element = EL_STEEL_CHAR_2;
5419 case 0x1635: // (blue steel)
5420 element = EL_STEEL_CHAR_3;
5423 case 0x1636: // (blue steel)
5424 element = EL_STEEL_CHAR_4;
5427 case 0x1637: // (blue steel)
5428 element = EL_STEEL_CHAR_5;
5431 case 0x1638: // (blue steel)
5432 element = EL_STEEL_CHAR_6;
5435 case 0x1639: // (blue steel)
5436 element = EL_STEEL_CHAR_7;
5439 case 0x163a: // (blue steel)
5440 element = EL_STEEL_CHAR_8;
5443 case 0x163b: // (blue steel)
5444 element = EL_STEEL_CHAR_9;
5447 case 0x163c: // (blue steel)
5448 element = EL_STEEL_CHAR_PERIOD;
5451 case 0x163d: // (blue steel)
5452 element = EL_STEEL_CHAR_EXCLAM;
5455 case 0x163e: // (blue steel)
5456 element = EL_STEEL_CHAR_COLON;
5459 case 0x163f: // (blue steel)
5460 element = EL_STEEL_CHAR_LESS;
5463 case 0x1640: // (blue steel)
5464 element = EL_STEEL_CHAR_GREATER;
5467 case 0x1641: // (blue steel)
5468 element = EL_STEEL_CHAR_QUESTION;
5471 case 0x1642: // (blue steel)
5472 element = EL_STEEL_CHAR_COPYRIGHT;
5475 case 0x1643: // (blue steel)
5476 element = EL_STEEL_CHAR_UP;
5479 case 0x1644: // (blue steel)
5480 element = EL_STEEL_CHAR_DOWN;
5483 case 0x1645: // (blue steel)
5484 element = EL_STEEL_CHAR_BUTTON;
5487 case 0x1646: // (blue steel)
5488 element = EL_STEEL_CHAR_PLUS;
5491 case 0x1647: // (blue steel)
5492 element = EL_STEEL_CHAR_MINUS;
5495 case 0x1648: // (blue steel)
5496 element = EL_STEEL_CHAR_APOSTROPHE;
5499 case 0x1649: // (blue steel)
5500 element = EL_STEEL_CHAR_PARENLEFT;
5503 case 0x164a: // (blue steel)
5504 element = EL_STEEL_CHAR_PARENRIGHT;
5507 case 0x164b: // (green steel)
5508 element = EL_STEEL_CHAR_A;
5511 case 0x164c: // (green steel)
5512 element = EL_STEEL_CHAR_B;
5515 case 0x164d: // (green steel)
5516 element = EL_STEEL_CHAR_C;
5519 case 0x164e: // (green steel)
5520 element = EL_STEEL_CHAR_D;
5523 case 0x164f: // (green steel)
5524 element = EL_STEEL_CHAR_E;
5527 case 0x1650: // (green steel)
5528 element = EL_STEEL_CHAR_F;
5531 case 0x1651: // (green steel)
5532 element = EL_STEEL_CHAR_G;
5535 case 0x1652: // (green steel)
5536 element = EL_STEEL_CHAR_H;
5539 case 0x1653: // (green steel)
5540 element = EL_STEEL_CHAR_I;
5543 case 0x1654: // (green steel)
5544 element = EL_STEEL_CHAR_J;
5547 case 0x1655: // (green steel)
5548 element = EL_STEEL_CHAR_K;
5551 case 0x1656: // (green steel)
5552 element = EL_STEEL_CHAR_L;
5555 case 0x1657: // (green steel)
5556 element = EL_STEEL_CHAR_M;
5559 case 0x1658: // (green steel)
5560 element = EL_STEEL_CHAR_N;
5563 case 0x1659: // (green steel)
5564 element = EL_STEEL_CHAR_O;
5567 case 0x165a: // (green steel)
5568 element = EL_STEEL_CHAR_P;
5571 case 0x165b: // (green steel)
5572 element = EL_STEEL_CHAR_Q;
5575 case 0x165c: // (green steel)
5576 element = EL_STEEL_CHAR_R;
5579 case 0x165d: // (green steel)
5580 element = EL_STEEL_CHAR_S;
5583 case 0x165e: // (green steel)
5584 element = EL_STEEL_CHAR_T;
5587 case 0x165f: // (green steel)
5588 element = EL_STEEL_CHAR_U;
5591 case 0x1660: // (green steel)
5592 element = EL_STEEL_CHAR_V;
5595 case 0x1661: // (green steel)
5596 element = EL_STEEL_CHAR_W;
5599 case 0x1662: // (green steel)
5600 element = EL_STEEL_CHAR_X;
5603 case 0x1663: // (green steel)
5604 element = EL_STEEL_CHAR_Y;
5607 case 0x1664: // (green steel)
5608 element = EL_STEEL_CHAR_Z;
5611 case 0x1665: // (green steel)
5612 element = EL_STEEL_CHAR_AUMLAUT;
5615 case 0x1666: // (green steel)
5616 element = EL_STEEL_CHAR_OUMLAUT;
5619 case 0x1667: // (green steel)
5620 element = EL_STEEL_CHAR_UUMLAUT;
5623 case 0x1668: // (green steel)
5624 element = EL_STEEL_CHAR_0;
5627 case 0x1669: // (green steel)
5628 element = EL_STEEL_CHAR_1;
5631 case 0x166a: // (green steel)
5632 element = EL_STEEL_CHAR_2;
5635 case 0x166b: // (green steel)
5636 element = EL_STEEL_CHAR_3;
5639 case 0x166c: // (green steel)
5640 element = EL_STEEL_CHAR_4;
5643 case 0x166d: // (green steel)
5644 element = EL_STEEL_CHAR_5;
5647 case 0x166e: // (green steel)
5648 element = EL_STEEL_CHAR_6;
5651 case 0x166f: // (green steel)
5652 element = EL_STEEL_CHAR_7;
5655 case 0x1670: // (green steel)
5656 element = EL_STEEL_CHAR_8;
5659 case 0x1671: // (green steel)
5660 element = EL_STEEL_CHAR_9;
5663 case 0x1672: // (green steel)
5664 element = EL_STEEL_CHAR_PERIOD;
5667 case 0x1673: // (green steel)
5668 element = EL_STEEL_CHAR_EXCLAM;
5671 case 0x1674: // (green steel)
5672 element = EL_STEEL_CHAR_COLON;
5675 case 0x1675: // (green steel)
5676 element = EL_STEEL_CHAR_LESS;
5679 case 0x1676: // (green steel)
5680 element = EL_STEEL_CHAR_GREATER;
5683 case 0x1677: // (green steel)
5684 element = EL_STEEL_CHAR_QUESTION;
5687 case 0x1678: // (green steel)
5688 element = EL_STEEL_CHAR_COPYRIGHT;
5691 case 0x1679: // (green steel)
5692 element = EL_STEEL_CHAR_UP;
5695 case 0x167a: // (green steel)
5696 element = EL_STEEL_CHAR_DOWN;
5699 case 0x167b: // (green steel)
5700 element = EL_STEEL_CHAR_BUTTON;
5703 case 0x167c: // (green steel)
5704 element = EL_STEEL_CHAR_PLUS;
5707 case 0x167d: // (green steel)
5708 element = EL_STEEL_CHAR_MINUS;
5711 case 0x167e: // (green steel)
5712 element = EL_STEEL_CHAR_APOSTROPHE;
5715 case 0x167f: // (green steel)
5716 element = EL_STEEL_CHAR_PARENLEFT;
5719 case 0x1680: // (green steel)
5720 element = EL_STEEL_CHAR_PARENRIGHT;
5723 case 0x1681: // gate (red)
5724 element = EL_EM_GATE_1;
5727 case 0x1682: // secret gate (red)
5728 element = EL_EM_GATE_1_GRAY;
5731 case 0x1683: // gate (yellow)
5732 element = EL_EM_GATE_2;
5735 case 0x1684: // secret gate (yellow)
5736 element = EL_EM_GATE_2_GRAY;
5739 case 0x1685: // gate (blue)
5740 element = EL_EM_GATE_4;
5743 case 0x1686: // secret gate (blue)
5744 element = EL_EM_GATE_4_GRAY;
5747 case 0x1687: // gate (green)
5748 element = EL_EM_GATE_3;
5751 case 0x1688: // secret gate (green)
5752 element = EL_EM_GATE_3_GRAY;
5755 case 0x1689: // gate (white)
5756 element = EL_DC_GATE_WHITE;
5759 case 0x168a: // secret gate (white)
5760 element = EL_DC_GATE_WHITE_GRAY;
5763 case 0x168b: // secret gate (no key)
5764 element = EL_DC_GATE_FAKE_GRAY;
5768 element = EL_ROBOT_WHEEL;
5772 element = EL_DC_TIMEGATE_SWITCH;
5776 element = EL_ACID_POOL_BOTTOM;
5780 element = EL_ACID_POOL_TOPLEFT;
5784 element = EL_ACID_POOL_TOPRIGHT;
5788 element = EL_ACID_POOL_BOTTOMLEFT;
5792 element = EL_ACID_POOL_BOTTOMRIGHT;
5796 element = EL_STEELWALL;
5800 element = EL_STEELWALL_SLIPPERY;
5803 case 0x1695: // steel wall (not round)
5804 element = EL_STEELWALL;
5807 case 0x1696: // steel wall (left)
5808 element = EL_DC_STEELWALL_1_LEFT;
5811 case 0x1697: // steel wall (bottom)
5812 element = EL_DC_STEELWALL_1_BOTTOM;
5815 case 0x1698: // steel wall (right)
5816 element = EL_DC_STEELWALL_1_RIGHT;
5819 case 0x1699: // steel wall (top)
5820 element = EL_DC_STEELWALL_1_TOP;
5823 case 0x169a: // steel wall (left/bottom)
5824 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5827 case 0x169b: // steel wall (right/bottom)
5828 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5831 case 0x169c: // steel wall (right/top)
5832 element = EL_DC_STEELWALL_1_TOPRIGHT;
5835 case 0x169d: // steel wall (left/top)
5836 element = EL_DC_STEELWALL_1_TOPLEFT;
5839 case 0x169e: // steel wall (right/bottom small)
5840 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5843 case 0x169f: // steel wall (left/bottom small)
5844 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5847 case 0x16a0: // steel wall (right/top small)
5848 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5851 case 0x16a1: // steel wall (left/top small)
5852 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5855 case 0x16a2: // steel wall (left/right)
5856 element = EL_DC_STEELWALL_1_VERTICAL;
5859 case 0x16a3: // steel wall (top/bottom)
5860 element = EL_DC_STEELWALL_1_HORIZONTAL;
5863 case 0x16a4: // steel wall 2 (left end)
5864 element = EL_DC_STEELWALL_2_LEFT;
5867 case 0x16a5: // steel wall 2 (right end)
5868 element = EL_DC_STEELWALL_2_RIGHT;
5871 case 0x16a6: // steel wall 2 (top end)
5872 element = EL_DC_STEELWALL_2_TOP;
5875 case 0x16a7: // steel wall 2 (bottom end)
5876 element = EL_DC_STEELWALL_2_BOTTOM;
5879 case 0x16a8: // steel wall 2 (left/right)
5880 element = EL_DC_STEELWALL_2_HORIZONTAL;
5883 case 0x16a9: // steel wall 2 (up/down)
5884 element = EL_DC_STEELWALL_2_VERTICAL;
5887 case 0x16aa: // steel wall 2 (mid)
5888 element = EL_DC_STEELWALL_2_MIDDLE;
5892 element = EL_SIGN_EXCLAMATION;
5896 element = EL_SIGN_RADIOACTIVITY;
5900 element = EL_SIGN_STOP;
5904 element = EL_SIGN_WHEELCHAIR;
5908 element = EL_SIGN_PARKING;
5912 element = EL_SIGN_NO_ENTRY;
5916 element = EL_SIGN_HEART;
5920 element = EL_SIGN_GIVE_WAY;
5924 element = EL_SIGN_ENTRY_FORBIDDEN;
5928 element = EL_SIGN_EMERGENCY_EXIT;
5932 element = EL_SIGN_YIN_YANG;
5936 element = EL_WALL_EMERALD;
5940 element = EL_WALL_DIAMOND;
5944 element = EL_WALL_PEARL;
5948 element = EL_WALL_CRYSTAL;
5952 element = EL_INVISIBLE_WALL;
5956 element = EL_INVISIBLE_STEELWALL;
5960 // EL_INVISIBLE_SAND
5963 element = EL_LIGHT_SWITCH;
5967 element = EL_ENVELOPE_1;
5971 if (element >= 0x0117 && element <= 0x036e) // (?)
5972 element = EL_DIAMOND;
5973 else if (element >= 0x042d && element <= 0x0684) // (?)
5974 element = EL_EMERALD;
5975 else if (element >= 0x157c && element <= 0x158b)
5977 else if (element >= 0x1590 && element <= 0x159f)
5978 element = EL_DC_LANDMINE;
5979 else if (element >= 0x16bc && element <= 0x16cb)
5980 element = EL_INVISIBLE_SAND;
5983 Warn("unknown Diamond Caves element 0x%04x", element);
5985 element = EL_UNKNOWN;
5990 return getMappedElement(element);
5993 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5995 byte header[DC_LEVEL_HEADER_SIZE];
5997 int envelope_header_pos = 62;
5998 int envelope_content_pos = 94;
5999 int level_name_pos = 251;
6000 int level_author_pos = 292;
6001 int envelope_header_len;
6002 int envelope_content_len;
6004 int level_author_len;
6006 int num_yamyam_contents;
6009 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6011 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6013 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6015 header[i * 2 + 0] = header_word >> 8;
6016 header[i * 2 + 1] = header_word & 0xff;
6019 // read some values from level header to check level decoding integrity
6020 fieldx = header[6] | (header[7] << 8);
6021 fieldy = header[8] | (header[9] << 8);
6022 num_yamyam_contents = header[60] | (header[61] << 8);
6024 // do some simple sanity checks to ensure that level was correctly decoded
6025 if (fieldx < 1 || fieldx > 256 ||
6026 fieldy < 1 || fieldy > 256 ||
6027 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6029 level->no_valid_file = TRUE;
6031 Warn("cannot decode level from stream -- using empty level");
6036 // maximum envelope header size is 31 bytes
6037 envelope_header_len = header[envelope_header_pos];
6038 // maximum envelope content size is 110 (156?) bytes
6039 envelope_content_len = header[envelope_content_pos];
6041 // maximum level title size is 40 bytes
6042 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6043 // maximum level author size is 30 (51?) bytes
6044 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6048 for (i = 0; i < envelope_header_len; i++)
6049 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6050 level->envelope[0].text[envelope_size++] =
6051 header[envelope_header_pos + 1 + i];
6053 if (envelope_header_len > 0 && envelope_content_len > 0)
6055 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6056 level->envelope[0].text[envelope_size++] = '\n';
6057 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6058 level->envelope[0].text[envelope_size++] = '\n';
6061 for (i = 0; i < envelope_content_len; i++)
6062 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6063 level->envelope[0].text[envelope_size++] =
6064 header[envelope_content_pos + 1 + i];
6066 level->envelope[0].text[envelope_size] = '\0';
6068 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6069 level->envelope[0].ysize = 10;
6070 level->envelope[0].autowrap = TRUE;
6071 level->envelope[0].centered = TRUE;
6073 for (i = 0; i < level_name_len; i++)
6074 level->name[i] = header[level_name_pos + 1 + i];
6075 level->name[level_name_len] = '\0';
6077 for (i = 0; i < level_author_len; i++)
6078 level->author[i] = header[level_author_pos + 1 + i];
6079 level->author[level_author_len] = '\0';
6081 num_yamyam_contents = header[60] | (header[61] << 8);
6082 level->num_yamyam_contents =
6083 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6085 for (i = 0; i < num_yamyam_contents; i++)
6087 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6089 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6090 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6092 if (i < MAX_ELEMENT_CONTENTS)
6093 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6097 fieldx = header[6] | (header[7] << 8);
6098 fieldy = header[8] | (header[9] << 8);
6099 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6100 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6102 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6104 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6105 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6107 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6108 level->field[x][y] = getMappedElement_DC(element_dc);
6111 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6112 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6113 level->field[x][y] = EL_PLAYER_1;
6115 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6116 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6117 level->field[x][y] = EL_PLAYER_2;
6119 level->gems_needed = header[18] | (header[19] << 8);
6121 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6122 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6123 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6124 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6125 level->score[SC_NUT] = header[28] | (header[29] << 8);
6126 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6127 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6128 level->score[SC_BUG] = header[34] | (header[35] << 8);
6129 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6130 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6131 level->score[SC_KEY] = header[40] | (header[41] << 8);
6132 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6134 level->time = header[44] | (header[45] << 8);
6136 level->amoeba_speed = header[46] | (header[47] << 8);
6137 level->time_light = header[48] | (header[49] << 8);
6138 level->time_timegate = header[50] | (header[51] << 8);
6139 level->time_wheel = header[52] | (header[53] << 8);
6140 level->time_magic_wall = header[54] | (header[55] << 8);
6141 level->extra_time = header[56] | (header[57] << 8);
6142 level->shield_normal_time = header[58] | (header[59] << 8);
6144 // shield and extra time elements do not have a score
6145 level->score[SC_SHIELD] = 0;
6146 level->extra_time_score = 0;
6148 // set time for normal and deadly shields to the same value
6149 level->shield_deadly_time = level->shield_normal_time;
6151 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6152 // can slip down from flat walls, like normal walls and steel walls
6153 level->em_slippery_gems = TRUE;
6155 // time score is counted for each 10 seconds left in Diamond Caves levels
6156 level->time_score_base = 10;
6159 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6160 struct LevelFileInfo *level_file_info,
6161 boolean level_info_only)
6163 char *filename = level_file_info->filename;
6165 int num_magic_bytes = 8;
6166 char magic_bytes[num_magic_bytes + 1];
6167 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6169 if (!(file = openFile(filename, MODE_READ)))
6171 level->no_valid_file = TRUE;
6173 if (!level_info_only)
6174 Warn("cannot read level '%s' -- using empty level", filename);
6179 // fseek(file, 0x0000, SEEK_SET);
6181 if (level_file_info->packed)
6183 // read "magic bytes" from start of file
6184 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6185 magic_bytes[0] = '\0';
6187 // check "magic bytes" for correct file format
6188 if (!strPrefix(magic_bytes, "DC2"))
6190 level->no_valid_file = TRUE;
6192 Warn("unknown DC level file '%s' -- using empty level", filename);
6197 if (strPrefix(magic_bytes, "DC2Win95") ||
6198 strPrefix(magic_bytes, "DC2Win98"))
6200 int position_first_level = 0x00fa;
6201 int extra_bytes = 4;
6204 // advance file stream to first level inside the level package
6205 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6207 // each block of level data is followed by block of non-level data
6208 num_levels_to_skip *= 2;
6210 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6211 while (num_levels_to_skip >= 0)
6213 // advance file stream to next level inside the level package
6214 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6216 level->no_valid_file = TRUE;
6218 Warn("cannot fseek in file '%s' -- using empty level", filename);
6223 // skip apparently unused extra bytes following each level
6224 ReadUnusedBytesFromFile(file, extra_bytes);
6226 // read size of next level in level package
6227 skip_bytes = getFile32BitLE(file);
6229 num_levels_to_skip--;
6234 level->no_valid_file = TRUE;
6236 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6242 LoadLevelFromFileStream_DC(file, level);
6248 // ----------------------------------------------------------------------------
6249 // functions for loading SB level
6250 // ----------------------------------------------------------------------------
6252 int getMappedElement_SB(int element_ascii, boolean use_ces)
6260 sb_element_mapping[] =
6262 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6263 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6264 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6265 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6266 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6267 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6268 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6269 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6276 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6277 if (element_ascii == sb_element_mapping[i].ascii)
6278 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6280 return EL_UNDEFINED;
6283 static void SetLevelSettings_SB(struct LevelInfo *level)
6287 level->use_step_counter = TRUE;
6290 level->score[SC_TIME_BONUS] = 0;
6291 level->time_score_base = 1;
6292 level->rate_time_over_score = TRUE;
6295 level->auto_exit_sokoban = TRUE;
6298 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6299 struct LevelFileInfo *level_file_info,
6300 boolean level_info_only)
6302 char *filename = level_file_info->filename;
6303 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6304 char last_comment[MAX_LINE_LEN];
6305 char level_name[MAX_LINE_LEN];
6308 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6309 boolean read_continued_line = FALSE;
6310 boolean reading_playfield = FALSE;
6311 boolean got_valid_playfield_line = FALSE;
6312 boolean invalid_playfield_char = FALSE;
6313 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6314 int file_level_nr = 0;
6315 int x = 0, y = 0; // initialized to make compilers happy
6317 last_comment[0] = '\0';
6318 level_name[0] = '\0';
6320 if (!(file = openFile(filename, MODE_READ)))
6322 level->no_valid_file = TRUE;
6324 if (!level_info_only)
6325 Warn("cannot read level '%s' -- using empty level", filename);
6330 while (!checkEndOfFile(file))
6332 // level successfully read, but next level may follow here
6333 if (!got_valid_playfield_line && reading_playfield)
6335 // read playfield from single level file -- skip remaining file
6336 if (!level_file_info->packed)
6339 if (file_level_nr >= num_levels_to_skip)
6344 last_comment[0] = '\0';
6345 level_name[0] = '\0';
6347 reading_playfield = FALSE;
6350 got_valid_playfield_line = FALSE;
6352 // read next line of input file
6353 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6356 // cut trailing line break (this can be newline and/or carriage return)
6357 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6358 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6361 // copy raw input line for later use (mainly debugging output)
6362 strcpy(line_raw, line);
6364 if (read_continued_line)
6366 // append new line to existing line, if there is enough space
6367 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6368 strcat(previous_line, line_ptr);
6370 strcpy(line, previous_line); // copy storage buffer to line
6372 read_continued_line = FALSE;
6375 // if the last character is '\', continue at next line
6376 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6378 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6379 strcpy(previous_line, line); // copy line to storage buffer
6381 read_continued_line = TRUE;
6387 if (line[0] == '\0')
6390 // extract comment text from comment line
6393 for (line_ptr = line; *line_ptr; line_ptr++)
6394 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6397 strcpy(last_comment, line_ptr);
6402 // extract level title text from line containing level title
6403 if (line[0] == '\'')
6405 strcpy(level_name, &line[1]);
6407 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6408 level_name[strlen(level_name) - 1] = '\0';
6413 // skip lines containing only spaces (or empty lines)
6414 for (line_ptr = line; *line_ptr; line_ptr++)
6415 if (*line_ptr != ' ')
6417 if (*line_ptr == '\0')
6420 // at this point, we have found a line containing part of a playfield
6422 got_valid_playfield_line = TRUE;
6424 if (!reading_playfield)
6426 reading_playfield = TRUE;
6427 invalid_playfield_char = FALSE;
6429 for (x = 0; x < MAX_LEV_FIELDX; x++)
6430 for (y = 0; y < MAX_LEV_FIELDY; y++)
6431 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6436 // start with topmost tile row
6440 // skip playfield line if larger row than allowed
6441 if (y >= MAX_LEV_FIELDY)
6444 // start with leftmost tile column
6447 // read playfield elements from line
6448 for (line_ptr = line; *line_ptr; line_ptr++)
6450 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6452 // stop parsing playfield line if larger column than allowed
6453 if (x >= MAX_LEV_FIELDX)
6456 if (mapped_sb_element == EL_UNDEFINED)
6458 invalid_playfield_char = TRUE;
6463 level->field[x][y] = mapped_sb_element;
6465 // continue with next tile column
6468 level->fieldx = MAX(x, level->fieldx);
6471 if (invalid_playfield_char)
6473 // if first playfield line, treat invalid lines as comment lines
6475 reading_playfield = FALSE;
6480 // continue with next tile row
6488 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6489 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6491 if (!reading_playfield)
6493 level->no_valid_file = TRUE;
6495 Warn("cannot read level '%s' -- using empty level", filename);
6500 if (*level_name != '\0')
6502 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6503 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6505 else if (*last_comment != '\0')
6507 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6508 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6512 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6515 // set all empty fields beyond the border walls to invisible steel wall
6516 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6518 if ((x == 0 || x == level->fieldx - 1 ||
6519 y == 0 || y == level->fieldy - 1) &&
6520 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6521 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6522 level->field, level->fieldx, level->fieldy);
6525 // set special level settings for Sokoban levels
6526 SetLevelSettings_SB(level);
6528 if (load_xsb_to_ces)
6530 // special global settings can now be set in level template
6531 level->use_custom_template = TRUE;
6536 // -------------------------------------------------------------------------
6537 // functions for handling native levels
6538 // -------------------------------------------------------------------------
6540 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6541 struct LevelFileInfo *level_file_info,
6542 boolean level_info_only)
6546 // determine position of requested level inside level package
6547 if (level_file_info->packed)
6548 pos = level_file_info->nr - leveldir_current->first_level;
6550 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6551 level->no_valid_file = TRUE;
6554 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6555 struct LevelFileInfo *level_file_info,
6556 boolean level_info_only)
6558 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6559 level->no_valid_file = TRUE;
6562 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6563 struct LevelFileInfo *level_file_info,
6564 boolean level_info_only)
6568 // determine position of requested level inside level package
6569 if (level_file_info->packed)
6570 pos = level_file_info->nr - leveldir_current->first_level;
6572 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6573 level->no_valid_file = TRUE;
6576 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6577 struct LevelFileInfo *level_file_info,
6578 boolean level_info_only)
6580 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6581 level->no_valid_file = TRUE;
6584 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6586 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6587 CopyNativeLevel_RND_to_BD(level);
6588 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6589 CopyNativeLevel_RND_to_EM(level);
6590 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6591 CopyNativeLevel_RND_to_SP(level);
6592 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6593 CopyNativeLevel_RND_to_MM(level);
6596 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6598 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6599 CopyNativeLevel_BD_to_RND(level);
6600 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6601 CopyNativeLevel_EM_to_RND(level);
6602 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6603 CopyNativeLevel_SP_to_RND(level);
6604 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6605 CopyNativeLevel_MM_to_RND(level);
6608 void SaveNativeLevel(struct LevelInfo *level)
6610 // saving native level files only supported for some game engines
6611 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6612 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6615 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6616 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6617 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6618 char *filename = getLevelFilenameFromBasename(basename);
6620 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6623 boolean success = FALSE;
6625 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6627 CopyNativeLevel_RND_to_BD(level);
6628 // CopyNativeTape_RND_to_BD(level);
6630 success = SaveNativeLevel_BD(filename);
6632 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6634 CopyNativeLevel_RND_to_SP(level);
6635 CopyNativeTape_RND_to_SP(level);
6637 success = SaveNativeLevel_SP(filename);
6641 Request("Native level file saved!", REQ_CONFIRM);
6643 Request("Failed to save native level file!", REQ_CONFIRM);
6647 // ----------------------------------------------------------------------------
6648 // functions for loading generic level
6649 // ----------------------------------------------------------------------------
6651 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6652 struct LevelFileInfo *level_file_info,
6653 boolean level_info_only)
6655 // always start with reliable default values
6656 setLevelInfoToDefaults(level, level_info_only, TRUE);
6658 switch (level_file_info->type)
6660 case LEVEL_FILE_TYPE_RND:
6661 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6664 case LEVEL_FILE_TYPE_BD:
6665 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6666 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6669 case LEVEL_FILE_TYPE_EM:
6670 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6671 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6674 case LEVEL_FILE_TYPE_SP:
6675 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6676 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6679 case LEVEL_FILE_TYPE_MM:
6680 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6681 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6684 case LEVEL_FILE_TYPE_DC:
6685 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6688 case LEVEL_FILE_TYPE_SB:
6689 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6693 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6697 // if level file is invalid, restore level structure to default values
6698 if (level->no_valid_file)
6699 setLevelInfoToDefaults(level, level_info_only, FALSE);
6701 if (check_special_flags("use_native_bd_game_engine"))
6702 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6704 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6705 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6707 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6708 CopyNativeLevel_Native_to_RND(level);
6711 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6713 static struct LevelFileInfo level_file_info;
6715 // always start with reliable default values
6716 setFileInfoToDefaults(&level_file_info);
6718 level_file_info.nr = 0; // unknown level number
6719 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6721 setString(&level_file_info.filename, filename);
6723 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6726 static void LoadLevel_InitVersion(struct LevelInfo *level)
6730 if (leveldir_current == NULL) // only when dumping level
6733 // all engine modifications also valid for levels which use latest engine
6734 if (level->game_version < VERSION_IDENT(3,2,0,5))
6736 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6737 level->time_score_base = 10;
6740 if (leveldir_current->latest_engine)
6742 // ---------- use latest game engine --------------------------------------
6744 /* For all levels which are forced to use the latest game engine version
6745 (normally all but user contributed, private and undefined levels), set
6746 the game engine version to the actual version; this allows for actual
6747 corrections in the game engine to take effect for existing, converted
6748 levels (from "classic" or other existing games) to make the emulation
6749 of the corresponding game more accurate, while (hopefully) not breaking
6750 existing levels created from other players. */
6752 level->game_version = GAME_VERSION_ACTUAL;
6754 /* Set special EM style gems behaviour: EM style gems slip down from
6755 normal, steel and growing wall. As this is a more fundamental change,
6756 it seems better to set the default behaviour to "off" (as it is more
6757 natural) and make it configurable in the level editor (as a property
6758 of gem style elements). Already existing converted levels (neither
6759 private nor contributed levels) are changed to the new behaviour. */
6761 if (level->file_version < FILE_VERSION_2_0)
6762 level->em_slippery_gems = TRUE;
6767 // ---------- use game engine the level was created with --------------------
6769 /* For all levels which are not forced to use the latest game engine
6770 version (normally user contributed, private and undefined levels),
6771 use the version of the game engine the levels were created for.
6773 Since 2.0.1, the game engine version is now directly stored
6774 in the level file (chunk "VERS"), so there is no need anymore
6775 to set the game version from the file version (except for old,
6776 pre-2.0 levels, where the game version is still taken from the
6777 file format version used to store the level -- see above). */
6779 // player was faster than enemies in 1.0.0 and before
6780 if (level->file_version == FILE_VERSION_1_0)
6781 for (i = 0; i < MAX_PLAYERS; i++)
6782 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6784 // default behaviour for EM style gems was "slippery" only in 2.0.1
6785 if (level->game_version == VERSION_IDENT(2,0,1,0))
6786 level->em_slippery_gems = TRUE;
6788 // springs could be pushed over pits before (pre-release version) 2.2.0
6789 if (level->game_version < VERSION_IDENT(2,2,0,0))
6790 level->use_spring_bug = TRUE;
6792 if (level->game_version < VERSION_IDENT(3,2,0,5))
6794 // time orb caused limited time in endless time levels before 3.2.0-5
6795 level->use_time_orb_bug = TRUE;
6797 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6798 level->block_snap_field = FALSE;
6800 // extra time score was same value as time left score before 3.2.0-5
6801 level->extra_time_score = level->score[SC_TIME_BONUS];
6804 if (level->game_version < VERSION_IDENT(3,2,0,7))
6806 // default behaviour for snapping was "not continuous" before 3.2.0-7
6807 level->continuous_snapping = FALSE;
6810 // only few elements were able to actively move into acid before 3.1.0
6811 // trigger settings did not exist before 3.1.0; set to default "any"
6812 if (level->game_version < VERSION_IDENT(3,1,0,0))
6814 // correct "can move into acid" settings (all zero in old levels)
6816 level->can_move_into_acid_bits = 0; // nothing can move into acid
6817 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6819 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6820 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6821 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6822 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6824 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6825 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6827 // correct trigger settings (stored as zero == "none" in old levels)
6829 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6831 int element = EL_CUSTOM_START + i;
6832 struct ElementInfo *ei = &element_info[element];
6834 for (j = 0; j < ei->num_change_pages; j++)
6836 struct ElementChangeInfo *change = &ei->change_page[j];
6838 change->trigger_player = CH_PLAYER_ANY;
6839 change->trigger_page = CH_PAGE_ANY;
6844 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6846 int element = EL_CUSTOM_256;
6847 struct ElementInfo *ei = &element_info[element];
6848 struct ElementChangeInfo *change = &ei->change_page[0];
6850 /* This is needed to fix a problem that was caused by a bugfix in function
6851 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6852 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6853 not replace walkable elements, but instead just placed the player on it,
6854 without placing the Sokoban field under the player). Unfortunately, this
6855 breaks "Snake Bite" style levels when the snake is halfway through a door
6856 that just closes (the snake head is still alive and can be moved in this
6857 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6858 player (without Sokoban element) which then gets killed as designed). */
6860 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6861 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6862 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6863 change->target_element = EL_PLAYER_1;
6866 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6867 if (level->game_version < VERSION_IDENT(3,2,5,0))
6869 /* This is needed to fix a problem that was caused by a bugfix in function
6870 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6871 corrects the behaviour when a custom element changes to another custom
6872 element with a higher element number that has change actions defined.
6873 Normally, only one change per frame is allowed for custom elements.
6874 Therefore, it is checked if a custom element already changed in the
6875 current frame; if it did, subsequent changes are suppressed.
6876 Unfortunately, this is only checked for element changes, but not for
6877 change actions, which are still executed. As the function above loops
6878 through all custom elements from lower to higher, an element change
6879 resulting in a lower CE number won't be checked again, while a target
6880 element with a higher number will also be checked, and potential change
6881 actions will get executed for this CE, too (which is wrong), while
6882 further changes are ignored (which is correct). As this bugfix breaks
6883 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6884 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6885 behaviour for existing levels and tapes that make use of this bug */
6887 level->use_action_after_change_bug = TRUE;
6890 // not centering level after relocating player was default only in 3.2.3
6891 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6892 level->shifted_relocation = TRUE;
6894 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6895 if (level->game_version < VERSION_IDENT(3,2,6,0))
6896 level->em_explodes_by_fire = TRUE;
6898 // levels were solved by the first player entering an exit up to 4.1.0.0
6899 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6900 level->solved_by_one_player = TRUE;
6902 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6903 if (level->game_version < VERSION_IDENT(4,1,1,1))
6904 level->use_life_bugs = TRUE;
6906 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6907 if (level->game_version < VERSION_IDENT(4,1,1,1))
6908 level->sb_objects_needed = FALSE;
6910 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6911 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6912 level->finish_dig_collect = FALSE;
6914 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6915 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6916 level->keep_walkable_ce = TRUE;
6919 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6921 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6924 // check if this level is (not) a Sokoban level
6925 for (y = 0; y < level->fieldy; y++)
6926 for (x = 0; x < level->fieldx; x++)
6927 if (!IS_SB_ELEMENT(Tile[x][y]))
6928 is_sokoban_level = FALSE;
6930 if (is_sokoban_level)
6932 // set special level settings for Sokoban levels
6933 SetLevelSettings_SB(level);
6937 static void LoadLevel_InitSettings(struct LevelInfo *level)
6939 // adjust level settings for (non-native) Sokoban-style levels
6940 LoadLevel_InitSettings_SB(level);
6942 // rename levels with title "nameless level" or if renaming is forced
6943 if (leveldir_current->empty_level_name != NULL &&
6944 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6945 leveldir_current->force_level_name))
6946 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6947 leveldir_current->empty_level_name, level_nr);
6950 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6954 // map elements that have changed in newer versions
6955 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6956 level->game_version);
6957 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6958 for (x = 0; x < 3; x++)
6959 for (y = 0; y < 3; y++)
6960 level->yamyam_content[i].e[x][y] =
6961 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6962 level->game_version);
6966 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6970 // map custom element change events that have changed in newer versions
6971 // (these following values were accidentally changed in version 3.0.1)
6972 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6973 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6975 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6977 int element = EL_CUSTOM_START + i;
6979 // order of checking and copying events to be mapped is important
6980 // (do not change the start and end value -- they are constant)
6981 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6983 if (HAS_CHANGE_EVENT(element, j - 2))
6985 SET_CHANGE_EVENT(element, j - 2, FALSE);
6986 SET_CHANGE_EVENT(element, j, TRUE);
6990 // order of checking and copying events to be mapped is important
6991 // (do not change the start and end value -- they are constant)
6992 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6994 if (HAS_CHANGE_EVENT(element, j - 1))
6996 SET_CHANGE_EVENT(element, j - 1, FALSE);
6997 SET_CHANGE_EVENT(element, j, TRUE);
7003 // initialize "can_change" field for old levels with only one change page
7004 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7006 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7008 int element = EL_CUSTOM_START + i;
7010 if (CAN_CHANGE(element))
7011 element_info[element].change->can_change = TRUE;
7015 // correct custom element values (for old levels without these options)
7016 if (level->game_version < VERSION_IDENT(3,1,1,0))
7018 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7020 int element = EL_CUSTOM_START + i;
7021 struct ElementInfo *ei = &element_info[element];
7023 if (ei->access_direction == MV_NO_DIRECTION)
7024 ei->access_direction = MV_ALL_DIRECTIONS;
7028 // correct custom element values (fix invalid values for all versions)
7031 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7033 int element = EL_CUSTOM_START + i;
7034 struct ElementInfo *ei = &element_info[element];
7036 for (j = 0; j < ei->num_change_pages; j++)
7038 struct ElementChangeInfo *change = &ei->change_page[j];
7040 if (change->trigger_player == CH_PLAYER_NONE)
7041 change->trigger_player = CH_PLAYER_ANY;
7043 if (change->trigger_side == CH_SIDE_NONE)
7044 change->trigger_side = CH_SIDE_ANY;
7049 // initialize "can_explode" field for old levels which did not store this
7050 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7051 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7053 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7055 int element = EL_CUSTOM_START + i;
7057 if (EXPLODES_1X1_OLD(element))
7058 element_info[element].explosion_type = EXPLODES_1X1;
7060 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7061 EXPLODES_SMASHED(element) ||
7062 EXPLODES_IMPACT(element)));
7066 // correct previously hard-coded move delay values for maze runner style
7067 if (level->game_version < VERSION_IDENT(3,1,1,0))
7069 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7071 int element = EL_CUSTOM_START + i;
7073 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7075 // previously hard-coded and therefore ignored
7076 element_info[element].move_delay_fixed = 9;
7077 element_info[element].move_delay_random = 0;
7082 // set some other uninitialized values of custom elements in older levels
7083 if (level->game_version < VERSION_IDENT(3,1,0,0))
7085 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7087 int element = EL_CUSTOM_START + i;
7089 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7091 element_info[element].explosion_delay = 17;
7092 element_info[element].ignition_delay = 8;
7096 // set mouse click change events to work for left/middle/right mouse button
7097 if (level->game_version < VERSION_IDENT(4,2,3,0))
7099 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7101 int element = EL_CUSTOM_START + i;
7102 struct ElementInfo *ei = &element_info[element];
7104 for (j = 0; j < ei->num_change_pages; j++)
7106 struct ElementChangeInfo *change = &ei->change_page[j];
7108 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7109 change->has_event[CE_PRESSED_BY_MOUSE] ||
7110 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7111 change->has_event[CE_MOUSE_PRESSED_ON_X])
7112 change->trigger_side = CH_SIDE_ANY;
7118 static void LoadLevel_InitElements(struct LevelInfo *level)
7120 LoadLevel_InitStandardElements(level);
7122 if (level->file_has_custom_elements)
7123 LoadLevel_InitCustomElements(level);
7125 // initialize element properties for level editor etc.
7126 InitElementPropertiesEngine(level->game_version);
7127 InitElementPropertiesGfxElement();
7130 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7134 // map elements that have changed in newer versions
7135 for (y = 0; y < level->fieldy; y++)
7136 for (x = 0; x < level->fieldx; x++)
7137 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7138 level->game_version);
7140 // clear unused playfield data (nicer if level gets resized in editor)
7141 for (x = 0; x < MAX_LEV_FIELDX; x++)
7142 for (y = 0; y < MAX_LEV_FIELDY; y++)
7143 if (x >= level->fieldx || y >= level->fieldy)
7144 level->field[x][y] = EL_EMPTY;
7146 // copy elements to runtime playfield array
7147 for (x = 0; x < MAX_LEV_FIELDX; x++)
7148 for (y = 0; y < MAX_LEV_FIELDY; y++)
7149 Tile[x][y] = level->field[x][y];
7151 // initialize level size variables for faster access
7152 lev_fieldx = level->fieldx;
7153 lev_fieldy = level->fieldy;
7155 // determine border element for this level
7156 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7157 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7162 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7164 struct LevelFileInfo *level_file_info = &level->file_info;
7166 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7167 CopyNativeLevel_RND_to_Native(level);
7170 static void LoadLevelTemplate_LoadAndInit(void)
7172 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7174 LoadLevel_InitVersion(&level_template);
7175 LoadLevel_InitElements(&level_template);
7176 LoadLevel_InitSettings(&level_template);
7178 ActivateLevelTemplate();
7181 void LoadLevelTemplate(int nr)
7183 if (!fileExists(getGlobalLevelTemplateFilename()))
7185 Warn("no level template found for this level");
7190 setLevelFileInfo(&level_template.file_info, nr);
7192 LoadLevelTemplate_LoadAndInit();
7195 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7197 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7199 LoadLevelTemplate_LoadAndInit();
7202 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7204 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7206 if (level.use_custom_template)
7208 if (network_level != NULL)
7209 LoadNetworkLevelTemplate(network_level);
7211 LoadLevelTemplate(-1);
7214 LoadLevel_InitVersion(&level);
7215 LoadLevel_InitElements(&level);
7216 LoadLevel_InitPlayfield(&level);
7217 LoadLevel_InitSettings(&level);
7219 LoadLevel_InitNativeEngines(&level);
7222 void LoadLevel(int nr)
7224 SetLevelSetInfo(leveldir_current->identifier, nr);
7226 setLevelFileInfo(&level.file_info, nr);
7228 LoadLevel_LoadAndInit(NULL);
7231 void LoadLevelInfoOnly(int nr)
7233 setLevelFileInfo(&level.file_info, nr);
7235 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7238 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7240 SetLevelSetInfo(network_level->leveldir_identifier,
7241 network_level->file_info.nr);
7243 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7245 LoadLevel_LoadAndInit(network_level);
7248 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7252 chunk_size += putFileVersion(file, level->file_version);
7253 chunk_size += putFileVersion(file, level->game_version);
7258 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7262 chunk_size += putFile16BitBE(file, level->creation_date.year);
7263 chunk_size += putFile8Bit(file, level->creation_date.month);
7264 chunk_size += putFile8Bit(file, level->creation_date.day);
7269 #if ENABLE_HISTORIC_CHUNKS
7270 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7274 putFile8Bit(file, level->fieldx);
7275 putFile8Bit(file, level->fieldy);
7277 putFile16BitBE(file, level->time);
7278 putFile16BitBE(file, level->gems_needed);
7280 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7281 putFile8Bit(file, level->name[i]);
7283 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7284 putFile8Bit(file, level->score[i]);
7286 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7287 for (y = 0; y < 3; y++)
7288 for (x = 0; x < 3; x++)
7289 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7290 level->yamyam_content[i].e[x][y]));
7291 putFile8Bit(file, level->amoeba_speed);
7292 putFile8Bit(file, level->time_magic_wall);
7293 putFile8Bit(file, level->time_wheel);
7294 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7295 level->amoeba_content));
7296 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7297 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7298 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7299 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7301 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7303 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7304 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7305 putFile32BitBE(file, level->can_move_into_acid_bits);
7306 putFile8Bit(file, level->dont_collide_with_bits);
7308 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7309 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7311 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7312 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7313 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7315 putFile8Bit(file, level->game_engine_type);
7317 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7321 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7326 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7327 chunk_size += putFile8Bit(file, level->name[i]);
7332 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7337 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7338 chunk_size += putFile8Bit(file, level->author[i]);
7343 #if ENABLE_HISTORIC_CHUNKS
7344 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7349 for (y = 0; y < level->fieldy; y++)
7350 for (x = 0; x < level->fieldx; x++)
7351 if (level->encoding_16bit_field)
7352 chunk_size += putFile16BitBE(file, level->field[x][y]);
7354 chunk_size += putFile8Bit(file, level->field[x][y]);
7360 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7365 for (y = 0; y < level->fieldy; y++)
7366 for (x = 0; x < level->fieldx; x++)
7367 chunk_size += putFile16BitBE(file, level->field[x][y]);
7372 #if ENABLE_HISTORIC_CHUNKS
7373 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7377 putFile8Bit(file, EL_YAMYAM);
7378 putFile8Bit(file, level->num_yamyam_contents);
7379 putFile8Bit(file, 0);
7380 putFile8Bit(file, 0);
7382 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7383 for (y = 0; y < 3; y++)
7384 for (x = 0; x < 3; x++)
7385 if (level->encoding_16bit_field)
7386 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7388 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7392 #if ENABLE_HISTORIC_CHUNKS
7393 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7396 int num_contents, content_xsize, content_ysize;
7397 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7399 if (element == EL_YAMYAM)
7401 num_contents = level->num_yamyam_contents;
7405 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7406 for (y = 0; y < 3; y++)
7407 for (x = 0; x < 3; x++)
7408 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7410 else if (element == EL_BD_AMOEBA)
7416 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7417 for (y = 0; y < 3; y++)
7418 for (x = 0; x < 3; x++)
7419 content_array[i][x][y] = EL_EMPTY;
7420 content_array[0][0][0] = level->amoeba_content;
7424 // chunk header already written -- write empty chunk data
7425 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7427 Warn("cannot save content for element '%d'", element);
7432 putFile16BitBE(file, element);
7433 putFile8Bit(file, num_contents);
7434 putFile8Bit(file, content_xsize);
7435 putFile8Bit(file, content_ysize);
7437 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7439 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7440 for (y = 0; y < 3; y++)
7441 for (x = 0; x < 3; x++)
7442 putFile16BitBE(file, content_array[i][x][y]);
7446 #if ENABLE_HISTORIC_CHUNKS
7447 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7449 int envelope_nr = element - EL_ENVELOPE_1;
7450 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7454 chunk_size += putFile16BitBE(file, element);
7455 chunk_size += putFile16BitBE(file, envelope_len);
7456 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7457 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7459 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7460 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7462 for (i = 0; i < envelope_len; i++)
7463 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7469 #if ENABLE_HISTORIC_CHUNKS
7470 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7471 int num_changed_custom_elements)
7475 putFile16BitBE(file, num_changed_custom_elements);
7477 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7479 int element = EL_CUSTOM_START + i;
7481 struct ElementInfo *ei = &element_info[element];
7483 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7485 if (check < num_changed_custom_elements)
7487 putFile16BitBE(file, element);
7488 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7495 if (check != num_changed_custom_elements) // should not happen
7496 Warn("inconsistent number of custom element properties");
7500 #if ENABLE_HISTORIC_CHUNKS
7501 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7502 int num_changed_custom_elements)
7506 putFile16BitBE(file, num_changed_custom_elements);
7508 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7510 int element = EL_CUSTOM_START + i;
7512 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7514 if (check < num_changed_custom_elements)
7516 putFile16BitBE(file, element);
7517 putFile16BitBE(file, element_info[element].change->target_element);
7524 if (check != num_changed_custom_elements) // should not happen
7525 Warn("inconsistent number of custom target elements");
7529 #if ENABLE_HISTORIC_CHUNKS
7530 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7531 int num_changed_custom_elements)
7533 int i, j, x, y, check = 0;
7535 putFile16BitBE(file, num_changed_custom_elements);
7537 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7539 int element = EL_CUSTOM_START + i;
7540 struct ElementInfo *ei = &element_info[element];
7542 if (ei->modified_settings)
7544 if (check < num_changed_custom_elements)
7546 putFile16BitBE(file, element);
7548 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7549 putFile8Bit(file, ei->description[j]);
7551 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7553 // some free bytes for future properties and padding
7554 WriteUnusedBytesToFile(file, 7);
7556 putFile8Bit(file, ei->use_gfx_element);
7557 putFile16BitBE(file, ei->gfx_element_initial);
7559 putFile8Bit(file, ei->collect_score_initial);
7560 putFile8Bit(file, ei->collect_count_initial);
7562 putFile16BitBE(file, ei->push_delay_fixed);
7563 putFile16BitBE(file, ei->push_delay_random);
7564 putFile16BitBE(file, ei->move_delay_fixed);
7565 putFile16BitBE(file, ei->move_delay_random);
7567 putFile16BitBE(file, ei->move_pattern);
7568 putFile8Bit(file, ei->move_direction_initial);
7569 putFile8Bit(file, ei->move_stepsize);
7571 for (y = 0; y < 3; y++)
7572 for (x = 0; x < 3; x++)
7573 putFile16BitBE(file, ei->content.e[x][y]);
7575 putFile32BitBE(file, ei->change->events);
7577 putFile16BitBE(file, ei->change->target_element);
7579 putFile16BitBE(file, ei->change->delay_fixed);
7580 putFile16BitBE(file, ei->change->delay_random);
7581 putFile16BitBE(file, ei->change->delay_frames);
7583 putFile16BitBE(file, ei->change->initial_trigger_element);
7585 putFile8Bit(file, ei->change->explode);
7586 putFile8Bit(file, ei->change->use_target_content);
7587 putFile8Bit(file, ei->change->only_if_complete);
7588 putFile8Bit(file, ei->change->use_random_replace);
7590 putFile8Bit(file, ei->change->random_percentage);
7591 putFile8Bit(file, ei->change->replace_when);
7593 for (y = 0; y < 3; y++)
7594 for (x = 0; x < 3; x++)
7595 putFile16BitBE(file, ei->change->content.e[x][y]);
7597 putFile8Bit(file, ei->slippery_type);
7599 // some free bytes for future properties and padding
7600 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7607 if (check != num_changed_custom_elements) // should not happen
7608 Warn("inconsistent number of custom element properties");
7612 #if ENABLE_HISTORIC_CHUNKS
7613 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7615 struct ElementInfo *ei = &element_info[element];
7618 // ---------- custom element base property values (96 bytes) ----------------
7620 putFile16BitBE(file, element);
7622 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7623 putFile8Bit(file, ei->description[i]);
7625 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7627 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7629 putFile8Bit(file, ei->num_change_pages);
7631 putFile16BitBE(file, ei->ce_value_fixed_initial);
7632 putFile16BitBE(file, ei->ce_value_random_initial);
7633 putFile8Bit(file, ei->use_last_ce_value);
7635 putFile8Bit(file, ei->use_gfx_element);
7636 putFile16BitBE(file, ei->gfx_element_initial);
7638 putFile8Bit(file, ei->collect_score_initial);
7639 putFile8Bit(file, ei->collect_count_initial);
7641 putFile8Bit(file, ei->drop_delay_fixed);
7642 putFile8Bit(file, ei->push_delay_fixed);
7643 putFile8Bit(file, ei->drop_delay_random);
7644 putFile8Bit(file, ei->push_delay_random);
7645 putFile16BitBE(file, ei->move_delay_fixed);
7646 putFile16BitBE(file, ei->move_delay_random);
7648 // bits 0 - 15 of "move_pattern" ...
7649 putFile16BitBE(file, ei->move_pattern & 0xffff);
7650 putFile8Bit(file, ei->move_direction_initial);
7651 putFile8Bit(file, ei->move_stepsize);
7653 putFile8Bit(file, ei->slippery_type);
7655 for (y = 0; y < 3; y++)
7656 for (x = 0; x < 3; x++)
7657 putFile16BitBE(file, ei->content.e[x][y]);
7659 putFile16BitBE(file, ei->move_enter_element);
7660 putFile16BitBE(file, ei->move_leave_element);
7661 putFile8Bit(file, ei->move_leave_type);
7663 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7664 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7666 putFile8Bit(file, ei->access_direction);
7668 putFile8Bit(file, ei->explosion_delay);
7669 putFile8Bit(file, ei->ignition_delay);
7670 putFile8Bit(file, ei->explosion_type);
7672 // some free bytes for future custom property values and padding
7673 WriteUnusedBytesToFile(file, 1);
7675 // ---------- change page property values (48 bytes) ------------------------
7677 for (i = 0; i < ei->num_change_pages; i++)
7679 struct ElementChangeInfo *change = &ei->change_page[i];
7680 unsigned int event_bits;
7682 // bits 0 - 31 of "has_event[]" ...
7684 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7685 if (change->has_event[j])
7686 event_bits |= (1u << j);
7687 putFile32BitBE(file, event_bits);
7689 putFile16BitBE(file, change->target_element);
7691 putFile16BitBE(file, change->delay_fixed);
7692 putFile16BitBE(file, change->delay_random);
7693 putFile16BitBE(file, change->delay_frames);
7695 putFile16BitBE(file, change->initial_trigger_element);
7697 putFile8Bit(file, change->explode);
7698 putFile8Bit(file, change->use_target_content);
7699 putFile8Bit(file, change->only_if_complete);
7700 putFile8Bit(file, change->use_random_replace);
7702 putFile8Bit(file, change->random_percentage);
7703 putFile8Bit(file, change->replace_when);
7705 for (y = 0; y < 3; y++)
7706 for (x = 0; x < 3; x++)
7707 putFile16BitBE(file, change->target_content.e[x][y]);
7709 putFile8Bit(file, change->can_change);
7711 putFile8Bit(file, change->trigger_side);
7713 putFile8Bit(file, change->trigger_player);
7714 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7715 log_2(change->trigger_page)));
7717 putFile8Bit(file, change->has_action);
7718 putFile8Bit(file, change->action_type);
7719 putFile8Bit(file, change->action_mode);
7720 putFile16BitBE(file, change->action_arg);
7722 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7724 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7725 if (change->has_event[j])
7726 event_bits |= (1u << (j - 32));
7727 putFile8Bit(file, event_bits);
7732 #if ENABLE_HISTORIC_CHUNKS
7733 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7735 struct ElementInfo *ei = &element_info[element];
7736 struct ElementGroupInfo *group = ei->group;
7739 putFile16BitBE(file, element);
7741 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7742 putFile8Bit(file, ei->description[i]);
7744 putFile8Bit(file, group->num_elements);
7746 putFile8Bit(file, ei->use_gfx_element);
7747 putFile16BitBE(file, ei->gfx_element_initial);
7749 putFile8Bit(file, group->choice_mode);
7751 // some free bytes for future values and padding
7752 WriteUnusedBytesToFile(file, 3);
7754 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7755 putFile16BitBE(file, group->element[i]);
7759 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7760 boolean write_element)
7762 int save_type = entry->save_type;
7763 int data_type = entry->data_type;
7764 int conf_type = entry->conf_type;
7765 int byte_mask = conf_type & CONF_MASK_BYTES;
7766 int element = entry->element;
7767 int default_value = entry->default_value;
7769 boolean modified = FALSE;
7771 if (byte_mask != CONF_MASK_MULTI_BYTES)
7773 void *value_ptr = entry->value;
7774 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7777 // check if any settings have been modified before saving them
7778 if (value != default_value)
7781 // do not save if explicitly told or if unmodified default settings
7782 if ((save_type == SAVE_CONF_NEVER) ||
7783 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7787 num_bytes += putFile16BitBE(file, element);
7789 num_bytes += putFile8Bit(file, conf_type);
7790 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7791 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7792 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7795 else if (data_type == TYPE_STRING)
7797 char *default_string = entry->default_string;
7798 char *string = (char *)(entry->value);
7799 int string_length = strlen(string);
7802 // check if any settings have been modified before saving them
7803 if (!strEqual(string, default_string))
7806 // do not save if explicitly told or if unmodified default settings
7807 if ((save_type == SAVE_CONF_NEVER) ||
7808 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7812 num_bytes += putFile16BitBE(file, element);
7814 num_bytes += putFile8Bit(file, conf_type);
7815 num_bytes += putFile16BitBE(file, string_length);
7817 for (i = 0; i < string_length; i++)
7818 num_bytes += putFile8Bit(file, string[i]);
7820 else if (data_type == TYPE_ELEMENT_LIST)
7822 int *element_array = (int *)(entry->value);
7823 int num_elements = *(int *)(entry->num_entities);
7826 // check if any settings have been modified before saving them
7827 for (i = 0; i < num_elements; i++)
7828 if (element_array[i] != default_value)
7831 // do not save if explicitly told or if unmodified default settings
7832 if ((save_type == SAVE_CONF_NEVER) ||
7833 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7837 num_bytes += putFile16BitBE(file, element);
7839 num_bytes += putFile8Bit(file, conf_type);
7840 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7842 for (i = 0; i < num_elements; i++)
7843 num_bytes += putFile16BitBE(file, element_array[i]);
7845 else if (data_type == TYPE_CONTENT_LIST)
7847 struct Content *content = (struct Content *)(entry->value);
7848 int num_contents = *(int *)(entry->num_entities);
7851 // check if any settings have been modified before saving them
7852 for (i = 0; i < num_contents; i++)
7853 for (y = 0; y < 3; y++)
7854 for (x = 0; x < 3; x++)
7855 if (content[i].e[x][y] != default_value)
7858 // do not save if explicitly told or if unmodified default settings
7859 if ((save_type == SAVE_CONF_NEVER) ||
7860 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7864 num_bytes += putFile16BitBE(file, element);
7866 num_bytes += putFile8Bit(file, conf_type);
7867 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7869 for (i = 0; i < num_contents; i++)
7870 for (y = 0; y < 3; y++)
7871 for (x = 0; x < 3; x++)
7872 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7878 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7883 li = *level; // copy level data into temporary buffer
7885 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7886 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7891 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7896 li = *level; // copy level data into temporary buffer
7898 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7899 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7904 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7906 int envelope_nr = element - EL_ENVELOPE_1;
7910 chunk_size += putFile16BitBE(file, element);
7912 // copy envelope data into temporary buffer
7913 xx_envelope = level->envelope[envelope_nr];
7915 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7916 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7921 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7923 struct ElementInfo *ei = &element_info[element];
7927 chunk_size += putFile16BitBE(file, element);
7929 xx_ei = *ei; // copy element data into temporary buffer
7931 // set default description string for this specific element
7932 strcpy(xx_default_description, getDefaultElementDescription(ei));
7934 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7935 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7937 for (i = 0; i < ei->num_change_pages; i++)
7939 struct ElementChangeInfo *change = &ei->change_page[i];
7941 xx_current_change_page = i;
7943 xx_change = *change; // copy change data into temporary buffer
7946 setEventBitsFromEventFlags(change);
7948 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7949 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7956 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7958 struct ElementInfo *ei = &element_info[element];
7959 struct ElementGroupInfo *group = ei->group;
7963 chunk_size += putFile16BitBE(file, element);
7965 xx_ei = *ei; // copy element data into temporary buffer
7966 xx_group = *group; // copy group data into temporary buffer
7968 // set default description string for this specific element
7969 strcpy(xx_default_description, getDefaultElementDescription(ei));
7971 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7972 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7977 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7979 struct ElementInfo *ei = &element_info[element];
7983 chunk_size += putFile16BitBE(file, element);
7985 xx_ei = *ei; // copy element data into temporary buffer
7987 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7988 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7993 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7994 boolean save_as_template)
8000 if (!(file = fopen(filename, MODE_WRITE)))
8002 Warn("cannot save level file '%s'", filename);
8007 level->file_version = FILE_VERSION_ACTUAL;
8008 level->game_version = GAME_VERSION_ACTUAL;
8010 level->creation_date = getCurrentDate();
8012 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8013 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8015 chunk_size = SaveLevel_VERS(NULL, level);
8016 putFileChunkBE(file, "VERS", chunk_size);
8017 SaveLevel_VERS(file, level);
8019 chunk_size = SaveLevel_DATE(NULL, level);
8020 putFileChunkBE(file, "DATE", chunk_size);
8021 SaveLevel_DATE(file, level);
8023 chunk_size = SaveLevel_NAME(NULL, level);
8024 putFileChunkBE(file, "NAME", chunk_size);
8025 SaveLevel_NAME(file, level);
8027 chunk_size = SaveLevel_AUTH(NULL, level);
8028 putFileChunkBE(file, "AUTH", chunk_size);
8029 SaveLevel_AUTH(file, level);
8031 chunk_size = SaveLevel_INFO(NULL, level);
8032 putFileChunkBE(file, "INFO", chunk_size);
8033 SaveLevel_INFO(file, level);
8035 chunk_size = SaveLevel_BODY(NULL, level);
8036 putFileChunkBE(file, "BODY", chunk_size);
8037 SaveLevel_BODY(file, level);
8039 chunk_size = SaveLevel_ELEM(NULL, level);
8040 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8042 putFileChunkBE(file, "ELEM", chunk_size);
8043 SaveLevel_ELEM(file, level);
8046 for (i = 0; i < NUM_ENVELOPES; i++)
8048 int element = EL_ENVELOPE_1 + i;
8050 chunk_size = SaveLevel_NOTE(NULL, level, element);
8051 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8053 putFileChunkBE(file, "NOTE", chunk_size);
8054 SaveLevel_NOTE(file, level, element);
8058 // if not using template level, check for non-default custom/group elements
8059 if (!level->use_custom_template || save_as_template)
8061 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8063 int element = EL_CUSTOM_START + i;
8065 chunk_size = SaveLevel_CUSX(NULL, level, element);
8066 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8068 putFileChunkBE(file, "CUSX", chunk_size);
8069 SaveLevel_CUSX(file, level, element);
8073 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8075 int element = EL_GROUP_START + i;
8077 chunk_size = SaveLevel_GRPX(NULL, level, element);
8078 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8080 putFileChunkBE(file, "GRPX", chunk_size);
8081 SaveLevel_GRPX(file, level, element);
8085 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8087 int element = GET_EMPTY_ELEMENT(i);
8089 chunk_size = SaveLevel_EMPX(NULL, level, element);
8090 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8092 putFileChunkBE(file, "EMPX", chunk_size);
8093 SaveLevel_EMPX(file, level, element);
8100 SetFilePermissions(filename, PERMS_PRIVATE);
8103 void SaveLevel(int nr)
8105 char *filename = getDefaultLevelFilename(nr);
8107 SaveLevelFromFilename(&level, filename, FALSE);
8110 void SaveLevelTemplate(void)
8112 char *filename = getLocalLevelTemplateFilename();
8114 SaveLevelFromFilename(&level, filename, TRUE);
8117 boolean SaveLevelChecked(int nr)
8119 char *filename = getDefaultLevelFilename(nr);
8120 boolean new_level = !fileExists(filename);
8121 boolean level_saved = FALSE;
8123 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8128 Request("Level saved!", REQ_CONFIRM);
8136 void DumpLevel(struct LevelInfo *level)
8138 if (level->no_level_file || level->no_valid_file)
8140 Warn("cannot dump -- no valid level file found");
8146 Print("Level xxx (file version %08d, game version %08d)\n",
8147 level->file_version, level->game_version);
8150 Print("Level author: '%s'\n", level->author);
8151 Print("Level title: '%s'\n", level->name);
8153 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8155 Print("Level time: %d seconds\n", level->time);
8156 Print("Gems needed: %d\n", level->gems_needed);
8158 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8159 Print("Time for wheel: %d seconds\n", level->time_wheel);
8160 Print("Time for light: %d seconds\n", level->time_light);
8161 Print("Time for timegate: %d seconds\n", level->time_timegate);
8163 Print("Amoeba speed: %d\n", level->amoeba_speed);
8166 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8167 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8168 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8169 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8170 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8171 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8177 for (i = 0; i < NUM_ENVELOPES; i++)
8179 char *text = level->envelope[i].text;
8180 int text_len = strlen(text);
8181 boolean has_text = FALSE;
8183 for (j = 0; j < text_len; j++)
8184 if (text[j] != ' ' && text[j] != '\n')
8190 Print("Envelope %d:\n'%s'\n", i + 1, text);
8198 void DumpLevels(void)
8200 static LevelDirTree *dumplevel_leveldir = NULL;
8202 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8203 global.dumplevel_leveldir);
8205 if (dumplevel_leveldir == NULL)
8206 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8208 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8209 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8210 Fail("no such level number: %d", global.dumplevel_level_nr);
8212 leveldir_current = dumplevel_leveldir;
8214 LoadLevel(global.dumplevel_level_nr);
8221 // ============================================================================
8222 // tape file functions
8223 // ============================================================================
8225 static void setTapeInfoToDefaults(void)
8229 // always start with reliable default values (empty tape)
8232 // default values (also for pre-1.2 tapes) with only the first player
8233 tape.player_participates[0] = TRUE;
8234 for (i = 1; i < MAX_PLAYERS; i++)
8235 tape.player_participates[i] = FALSE;
8237 // at least one (default: the first) player participates in every tape
8238 tape.num_participating_players = 1;
8240 tape.property_bits = TAPE_PROPERTY_NONE;
8242 tape.level_nr = level_nr;
8244 tape.changed = FALSE;
8245 tape.solved = FALSE;
8247 tape.recording = FALSE;
8248 tape.playing = FALSE;
8249 tape.pausing = FALSE;
8251 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8252 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8254 tape.no_info_chunk = TRUE;
8255 tape.no_valid_file = FALSE;
8258 static int getTapePosSize(struct TapeInfo *tape)
8260 int tape_pos_size = 0;
8262 if (tape->use_key_actions)
8263 tape_pos_size += tape->num_participating_players;
8265 if (tape->use_mouse_actions)
8266 tape_pos_size += 3; // x and y position and mouse button mask
8268 tape_pos_size += 1; // tape action delay value
8270 return tape_pos_size;
8273 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8275 tape->use_key_actions = FALSE;
8276 tape->use_mouse_actions = FALSE;
8278 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8279 tape->use_key_actions = TRUE;
8281 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8282 tape->use_mouse_actions = TRUE;
8285 static int getTapeActionValue(struct TapeInfo *tape)
8287 return (tape->use_key_actions &&
8288 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8289 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8290 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8291 TAPE_ACTIONS_DEFAULT);
8294 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8296 tape->file_version = getFileVersion(file);
8297 tape->game_version = getFileVersion(file);
8302 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8306 tape->random_seed = getFile32BitBE(file);
8307 tape->date = getFile32BitBE(file);
8308 tape->length = getFile32BitBE(file);
8310 // read header fields that are new since version 1.2
8311 if (tape->file_version >= FILE_VERSION_1_2)
8313 byte store_participating_players = getFile8Bit(file);
8316 // since version 1.2, tapes store which players participate in the tape
8317 tape->num_participating_players = 0;
8318 for (i = 0; i < MAX_PLAYERS; i++)
8320 tape->player_participates[i] = FALSE;
8322 if (store_participating_players & (1 << i))
8324 tape->player_participates[i] = TRUE;
8325 tape->num_participating_players++;
8329 setTapeActionFlags(tape, getFile8Bit(file));
8331 tape->property_bits = getFile8Bit(file);
8332 tape->solved = getFile8Bit(file);
8334 engine_version = getFileVersion(file);
8335 if (engine_version > 0)
8336 tape->engine_version = engine_version;
8338 tape->engine_version = tape->game_version;
8344 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8346 tape->scr_fieldx = getFile8Bit(file);
8347 tape->scr_fieldy = getFile8Bit(file);
8352 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8354 char *level_identifier = NULL;
8355 int level_identifier_size;
8358 tape->no_info_chunk = FALSE;
8360 level_identifier_size = getFile16BitBE(file);
8362 level_identifier = checked_malloc(level_identifier_size);
8364 for (i = 0; i < level_identifier_size; i++)
8365 level_identifier[i] = getFile8Bit(file);
8367 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8368 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8370 checked_free(level_identifier);
8372 tape->level_nr = getFile16BitBE(file);
8374 chunk_size = 2 + level_identifier_size + 2;
8379 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8382 int tape_pos_size = getTapePosSize(tape);
8383 int chunk_size_expected = tape_pos_size * tape->length;
8385 if (chunk_size_expected != chunk_size)
8387 ReadUnusedBytesFromFile(file, chunk_size);
8388 return chunk_size_expected;
8391 for (i = 0; i < tape->length; i++)
8393 if (i >= MAX_TAPE_LEN)
8395 Warn("tape truncated -- size exceeds maximum tape size %d",
8398 // tape too large; read and ignore remaining tape data from this chunk
8399 for (;i < tape->length; i++)
8400 ReadUnusedBytesFromFile(file, tape_pos_size);
8405 if (tape->use_key_actions)
8407 for (j = 0; j < MAX_PLAYERS; j++)
8409 tape->pos[i].action[j] = MV_NONE;
8411 if (tape->player_participates[j])
8412 tape->pos[i].action[j] = getFile8Bit(file);
8416 if (tape->use_mouse_actions)
8418 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8419 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8420 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8423 tape->pos[i].delay = getFile8Bit(file);
8425 if (tape->file_version == FILE_VERSION_1_0)
8427 // eliminate possible diagonal moves in old tapes
8428 // this is only for backward compatibility
8430 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8431 byte action = tape->pos[i].action[0];
8432 int k, num_moves = 0;
8434 for (k = 0; k < 4; k++)
8436 if (action & joy_dir[k])
8438 tape->pos[i + num_moves].action[0] = joy_dir[k];
8440 tape->pos[i + num_moves].delay = 0;
8449 tape->length += num_moves;
8452 else if (tape->file_version < FILE_VERSION_2_0)
8454 // convert pre-2.0 tapes to new tape format
8456 if (tape->pos[i].delay > 1)
8459 tape->pos[i + 1] = tape->pos[i];
8460 tape->pos[i + 1].delay = 1;
8463 for (j = 0; j < MAX_PLAYERS; j++)
8464 tape->pos[i].action[j] = MV_NONE;
8465 tape->pos[i].delay--;
8472 if (checkEndOfFile(file))
8476 if (i != tape->length)
8477 chunk_size = tape_pos_size * i;
8482 static void LoadTape_SokobanSolution(char *filename)
8485 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8487 if (!(file = openFile(filename, MODE_READ)))
8489 tape.no_valid_file = TRUE;
8494 while (!checkEndOfFile(file))
8496 unsigned char c = getByteFromFile(file);
8498 if (checkEndOfFile(file))
8505 tape.pos[tape.length].action[0] = MV_UP;
8506 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8512 tape.pos[tape.length].action[0] = MV_DOWN;
8513 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8519 tape.pos[tape.length].action[0] = MV_LEFT;
8520 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8526 tape.pos[tape.length].action[0] = MV_RIGHT;
8527 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8535 // ignore white-space characters
8539 tape.no_valid_file = TRUE;
8541 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8549 if (tape.no_valid_file)
8552 tape.length_frames = GetTapeLengthFrames();
8553 tape.length_seconds = GetTapeLengthSeconds();
8556 void LoadTapeFromFilename(char *filename)
8558 char cookie[MAX_LINE_LEN];
8559 char chunk_name[CHUNK_ID_LEN + 1];
8563 // always start with reliable default values
8564 setTapeInfoToDefaults();
8566 if (strSuffix(filename, ".sln"))
8568 LoadTape_SokobanSolution(filename);
8573 if (!(file = openFile(filename, MODE_READ)))
8575 tape.no_valid_file = TRUE;
8580 getFileChunkBE(file, chunk_name, NULL);
8581 if (strEqual(chunk_name, "RND1"))
8583 getFile32BitBE(file); // not used
8585 getFileChunkBE(file, chunk_name, NULL);
8586 if (!strEqual(chunk_name, "TAPE"))
8588 tape.no_valid_file = TRUE;
8590 Warn("unknown format of tape file '%s'", filename);
8597 else // check for pre-2.0 file format with cookie string
8599 strcpy(cookie, chunk_name);
8600 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8602 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8603 cookie[strlen(cookie) - 1] = '\0';
8605 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8607 tape.no_valid_file = TRUE;
8609 Warn("unknown format of tape file '%s'", filename);
8616 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8618 tape.no_valid_file = TRUE;
8620 Warn("unsupported version of tape file '%s'", filename);
8627 // pre-2.0 tape files have no game version, so use file version here
8628 tape.game_version = tape.file_version;
8631 if (tape.file_version < FILE_VERSION_1_2)
8633 // tape files from versions before 1.2.0 without chunk structure
8634 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8635 LoadTape_BODY(file, 2 * tape.length, &tape);
8643 int (*loader)(File *, int, struct TapeInfo *);
8647 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8648 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8649 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8650 { "INFO", -1, LoadTape_INFO },
8651 { "BODY", -1, LoadTape_BODY },
8655 while (getFileChunkBE(file, chunk_name, &chunk_size))
8659 while (chunk_info[i].name != NULL &&
8660 !strEqual(chunk_name, chunk_info[i].name))
8663 if (chunk_info[i].name == NULL)
8665 Warn("unknown chunk '%s' in tape file '%s'",
8666 chunk_name, filename);
8668 ReadUnusedBytesFromFile(file, chunk_size);
8670 else if (chunk_info[i].size != -1 &&
8671 chunk_info[i].size != chunk_size)
8673 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8674 chunk_size, chunk_name, filename);
8676 ReadUnusedBytesFromFile(file, chunk_size);
8680 // call function to load this tape chunk
8681 int chunk_size_expected =
8682 (chunk_info[i].loader)(file, chunk_size, &tape);
8684 // the size of some chunks cannot be checked before reading other
8685 // chunks first (like "HEAD" and "BODY") that contain some header
8686 // information, so check them here
8687 if (chunk_size_expected != chunk_size)
8689 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8690 chunk_size, chunk_name, filename);
8698 tape.length_frames = GetTapeLengthFrames();
8699 tape.length_seconds = GetTapeLengthSeconds();
8702 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8704 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8706 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8707 tape.engine_version);
8711 void LoadTape(int nr)
8713 char *filename = getTapeFilename(nr);
8715 LoadTapeFromFilename(filename);
8718 void LoadSolutionTape(int nr)
8720 char *filename = getSolutionTapeFilename(nr);
8722 LoadTapeFromFilename(filename);
8724 if (TAPE_IS_EMPTY(tape))
8726 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8727 level.native_bd_level->replay != NULL)
8728 CopyNativeTape_BD_to_RND(&level);
8729 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8730 level.native_sp_level->demo.is_available)
8731 CopyNativeTape_SP_to_RND(&level);
8735 void LoadScoreTape(char *score_tape_basename, int nr)
8737 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8739 LoadTapeFromFilename(filename);
8742 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8744 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8746 LoadTapeFromFilename(filename);
8749 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8751 // chunk required for team mode tapes with non-default screen size
8752 return (tape->num_participating_players > 1 &&
8753 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8754 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8757 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8759 putFileVersion(file, tape->file_version);
8760 putFileVersion(file, tape->game_version);
8763 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8766 byte store_participating_players = 0;
8768 // set bits for participating players for compact storage
8769 for (i = 0; i < MAX_PLAYERS; i++)
8770 if (tape->player_participates[i])
8771 store_participating_players |= (1 << i);
8773 putFile32BitBE(file, tape->random_seed);
8774 putFile32BitBE(file, tape->date);
8775 putFile32BitBE(file, tape->length);
8777 putFile8Bit(file, store_participating_players);
8779 putFile8Bit(file, getTapeActionValue(tape));
8781 putFile8Bit(file, tape->property_bits);
8782 putFile8Bit(file, tape->solved);
8784 putFileVersion(file, tape->engine_version);
8787 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8789 putFile8Bit(file, tape->scr_fieldx);
8790 putFile8Bit(file, tape->scr_fieldy);
8793 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8795 int level_identifier_size = strlen(tape->level_identifier) + 1;
8798 putFile16BitBE(file, level_identifier_size);
8800 for (i = 0; i < level_identifier_size; i++)
8801 putFile8Bit(file, tape->level_identifier[i]);
8803 putFile16BitBE(file, tape->level_nr);
8806 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8810 for (i = 0; i < tape->length; i++)
8812 if (tape->use_key_actions)
8814 for (j = 0; j < MAX_PLAYERS; j++)
8815 if (tape->player_participates[j])
8816 putFile8Bit(file, tape->pos[i].action[j]);
8819 if (tape->use_mouse_actions)
8821 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8822 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8823 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8826 putFile8Bit(file, tape->pos[i].delay);
8830 void SaveTapeToFilename(char *filename)
8834 int info_chunk_size;
8835 int body_chunk_size;
8837 if (!(file = fopen(filename, MODE_WRITE)))
8839 Warn("cannot save level recording file '%s'", filename);
8844 tape_pos_size = getTapePosSize(&tape);
8846 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8847 body_chunk_size = tape_pos_size * tape.length;
8849 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8850 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8852 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8853 SaveTape_VERS(file, &tape);
8855 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8856 SaveTape_HEAD(file, &tape);
8858 if (checkSaveTape_SCRN(&tape))
8860 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8861 SaveTape_SCRN(file, &tape);
8864 putFileChunkBE(file, "INFO", info_chunk_size);
8865 SaveTape_INFO(file, &tape);
8867 putFileChunkBE(file, "BODY", body_chunk_size);
8868 SaveTape_BODY(file, &tape);
8872 SetFilePermissions(filename, PERMS_PRIVATE);
8875 static void SaveTapeExt(char *filename)
8879 tape.file_version = FILE_VERSION_ACTUAL;
8880 tape.game_version = GAME_VERSION_ACTUAL;
8882 tape.num_participating_players = 0;
8884 // count number of participating players
8885 for (i = 0; i < MAX_PLAYERS; i++)
8886 if (tape.player_participates[i])
8887 tape.num_participating_players++;
8889 SaveTapeToFilename(filename);
8891 tape.changed = FALSE;
8894 void SaveTape(int nr)
8896 char *filename = getTapeFilename(nr);
8898 InitTapeDirectory(leveldir_current->subdir);
8900 SaveTapeExt(filename);
8903 void SaveScoreTape(int nr)
8905 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8907 // used instead of "leveldir_current->subdir" (for network games)
8908 InitScoreTapeDirectory(levelset.identifier, nr);
8910 SaveTapeExt(filename);
8913 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8914 unsigned int req_state_added)
8916 char *filename = getTapeFilename(nr);
8917 boolean new_tape = !fileExists(filename);
8918 boolean tape_saved = FALSE;
8920 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8925 Request(msg_saved, REQ_CONFIRM | req_state_added);
8933 boolean SaveTapeChecked(int nr)
8935 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8938 boolean SaveTapeChecked_LevelSolved(int nr)
8940 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8941 "Level solved! Tape saved!", REQ_STAY_OPEN);
8944 void DumpTape(struct TapeInfo *tape)
8946 int tape_frame_counter;
8949 if (tape->no_valid_file)
8951 Warn("cannot dump -- no valid tape file found");
8958 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8959 tape->level_nr, tape->file_version, tape->game_version);
8960 Print(" (effective engine version %08d)\n",
8961 tape->engine_version);
8962 Print("Level series identifier: '%s'\n", tape->level_identifier);
8964 Print("Solution tape: %s\n",
8965 tape->solved ? "yes" :
8966 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8968 Print("Special tape properties: ");
8969 if (tape->property_bits == TAPE_PROPERTY_NONE)
8971 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8972 Print("[em_random_bug]");
8973 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8974 Print("[game_speed]");
8975 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8977 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8978 Print("[single_step]");
8979 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8980 Print("[snapshot]");
8981 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8982 Print("[replayed]");
8983 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8984 Print("[tas_keys]");
8985 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8986 Print("[small_graphics]");
8989 int year2 = tape->date / 10000;
8990 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8991 int month_index_raw = (tape->date / 100) % 100;
8992 int month_index = month_index_raw % 12; // prevent invalid index
8993 int month = month_index + 1;
8994 int day = tape->date % 100;
8996 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9000 tape_frame_counter = 0;
9002 for (i = 0; i < tape->length; i++)
9004 if (i >= MAX_TAPE_LEN)
9009 for (j = 0; j < MAX_PLAYERS; j++)
9011 if (tape->player_participates[j])
9013 int action = tape->pos[i].action[j];
9015 Print("%d:%02x ", j, action);
9016 Print("[%c%c%c%c|%c%c] - ",
9017 (action & JOY_LEFT ? '<' : ' '),
9018 (action & JOY_RIGHT ? '>' : ' '),
9019 (action & JOY_UP ? '^' : ' '),
9020 (action & JOY_DOWN ? 'v' : ' '),
9021 (action & JOY_BUTTON_1 ? '1' : ' '),
9022 (action & JOY_BUTTON_2 ? '2' : ' '));
9026 Print("(%03d) ", tape->pos[i].delay);
9027 Print("[%05d]\n", tape_frame_counter);
9029 tape_frame_counter += tape->pos[i].delay;
9035 void DumpTapes(void)
9037 static LevelDirTree *dumptape_leveldir = NULL;
9039 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9040 global.dumptape_leveldir);
9042 if (dumptape_leveldir == NULL)
9043 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9045 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9046 global.dumptape_level_nr > dumptape_leveldir->last_level)
9047 Fail("no such level number: %d", global.dumptape_level_nr);
9049 leveldir_current = dumptape_leveldir;
9051 if (options.mytapes)
9052 LoadTape(global.dumptape_level_nr);
9054 LoadSolutionTape(global.dumptape_level_nr);
9062 // ============================================================================
9063 // score file functions
9064 // ============================================================================
9066 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9070 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9072 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9073 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9074 scores->entry[i].score = 0;
9075 scores->entry[i].time = 0;
9077 scores->entry[i].id = -1;
9078 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9079 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9080 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9081 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9082 strcpy(scores->entry[i].country_code, "??");
9085 scores->num_entries = 0;
9086 scores->last_added = -1;
9087 scores->last_added_local = -1;
9089 scores->updated = FALSE;
9090 scores->uploaded = FALSE;
9091 scores->tape_downloaded = FALSE;
9092 scores->force_last_added = FALSE;
9094 // The following values are intentionally not reset here:
9098 // - continue_playing
9099 // - continue_on_return
9102 static void setScoreInfoToDefaults(void)
9104 setScoreInfoToDefaultsExt(&scores);
9107 static void setServerScoreInfoToDefaults(void)
9109 setScoreInfoToDefaultsExt(&server_scores);
9112 static void LoadScore_OLD(int nr)
9115 char *filename = getScoreFilename(nr);
9116 char cookie[MAX_LINE_LEN];
9117 char line[MAX_LINE_LEN];
9121 if (!(file = fopen(filename, MODE_READ)))
9124 // check file identifier
9125 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9127 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9128 cookie[strlen(cookie) - 1] = '\0';
9130 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9132 Warn("unknown format of score file '%s'", filename);
9139 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9141 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9142 Warn("fscanf() failed; %s", strerror(errno));
9144 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9147 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9148 line[strlen(line) - 1] = '\0';
9150 for (line_ptr = line; *line_ptr; line_ptr++)
9152 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9154 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9155 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9164 static void ConvertScore_OLD(void)
9166 // only convert score to time for levels that rate playing time over score
9167 if (!level.rate_time_over_score)
9170 // convert old score to playing time for score-less levels (like Supaplex)
9171 int time_final_max = 999;
9174 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9176 int score = scores.entry[i].score;
9178 if (score > 0 && score < time_final_max)
9179 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9183 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9185 scores->file_version = getFileVersion(file);
9186 scores->game_version = getFileVersion(file);
9191 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9193 char *level_identifier = NULL;
9194 int level_identifier_size;
9197 level_identifier_size = getFile16BitBE(file);
9199 level_identifier = checked_malloc(level_identifier_size);
9201 for (i = 0; i < level_identifier_size; i++)
9202 level_identifier[i] = getFile8Bit(file);
9204 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9205 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9207 checked_free(level_identifier);
9209 scores->level_nr = getFile16BitBE(file);
9210 scores->num_entries = getFile16BitBE(file);
9212 chunk_size = 2 + level_identifier_size + 2 + 2;
9217 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9221 for (i = 0; i < scores->num_entries; i++)
9223 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9224 scores->entry[i].name[j] = getFile8Bit(file);
9226 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9229 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9234 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9238 for (i = 0; i < scores->num_entries; i++)
9239 scores->entry[i].score = getFile16BitBE(file);
9241 chunk_size = scores->num_entries * 2;
9246 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9250 for (i = 0; i < scores->num_entries; i++)
9251 scores->entry[i].score = getFile32BitBE(file);
9253 chunk_size = scores->num_entries * 4;
9258 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9262 for (i = 0; i < scores->num_entries; i++)
9263 scores->entry[i].time = getFile32BitBE(file);
9265 chunk_size = scores->num_entries * 4;
9270 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9274 for (i = 0; i < scores->num_entries; i++)
9276 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9277 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9279 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9282 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9287 void LoadScore(int nr)
9289 char *filename = getScoreFilename(nr);
9290 char cookie[MAX_LINE_LEN];
9291 char chunk_name[CHUNK_ID_LEN + 1];
9293 boolean old_score_file_format = FALSE;
9296 // always start with reliable default values
9297 setScoreInfoToDefaults();
9299 if (!(file = openFile(filename, MODE_READ)))
9302 getFileChunkBE(file, chunk_name, NULL);
9303 if (strEqual(chunk_name, "RND1"))
9305 getFile32BitBE(file); // not used
9307 getFileChunkBE(file, chunk_name, NULL);
9308 if (!strEqual(chunk_name, "SCOR"))
9310 Warn("unknown format of score file '%s'", filename);
9317 else // check for old file format with cookie string
9319 strcpy(cookie, chunk_name);
9320 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9322 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9323 cookie[strlen(cookie) - 1] = '\0';
9325 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9327 Warn("unknown format of score file '%s'", filename);
9334 old_score_file_format = TRUE;
9337 if (old_score_file_format)
9339 // score files from versions before 4.2.4.0 without chunk structure
9342 // convert score to time, if possible (mainly for Supaplex levels)
9351 int (*loader)(File *, int, struct ScoreInfo *);
9355 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9356 { "INFO", -1, LoadScore_INFO },
9357 { "NAME", -1, LoadScore_NAME },
9358 { "SCOR", -1, LoadScore_SCOR },
9359 { "SC4R", -1, LoadScore_SC4R },
9360 { "TIME", -1, LoadScore_TIME },
9361 { "TAPE", -1, LoadScore_TAPE },
9366 while (getFileChunkBE(file, chunk_name, &chunk_size))
9370 while (chunk_info[i].name != NULL &&
9371 !strEqual(chunk_name, chunk_info[i].name))
9374 if (chunk_info[i].name == NULL)
9376 Warn("unknown chunk '%s' in score file '%s'",
9377 chunk_name, filename);
9379 ReadUnusedBytesFromFile(file, chunk_size);
9381 else if (chunk_info[i].size != -1 &&
9382 chunk_info[i].size != chunk_size)
9384 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9385 chunk_size, chunk_name, filename);
9387 ReadUnusedBytesFromFile(file, chunk_size);
9391 // call function to load this score chunk
9392 int chunk_size_expected =
9393 (chunk_info[i].loader)(file, chunk_size, &scores);
9395 // the size of some chunks cannot be checked before reading other
9396 // chunks first (like "HEAD" and "BODY") that contain some header
9397 // information, so check them here
9398 if (chunk_size_expected != chunk_size)
9400 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9401 chunk_size, chunk_name, filename);
9410 #if ENABLE_HISTORIC_CHUNKS
9411 void SaveScore_OLD(int nr)
9414 char *filename = getScoreFilename(nr);
9417 // used instead of "leveldir_current->subdir" (for network games)
9418 InitScoreDirectory(levelset.identifier);
9420 if (!(file = fopen(filename, MODE_WRITE)))
9422 Warn("cannot save score for level %d", nr);
9427 fprintf(file, "%s\n\n", SCORE_COOKIE);
9429 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9430 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9434 SetFilePermissions(filename, PERMS_PRIVATE);
9438 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9440 putFileVersion(file, scores->file_version);
9441 putFileVersion(file, scores->game_version);
9444 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9446 int level_identifier_size = strlen(scores->level_identifier) + 1;
9449 putFile16BitBE(file, level_identifier_size);
9451 for (i = 0; i < level_identifier_size; i++)
9452 putFile8Bit(file, scores->level_identifier[i]);
9454 putFile16BitBE(file, scores->level_nr);
9455 putFile16BitBE(file, scores->num_entries);
9458 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9462 for (i = 0; i < scores->num_entries; i++)
9464 int name_size = strlen(scores->entry[i].name);
9466 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9467 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9471 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9475 for (i = 0; i < scores->num_entries; i++)
9476 putFile16BitBE(file, scores->entry[i].score);
9479 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9483 for (i = 0; i < scores->num_entries; i++)
9484 putFile32BitBE(file, scores->entry[i].score);
9487 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9491 for (i = 0; i < scores->num_entries; i++)
9492 putFile32BitBE(file, scores->entry[i].time);
9495 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9499 for (i = 0; i < scores->num_entries; i++)
9501 int size = strlen(scores->entry[i].tape_basename);
9503 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9504 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9508 static void SaveScoreToFilename(char *filename)
9511 int info_chunk_size;
9512 int name_chunk_size;
9513 int scor_chunk_size;
9514 int sc4r_chunk_size;
9515 int time_chunk_size;
9516 int tape_chunk_size;
9517 boolean has_large_score_values;
9520 if (!(file = fopen(filename, MODE_WRITE)))
9522 Warn("cannot save score file '%s'", filename);
9527 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9528 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9529 scor_chunk_size = scores.num_entries * 2;
9530 sc4r_chunk_size = scores.num_entries * 4;
9531 time_chunk_size = scores.num_entries * 4;
9532 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9534 has_large_score_values = FALSE;
9535 for (i = 0; i < scores.num_entries; i++)
9536 if (scores.entry[i].score > 0xffff)
9537 has_large_score_values = TRUE;
9539 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9540 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9542 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9543 SaveScore_VERS(file, &scores);
9545 putFileChunkBE(file, "INFO", info_chunk_size);
9546 SaveScore_INFO(file, &scores);
9548 putFileChunkBE(file, "NAME", name_chunk_size);
9549 SaveScore_NAME(file, &scores);
9551 if (has_large_score_values)
9553 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9554 SaveScore_SC4R(file, &scores);
9558 putFileChunkBE(file, "SCOR", scor_chunk_size);
9559 SaveScore_SCOR(file, &scores);
9562 putFileChunkBE(file, "TIME", time_chunk_size);
9563 SaveScore_TIME(file, &scores);
9565 putFileChunkBE(file, "TAPE", tape_chunk_size);
9566 SaveScore_TAPE(file, &scores);
9570 SetFilePermissions(filename, PERMS_PRIVATE);
9573 void SaveScore(int nr)
9575 char *filename = getScoreFilename(nr);
9578 // used instead of "leveldir_current->subdir" (for network games)
9579 InitScoreDirectory(levelset.identifier);
9581 scores.file_version = FILE_VERSION_ACTUAL;
9582 scores.game_version = GAME_VERSION_ACTUAL;
9584 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9585 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9586 scores.level_nr = level_nr;
9588 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9589 if (scores.entry[i].score == 0 &&
9590 scores.entry[i].time == 0 &&
9591 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9594 scores.num_entries = i;
9596 if (scores.num_entries == 0)
9599 SaveScoreToFilename(filename);
9602 static void LoadServerScoreFromCache(int nr)
9604 struct ScoreEntry score_entry;
9613 { &score_entry.score, FALSE, 0 },
9614 { &score_entry.time, FALSE, 0 },
9615 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9616 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9617 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9618 { &score_entry.id, FALSE, 0 },
9619 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9620 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9621 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9622 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9626 char *filename = getScoreCacheFilename(nr);
9627 SetupFileHash *score_hash = loadSetupFileHash(filename);
9630 server_scores.num_entries = 0;
9632 if (score_hash == NULL)
9635 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9637 score_entry = server_scores.entry[i];
9639 for (j = 0; score_mapping[j].value != NULL; j++)
9643 sprintf(token, "%02d.%d", i, j);
9645 char *value = getHashEntry(score_hash, token);
9650 if (score_mapping[j].is_string)
9652 char *score_value = (char *)score_mapping[j].value;
9653 int value_size = score_mapping[j].string_size;
9655 strncpy(score_value, value, value_size);
9656 score_value[value_size] = '\0';
9660 int *score_value = (int *)score_mapping[j].value;
9662 *score_value = atoi(value);
9665 server_scores.num_entries = i + 1;
9668 server_scores.entry[i] = score_entry;
9671 freeSetupFileHash(score_hash);
9674 void LoadServerScore(int nr, boolean download_score)
9676 if (!setup.use_api_server)
9679 // always start with reliable default values
9680 setServerScoreInfoToDefaults();
9682 // 1st step: load server scores from cache file (which may not exist)
9683 // (this should prevent reading it while the thread is writing to it)
9684 LoadServerScoreFromCache(nr);
9686 if (download_score && runtime.use_api_server)
9688 // 2nd step: download server scores from score server to cache file
9689 // (as thread, as it might time out if the server is not reachable)
9690 ApiGetScoreAsThread(nr);
9694 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9696 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9698 // if score tape not uploaded, ask for uploading missing tapes later
9699 if (!setup.has_remaining_tapes)
9700 setup.ask_for_remaining_tapes = TRUE;
9702 setup.provide_uploading_tapes = TRUE;
9703 setup.has_remaining_tapes = TRUE;
9705 SaveSetup_ServerSetup();
9708 void SaveServerScore(int nr, boolean tape_saved)
9710 if (!runtime.use_api_server)
9712 PrepareScoreTapesForUpload(leveldir_current->subdir);
9717 ApiAddScoreAsThread(nr, tape_saved, NULL);
9720 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9721 char *score_tape_filename)
9723 if (!runtime.use_api_server)
9726 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9729 void LoadLocalAndServerScore(int nr, boolean download_score)
9731 int last_added_local = scores.last_added_local;
9732 boolean force_last_added = scores.force_last_added;
9734 // needed if only showing server scores
9735 setScoreInfoToDefaults();
9737 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9740 // restore last added local score entry (before merging server scores)
9741 scores.last_added = scores.last_added_local = last_added_local;
9743 if (setup.use_api_server &&
9744 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9746 // load server scores from cache file and trigger update from server
9747 LoadServerScore(nr, download_score);
9749 // merge local scores with scores from server
9753 if (force_last_added)
9754 scores.force_last_added = force_last_added;
9758 // ============================================================================
9759 // setup file functions
9760 // ============================================================================
9762 #define TOKEN_STR_PLAYER_PREFIX "player_"
9765 static struct TokenInfo global_setup_tokens[] =
9769 &setup.player_name, "player_name"
9773 &setup.multiple_users, "multiple_users"
9777 &setup.sound, "sound"
9781 &setup.sound_loops, "repeating_sound_loops"
9785 &setup.sound_music, "background_music"
9789 &setup.sound_simple, "simple_sound_effects"
9793 &setup.toons, "toons"
9797 &setup.global_animations, "global_animations"
9801 &setup.scroll_delay, "scroll_delay"
9805 &setup.forced_scroll_delay, "forced_scroll_delay"
9809 &setup.scroll_delay_value, "scroll_delay_value"
9813 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9817 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9821 &setup.fade_screens, "fade_screens"
9825 &setup.autorecord, "automatic_tape_recording"
9829 &setup.autorecord_after_replay, "autorecord_after_replay"
9833 &setup.auto_pause_on_start, "auto_pause_on_start"
9837 &setup.show_titlescreen, "show_titlescreen"
9841 &setup.quick_doors, "quick_doors"
9845 &setup.team_mode, "team_mode"
9849 &setup.handicap, "handicap"
9853 &setup.skip_levels, "skip_levels"
9857 &setup.increment_levels, "increment_levels"
9861 &setup.auto_play_next_level, "auto_play_next_level"
9865 &setup.count_score_after_game, "count_score_after_game"
9869 &setup.show_scores_after_game, "show_scores_after_game"
9873 &setup.time_limit, "time_limit"
9877 &setup.fullscreen, "fullscreen"
9881 &setup.window_scaling_percent, "window_scaling_percent"
9885 &setup.window_scaling_quality, "window_scaling_quality"
9889 &setup.screen_rendering_mode, "screen_rendering_mode"
9893 &setup.vsync_mode, "vsync_mode"
9897 &setup.ask_on_escape, "ask_on_escape"
9901 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9905 &setup.ask_on_game_over, "ask_on_game_over"
9909 &setup.ask_on_quit_game, "ask_on_quit_game"
9913 &setup.ask_on_quit_program, "ask_on_quit_program"
9917 &setup.quick_switch, "quick_player_switch"
9921 &setup.input_on_focus, "input_on_focus"
9925 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9929 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9933 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9937 &setup.game_speed_extended, "game_speed_extended"
9941 &setup.game_frame_delay, "game_frame_delay"
9945 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9949 &setup.bd_skip_hatching, "bd_skip_hatching"
9953 &setup.bd_scroll_delay, "bd_scroll_delay"
9957 &setup.bd_smooth_movements, "bd_smooth_movements"
9961 &setup.sp_show_border_elements, "sp_show_border_elements"
9965 &setup.small_game_graphics, "small_game_graphics"
9969 &setup.show_load_save_buttons, "show_load_save_buttons"
9973 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9977 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9981 &setup.graphics_set, "graphics_set"
9985 &setup.sounds_set, "sounds_set"
9989 &setup.music_set, "music_set"
9993 &setup.override_level_graphics, "override_level_graphics"
9997 &setup.override_level_sounds, "override_level_sounds"
10001 &setup.override_level_music, "override_level_music"
10005 &setup.volume_simple, "volume_simple"
10009 &setup.volume_loops, "volume_loops"
10013 &setup.volume_music, "volume_music"
10017 &setup.network_mode, "network_mode"
10021 &setup.network_player_nr, "network_player"
10025 &setup.network_server_hostname, "network_server_hostname"
10029 &setup.touch.control_type, "touch.control_type"
10033 &setup.touch.move_distance, "touch.move_distance"
10037 &setup.touch.drop_distance, "touch.drop_distance"
10041 &setup.touch.transparency, "touch.transparency"
10045 &setup.touch.draw_outlined, "touch.draw_outlined"
10049 &setup.touch.draw_pressed, "touch.draw_pressed"
10053 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10057 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10061 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10065 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10069 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10073 static struct TokenInfo auto_setup_tokens[] =
10077 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10081 static struct TokenInfo server_setup_tokens[] =
10085 &setup.player_uuid, "player_uuid"
10089 &setup.player_version, "player_version"
10093 &setup.use_api_server, TEST_PREFIX "use_api_server"
10097 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10101 &setup.api_server_password, TEST_PREFIX "api_server_password"
10105 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10109 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10113 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10117 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10121 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10125 static struct TokenInfo editor_setup_tokens[] =
10129 &setup.editor.el_classic, "editor.el_classic"
10133 &setup.editor.el_custom, "editor.el_custom"
10137 &setup.editor.el_user_defined, "editor.el_user_defined"
10141 &setup.editor.el_dynamic, "editor.el_dynamic"
10145 &setup.editor.el_headlines, "editor.el_headlines"
10149 &setup.editor.show_element_token, "editor.show_element_token"
10153 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10157 static struct TokenInfo editor_cascade_setup_tokens[] =
10161 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10165 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10169 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10173 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10177 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10181 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10185 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10189 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10193 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10197 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10201 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10205 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10209 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10213 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10217 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10221 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10225 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10229 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10233 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10237 static struct TokenInfo shortcut_setup_tokens[] =
10241 &setup.shortcut.save_game, "shortcut.save_game"
10245 &setup.shortcut.load_game, "shortcut.load_game"
10249 &setup.shortcut.restart_game, "shortcut.restart_game"
10253 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10257 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10261 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10265 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10269 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10273 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10277 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10281 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10285 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10289 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10293 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10297 &setup.shortcut.tape_record, "shortcut.tape_record"
10301 &setup.shortcut.tape_play, "shortcut.tape_play"
10305 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10309 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10313 &setup.shortcut.sound_music, "shortcut.sound_music"
10317 &setup.shortcut.snap_left, "shortcut.snap_left"
10321 &setup.shortcut.snap_right, "shortcut.snap_right"
10325 &setup.shortcut.snap_up, "shortcut.snap_up"
10329 &setup.shortcut.snap_down, "shortcut.snap_down"
10333 static struct SetupInputInfo setup_input;
10334 static struct TokenInfo player_setup_tokens[] =
10338 &setup_input.use_joystick, ".use_joystick"
10342 &setup_input.joy.device_name, ".joy.device_name"
10346 &setup_input.joy.xleft, ".joy.xleft"
10350 &setup_input.joy.xmiddle, ".joy.xmiddle"
10354 &setup_input.joy.xright, ".joy.xright"
10358 &setup_input.joy.yupper, ".joy.yupper"
10362 &setup_input.joy.ymiddle, ".joy.ymiddle"
10366 &setup_input.joy.ylower, ".joy.ylower"
10370 &setup_input.joy.snap, ".joy.snap_field"
10374 &setup_input.joy.drop, ".joy.place_bomb"
10378 &setup_input.key.left, ".key.move_left"
10382 &setup_input.key.right, ".key.move_right"
10386 &setup_input.key.up, ".key.move_up"
10390 &setup_input.key.down, ".key.move_down"
10394 &setup_input.key.snap, ".key.snap_field"
10398 &setup_input.key.drop, ".key.place_bomb"
10402 static struct TokenInfo system_setup_tokens[] =
10406 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10410 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10414 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10418 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10422 static struct TokenInfo internal_setup_tokens[] =
10426 &setup.internal.program_title, "program_title"
10430 &setup.internal.program_version, "program_version"
10434 &setup.internal.program_author, "program_author"
10438 &setup.internal.program_email, "program_email"
10442 &setup.internal.program_website, "program_website"
10446 &setup.internal.program_copyright, "program_copyright"
10450 &setup.internal.program_company, "program_company"
10454 &setup.internal.program_icon_file, "program_icon_file"
10458 &setup.internal.default_graphics_set, "default_graphics_set"
10462 &setup.internal.default_sounds_set, "default_sounds_set"
10466 &setup.internal.default_music_set, "default_music_set"
10470 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10474 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10478 &setup.internal.fallback_music_file, "fallback_music_file"
10482 &setup.internal.default_level_series, "default_level_series"
10486 &setup.internal.default_window_width, "default_window_width"
10490 &setup.internal.default_window_height, "default_window_height"
10494 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10498 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10502 &setup.internal.create_user_levelset, "create_user_levelset"
10506 &setup.internal.info_screens_from_main, "info_screens_from_main"
10510 &setup.internal.menu_game, "menu_game"
10514 &setup.internal.menu_engines, "menu_engines"
10518 &setup.internal.menu_editor, "menu_editor"
10522 &setup.internal.menu_graphics, "menu_graphics"
10526 &setup.internal.menu_sound, "menu_sound"
10530 &setup.internal.menu_artwork, "menu_artwork"
10534 &setup.internal.menu_input, "menu_input"
10538 &setup.internal.menu_touch, "menu_touch"
10542 &setup.internal.menu_shortcuts, "menu_shortcuts"
10546 &setup.internal.menu_exit, "menu_exit"
10550 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10554 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10558 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10562 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10566 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10570 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10574 &setup.internal.info_title, "info_title"
10578 &setup.internal.info_elements, "info_elements"
10582 &setup.internal.info_music, "info_music"
10586 &setup.internal.info_credits, "info_credits"
10590 &setup.internal.info_program, "info_program"
10594 &setup.internal.info_version, "info_version"
10598 &setup.internal.info_levelset, "info_levelset"
10602 &setup.internal.info_exit, "info_exit"
10606 static struct TokenInfo debug_setup_tokens[] =
10610 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10614 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10618 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10622 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10626 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10630 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10634 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10638 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10642 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10646 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10650 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10654 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10658 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10662 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10666 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10670 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10674 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10678 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10682 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10686 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10690 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10693 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10697 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10701 &setup.debug.xsn_mode, "debug.xsn_mode"
10705 &setup.debug.xsn_percent, "debug.xsn_percent"
10709 static struct TokenInfo options_setup_tokens[] =
10713 &setup.options.verbose, "options.verbose"
10717 &setup.options.debug, "options.debug"
10721 &setup.options.debug_mode, "options.debug_mode"
10725 static void setSetupInfoToDefaults(struct SetupInfo *si)
10729 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10731 si->multiple_users = TRUE;
10734 si->sound_loops = TRUE;
10735 si->sound_music = TRUE;
10736 si->sound_simple = TRUE;
10738 si->global_animations = TRUE;
10739 si->scroll_delay = TRUE;
10740 si->forced_scroll_delay = FALSE;
10741 si->scroll_delay_value = STD_SCROLL_DELAY;
10742 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10743 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10744 si->fade_screens = TRUE;
10745 si->autorecord = TRUE;
10746 si->autorecord_after_replay = TRUE;
10747 si->auto_pause_on_start = FALSE;
10748 si->show_titlescreen = TRUE;
10749 si->quick_doors = FALSE;
10750 si->team_mode = FALSE;
10751 si->handicap = TRUE;
10752 si->skip_levels = TRUE;
10753 si->increment_levels = TRUE;
10754 si->auto_play_next_level = TRUE;
10755 si->count_score_after_game = TRUE;
10756 si->show_scores_after_game = TRUE;
10757 si->time_limit = TRUE;
10758 si->fullscreen = FALSE;
10759 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10760 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10761 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10762 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10763 si->ask_on_escape = TRUE;
10764 si->ask_on_escape_editor = TRUE;
10765 si->ask_on_game_over = TRUE;
10766 si->ask_on_quit_game = TRUE;
10767 si->ask_on_quit_program = TRUE;
10768 si->quick_switch = FALSE;
10769 si->input_on_focus = FALSE;
10770 si->prefer_aga_graphics = TRUE;
10771 si->prefer_lowpass_sounds = FALSE;
10772 si->prefer_extra_panel_items = TRUE;
10773 si->game_speed_extended = FALSE;
10774 si->game_frame_delay = GAME_FRAME_DELAY;
10775 si->bd_skip_uncovering = FALSE;
10776 si->bd_skip_hatching = FALSE;
10777 si->bd_scroll_delay = TRUE;
10778 si->bd_smooth_movements = AUTO;
10779 si->sp_show_border_elements = FALSE;
10780 si->small_game_graphics = FALSE;
10781 si->show_load_save_buttons = FALSE;
10782 si->show_undo_redo_buttons = FALSE;
10783 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10785 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10786 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10787 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10789 si->override_level_graphics = FALSE;
10790 si->override_level_sounds = FALSE;
10791 si->override_level_music = FALSE;
10793 si->volume_simple = 100; // percent
10794 si->volume_loops = 100; // percent
10795 si->volume_music = 100; // percent
10797 si->network_mode = FALSE;
10798 si->network_player_nr = 0; // first player
10799 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10801 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10802 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10803 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10804 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10805 si->touch.draw_outlined = TRUE;
10806 si->touch.draw_pressed = TRUE;
10808 for (i = 0; i < 2; i++)
10810 char *default_grid_button[6][2] =
10816 { "111222", " vv " },
10817 { "111222", " vv " }
10819 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10820 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10821 int min_xsize = MIN(6, grid_xsize);
10822 int min_ysize = MIN(6, grid_ysize);
10823 int startx = grid_xsize - min_xsize;
10824 int starty = grid_ysize - min_ysize;
10827 // virtual buttons grid can only be set to defaults if video is initialized
10828 // (this will be repeated if virtual buttons are not loaded from setup file)
10829 if (video.initialized)
10831 si->touch.grid_xsize[i] = grid_xsize;
10832 si->touch.grid_ysize[i] = grid_ysize;
10836 si->touch.grid_xsize[i] = -1;
10837 si->touch.grid_ysize[i] = -1;
10840 for (x = 0; x < MAX_GRID_XSIZE; x++)
10841 for (y = 0; y < MAX_GRID_YSIZE; y++)
10842 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10844 for (x = 0; x < min_xsize; x++)
10845 for (y = 0; y < min_ysize; y++)
10846 si->touch.grid_button[i][x][starty + y] =
10847 default_grid_button[y][0][x];
10849 for (x = 0; x < min_xsize; x++)
10850 for (y = 0; y < min_ysize; y++)
10851 si->touch.grid_button[i][startx + x][starty + y] =
10852 default_grid_button[y][1][x];
10855 si->touch.grid_initialized = video.initialized;
10857 si->touch.overlay_buttons = FALSE;
10859 si->editor.el_boulderdash = TRUE;
10860 si->editor.el_boulderdash_native = TRUE;
10861 si->editor.el_emerald_mine = TRUE;
10862 si->editor.el_emerald_mine_club = TRUE;
10863 si->editor.el_more = TRUE;
10864 si->editor.el_sokoban = TRUE;
10865 si->editor.el_supaplex = TRUE;
10866 si->editor.el_diamond_caves = TRUE;
10867 si->editor.el_dx_boulderdash = TRUE;
10869 si->editor.el_mirror_magic = TRUE;
10870 si->editor.el_deflektor = TRUE;
10872 si->editor.el_chars = TRUE;
10873 si->editor.el_steel_chars = TRUE;
10875 si->editor.el_classic = TRUE;
10876 si->editor.el_custom = TRUE;
10878 si->editor.el_user_defined = FALSE;
10879 si->editor.el_dynamic = TRUE;
10881 si->editor.el_headlines = TRUE;
10883 si->editor.show_element_token = FALSE;
10885 si->editor.show_read_only_warning = TRUE;
10887 si->editor.use_template_for_new_levels = TRUE;
10889 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10890 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10891 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10892 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10893 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10895 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10896 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10897 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10898 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10899 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10901 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10902 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10903 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10904 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10905 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10906 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10908 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10909 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10910 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10912 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10913 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10914 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10915 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10917 for (i = 0; i < MAX_PLAYERS; i++)
10919 si->input[i].use_joystick = FALSE;
10920 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10921 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10922 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10923 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10924 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10925 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10926 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10927 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10928 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10929 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10930 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10931 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10932 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10933 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10934 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10937 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10938 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10939 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10940 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10942 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10943 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10944 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10945 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10946 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10947 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10948 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10950 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10952 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10953 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10954 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10956 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10957 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10958 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10960 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10961 si->internal.choose_from_top_leveldir = FALSE;
10962 si->internal.show_scaling_in_title = TRUE;
10963 si->internal.create_user_levelset = TRUE;
10964 si->internal.info_screens_from_main = FALSE;
10966 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10967 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10969 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10970 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10971 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10972 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10973 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10974 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10975 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10976 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10977 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10978 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10980 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10981 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10982 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10983 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10984 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10985 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10986 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10987 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10988 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10989 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10991 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10992 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10994 si->debug.show_frames_per_second = FALSE;
10996 si->debug.xsn_mode = AUTO;
10997 si->debug.xsn_percent = 0;
10999 si->options.verbose = FALSE;
11000 si->options.debug = FALSE;
11001 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11003 #if defined(PLATFORM_ANDROID)
11004 si->fullscreen = TRUE;
11005 si->touch.overlay_buttons = TRUE;
11008 setHideSetupEntry(&setup.debug.xsn_mode);
11011 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11013 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11016 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11018 si->player_uuid = NULL; // (will be set later)
11019 si->player_version = 1; // (will be set later)
11021 si->use_api_server = TRUE;
11022 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11023 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11024 si->ask_for_uploading_tapes = TRUE;
11025 si->ask_for_remaining_tapes = FALSE;
11026 si->provide_uploading_tapes = TRUE;
11027 si->ask_for_using_api_server = TRUE;
11028 si->has_remaining_tapes = FALSE;
11031 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11033 si->editor_cascade.el_bd = TRUE;
11034 si->editor_cascade.el_bd_native = TRUE;
11035 si->editor_cascade.el_em = TRUE;
11036 si->editor_cascade.el_emc = TRUE;
11037 si->editor_cascade.el_rnd = TRUE;
11038 si->editor_cascade.el_sb = TRUE;
11039 si->editor_cascade.el_sp = TRUE;
11040 si->editor_cascade.el_dc = TRUE;
11041 si->editor_cascade.el_dx = TRUE;
11043 si->editor_cascade.el_mm = TRUE;
11044 si->editor_cascade.el_df = TRUE;
11046 si->editor_cascade.el_chars = FALSE;
11047 si->editor_cascade.el_steel_chars = FALSE;
11048 si->editor_cascade.el_ce = FALSE;
11049 si->editor_cascade.el_ge = FALSE;
11050 si->editor_cascade.el_es = FALSE;
11051 si->editor_cascade.el_ref = FALSE;
11052 si->editor_cascade.el_user = FALSE;
11053 si->editor_cascade.el_dynamic = FALSE;
11056 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11058 static char *getHideSetupToken(void *setup_value)
11060 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11062 if (setup_value != NULL)
11063 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11065 return hide_setup_token;
11068 void setHideSetupEntry(void *setup_value)
11070 char *hide_setup_token = getHideSetupToken(setup_value);
11072 if (hide_setup_hash == NULL)
11073 hide_setup_hash = newSetupFileHash();
11075 if (setup_value != NULL)
11076 setHashEntry(hide_setup_hash, hide_setup_token, "");
11079 void removeHideSetupEntry(void *setup_value)
11081 char *hide_setup_token = getHideSetupToken(setup_value);
11083 if (setup_value != NULL)
11084 removeHashEntry(hide_setup_hash, hide_setup_token);
11087 boolean hideSetupEntry(void *setup_value)
11089 char *hide_setup_token = getHideSetupToken(setup_value);
11091 return (setup_value != NULL &&
11092 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11095 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11096 struct TokenInfo *token_info,
11097 int token_nr, char *token_text)
11099 char *token_hide_text = getStringCat2(token_text, ".hide");
11100 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11102 // set the value of this setup option in the setup option structure
11103 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11105 // check if this setup option should be hidden in the setup menu
11106 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11107 setHideSetupEntry(token_info[token_nr].value);
11109 free(token_hide_text);
11112 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11113 struct TokenInfo *token_info,
11116 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11117 token_info[token_nr].text);
11120 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11124 if (!setup_file_hash)
11127 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11128 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11130 setup.touch.grid_initialized = TRUE;
11131 for (i = 0; i < 2; i++)
11133 int grid_xsize = setup.touch.grid_xsize[i];
11134 int grid_ysize = setup.touch.grid_ysize[i];
11137 // if virtual buttons are not loaded from setup file, repeat initializing
11138 // virtual buttons grid with default values later when video is initialized
11139 if (grid_xsize == -1 ||
11142 setup.touch.grid_initialized = FALSE;
11147 for (y = 0; y < grid_ysize; y++)
11149 char token_string[MAX_LINE_LEN];
11151 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11153 char *value_string = getHashEntry(setup_file_hash, token_string);
11155 if (value_string == NULL)
11158 for (x = 0; x < grid_xsize; x++)
11160 char c = value_string[x];
11162 setup.touch.grid_button[i][x][y] =
11163 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11168 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11169 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11171 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11172 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11174 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11178 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11180 setup_input = setup.input[pnr];
11181 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11183 char full_token[100];
11185 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11186 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11189 setup.input[pnr] = setup_input;
11192 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11193 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11195 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11196 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11198 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11199 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11201 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11202 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11204 setHideRelatedSetupEntries();
11207 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11211 if (!setup_file_hash)
11214 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11215 setSetupInfo(auto_setup_tokens, i,
11216 getHashEntry(setup_file_hash,
11217 auto_setup_tokens[i].text));
11220 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11224 if (!setup_file_hash)
11227 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11228 setSetupInfo(server_setup_tokens, i,
11229 getHashEntry(setup_file_hash,
11230 server_setup_tokens[i].text));
11233 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11237 if (!setup_file_hash)
11240 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11241 setSetupInfo(editor_cascade_setup_tokens, i,
11242 getHashEntry(setup_file_hash,
11243 editor_cascade_setup_tokens[i].text));
11246 void LoadUserNames(void)
11248 int last_user_nr = user.nr;
11251 if (global.user_names != NULL)
11253 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11254 checked_free(global.user_names[i]);
11256 checked_free(global.user_names);
11259 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11261 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11265 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11267 if (setup_file_hash)
11269 char *player_name = getHashEntry(setup_file_hash, "player_name");
11271 global.user_names[i] = getFixedUserName(player_name);
11273 freeSetupFileHash(setup_file_hash);
11276 if (global.user_names[i] == NULL)
11277 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11280 user.nr = last_user_nr;
11283 void LoadSetupFromFilename(char *filename)
11285 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11287 if (setup_file_hash)
11289 decodeSetupFileHash_Default(setup_file_hash);
11291 freeSetupFileHash(setup_file_hash);
11295 Debug("setup", "using default setup values");
11299 static void LoadSetup_SpecialPostProcessing(void)
11301 char *player_name_new;
11303 // needed to work around problems with fixed length strings
11304 player_name_new = getFixedUserName(setup.player_name);
11305 free(setup.player_name);
11306 setup.player_name = player_name_new;
11308 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11309 if (setup.scroll_delay == FALSE)
11311 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11312 setup.scroll_delay = TRUE; // now always "on"
11315 // make sure that scroll delay value stays inside valid range
11316 setup.scroll_delay_value =
11317 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11320 void LoadSetup_Default(void)
11324 // always start with reliable default values
11325 setSetupInfoToDefaults(&setup);
11327 // try to load setup values from default setup file
11328 filename = getDefaultSetupFilename();
11330 if (fileExists(filename))
11331 LoadSetupFromFilename(filename);
11333 // try to load setup values from platform setup file
11334 filename = getPlatformSetupFilename();
11336 if (fileExists(filename))
11337 LoadSetupFromFilename(filename);
11339 // try to load setup values from user setup file
11340 filename = getSetupFilename();
11342 LoadSetupFromFilename(filename);
11344 LoadSetup_SpecialPostProcessing();
11347 void LoadSetup_AutoSetup(void)
11349 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11350 SetupFileHash *setup_file_hash = NULL;
11352 // always start with reliable default values
11353 setSetupInfoToDefaults_AutoSetup(&setup);
11355 setup_file_hash = loadSetupFileHash(filename);
11357 if (setup_file_hash)
11359 decodeSetupFileHash_AutoSetup(setup_file_hash);
11361 freeSetupFileHash(setup_file_hash);
11367 void LoadSetup_ServerSetup(void)
11369 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11370 SetupFileHash *setup_file_hash = NULL;
11372 // always start with reliable default values
11373 setSetupInfoToDefaults_ServerSetup(&setup);
11375 setup_file_hash = loadSetupFileHash(filename);
11377 if (setup_file_hash)
11379 decodeSetupFileHash_ServerSetup(setup_file_hash);
11381 freeSetupFileHash(setup_file_hash);
11386 if (setup.player_uuid == NULL)
11388 // player UUID does not yet exist in setup file
11389 setup.player_uuid = getStringCopy(getUUID());
11390 setup.player_version = 2;
11392 SaveSetup_ServerSetup();
11396 void LoadSetup_EditorCascade(void)
11398 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11399 SetupFileHash *setup_file_hash = NULL;
11401 // always start with reliable default values
11402 setSetupInfoToDefaults_EditorCascade(&setup);
11404 setup_file_hash = loadSetupFileHash(filename);
11406 if (setup_file_hash)
11408 decodeSetupFileHash_EditorCascade(setup_file_hash);
11410 freeSetupFileHash(setup_file_hash);
11416 void LoadSetup(void)
11418 LoadSetup_Default();
11419 LoadSetup_AutoSetup();
11420 LoadSetup_ServerSetup();
11421 LoadSetup_EditorCascade();
11424 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11425 char *mapping_line)
11427 char mapping_guid[MAX_LINE_LEN];
11428 char *mapping_start, *mapping_end;
11430 // get GUID from game controller mapping line: copy complete line
11431 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11432 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11434 // get GUID from game controller mapping line: cut after GUID part
11435 mapping_start = strchr(mapping_guid, ',');
11436 if (mapping_start != NULL)
11437 *mapping_start = '\0';
11439 // cut newline from game controller mapping line
11440 mapping_end = strchr(mapping_line, '\n');
11441 if (mapping_end != NULL)
11442 *mapping_end = '\0';
11444 // add mapping entry to game controller mappings hash
11445 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11448 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11453 if (!(file = fopen(filename, MODE_READ)))
11455 Warn("cannot read game controller mappings file '%s'", filename);
11460 while (!feof(file))
11462 char line[MAX_LINE_LEN];
11464 if (!fgets(line, MAX_LINE_LEN, file))
11467 addGameControllerMappingToHash(mappings_hash, line);
11473 void SaveSetup_Default(void)
11475 char *filename = getSetupFilename();
11479 InitUserDataDirectory();
11481 if (!(file = fopen(filename, MODE_WRITE)))
11483 Warn("cannot write setup file '%s'", filename);
11488 fprintFileHeader(file, SETUP_FILENAME);
11490 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11492 // just to make things nicer :)
11493 if (global_setup_tokens[i].value == &setup.multiple_users ||
11494 global_setup_tokens[i].value == &setup.sound ||
11495 global_setup_tokens[i].value == &setup.graphics_set ||
11496 global_setup_tokens[i].value == &setup.volume_simple ||
11497 global_setup_tokens[i].value == &setup.network_mode ||
11498 global_setup_tokens[i].value == &setup.touch.control_type ||
11499 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11500 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11501 fprintf(file, "\n");
11503 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11506 for (i = 0; i < 2; i++)
11508 int grid_xsize = setup.touch.grid_xsize[i];
11509 int grid_ysize = setup.touch.grid_ysize[i];
11512 fprintf(file, "\n");
11514 for (y = 0; y < grid_ysize; y++)
11516 char token_string[MAX_LINE_LEN];
11517 char value_string[MAX_LINE_LEN];
11519 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11521 for (x = 0; x < grid_xsize; x++)
11523 char c = setup.touch.grid_button[i][x][y];
11525 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11528 value_string[grid_xsize] = '\0';
11530 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11534 fprintf(file, "\n");
11535 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11536 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11538 fprintf(file, "\n");
11539 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11540 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11542 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11546 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11547 fprintf(file, "\n");
11549 setup_input = setup.input[pnr];
11550 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11551 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11554 fprintf(file, "\n");
11555 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11556 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11558 // (internal setup values not saved to user setup file)
11560 fprintf(file, "\n");
11561 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11562 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11563 setup.debug.xsn_mode != AUTO)
11564 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11566 fprintf(file, "\n");
11567 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11568 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11572 SetFilePermissions(filename, PERMS_PRIVATE);
11575 void SaveSetup_AutoSetup(void)
11577 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11581 InitUserDataDirectory();
11583 if (!(file = fopen(filename, MODE_WRITE)))
11585 Warn("cannot write auto setup file '%s'", filename);
11592 fprintFileHeader(file, AUTOSETUP_FILENAME);
11594 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11595 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11599 SetFilePermissions(filename, PERMS_PRIVATE);
11604 void SaveSetup_ServerSetup(void)
11606 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11610 InitUserDataDirectory();
11612 if (!(file = fopen(filename, MODE_WRITE)))
11614 Warn("cannot write server setup file '%s'", filename);
11621 fprintFileHeader(file, SERVERSETUP_FILENAME);
11623 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11625 // just to make things nicer :)
11626 if (server_setup_tokens[i].value == &setup.use_api_server)
11627 fprintf(file, "\n");
11629 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11634 SetFilePermissions(filename, PERMS_PRIVATE);
11639 void SaveSetup_EditorCascade(void)
11641 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11645 InitUserDataDirectory();
11647 if (!(file = fopen(filename, MODE_WRITE)))
11649 Warn("cannot write editor cascade state file '%s'", filename);
11656 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11658 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11659 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11663 SetFilePermissions(filename, PERMS_PRIVATE);
11668 void SaveSetup(void)
11670 SaveSetup_Default();
11671 SaveSetup_AutoSetup();
11672 SaveSetup_ServerSetup();
11673 SaveSetup_EditorCascade();
11676 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11681 if (!(file = fopen(filename, MODE_WRITE)))
11683 Warn("cannot write game controller mappings file '%s'", filename);
11688 BEGIN_HASH_ITERATION(mappings_hash, itr)
11690 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11692 END_HASH_ITERATION(mappings_hash, itr)
11697 void SaveSetup_AddGameControllerMapping(char *mapping)
11699 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11700 SetupFileHash *mappings_hash = newSetupFileHash();
11702 InitUserDataDirectory();
11704 // load existing personal game controller mappings
11705 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11707 // add new mapping to personal game controller mappings
11708 addGameControllerMappingToHash(mappings_hash, mapping);
11710 // save updated personal game controller mappings
11711 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11713 freeSetupFileHash(mappings_hash);
11717 void LoadCustomElementDescriptions(void)
11719 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11720 SetupFileHash *setup_file_hash;
11723 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11725 if (element_info[i].custom_description != NULL)
11727 free(element_info[i].custom_description);
11728 element_info[i].custom_description = NULL;
11732 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11735 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11737 char *token = getStringCat2(element_info[i].token_name, ".name");
11738 char *value = getHashEntry(setup_file_hash, token);
11741 element_info[i].custom_description = getStringCopy(value);
11746 freeSetupFileHash(setup_file_hash);
11749 static int getElementFromToken(char *token)
11751 char *value = getHashEntry(element_token_hash, token);
11754 return atoi(value);
11756 Warn("unknown element token '%s'", token);
11758 return EL_UNDEFINED;
11761 void FreeGlobalAnimEventInfo(void)
11763 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11765 if (gaei->event_list == NULL)
11770 for (i = 0; i < gaei->num_event_lists; i++)
11772 checked_free(gaei->event_list[i]->event_value);
11773 checked_free(gaei->event_list[i]);
11776 checked_free(gaei->event_list);
11778 gaei->event_list = NULL;
11779 gaei->num_event_lists = 0;
11782 static int AddGlobalAnimEventList(void)
11784 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11785 int list_pos = gaei->num_event_lists++;
11787 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11788 sizeof(struct GlobalAnimEventListInfo *));
11790 gaei->event_list[list_pos] =
11791 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11793 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11795 gaeli->event_value = NULL;
11796 gaeli->num_event_values = 0;
11801 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11803 // do not add empty global animation events
11804 if (event_value == ANIM_EVENT_NONE)
11807 // if list position is undefined, create new list
11808 if (list_pos == ANIM_EVENT_UNDEFINED)
11809 list_pos = AddGlobalAnimEventList();
11811 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11812 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11813 int value_pos = gaeli->num_event_values++;
11815 gaeli->event_value = checked_realloc(gaeli->event_value,
11816 gaeli->num_event_values * sizeof(int *));
11818 gaeli->event_value[value_pos] = event_value;
11823 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11825 if (list_pos == ANIM_EVENT_UNDEFINED)
11826 return ANIM_EVENT_NONE;
11828 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11829 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11831 return gaeli->event_value[value_pos];
11834 int GetGlobalAnimEventValueCount(int list_pos)
11836 if (list_pos == ANIM_EVENT_UNDEFINED)
11839 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11840 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11842 return gaeli->num_event_values;
11845 // This function checks if a string <s> of the format "string1, string2, ..."
11846 // exactly contains a string <s_contained>.
11848 static boolean string_has_parameter(char *s, char *s_contained)
11852 if (s == NULL || s_contained == NULL)
11855 if (strlen(s_contained) > strlen(s))
11858 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11860 char next_char = s[strlen(s_contained)];
11862 // check if next character is delimiter or whitespace
11863 if (next_char == ',' || next_char == '\0' ||
11864 next_char == ' ' || next_char == '\t')
11868 // check if string contains another parameter string after a comma
11869 substring = strchr(s, ',');
11870 if (substring == NULL) // string does not contain a comma
11873 // advance string pointer to next character after the comma
11876 // skip potential whitespaces after the comma
11877 while (*substring == ' ' || *substring == '\t')
11880 return string_has_parameter(substring, s_contained);
11883 static int get_anim_parameter_value_ce(char *s)
11886 char *pattern_1 = "ce_change:custom_";
11887 char *pattern_2 = ".page_";
11888 int pattern_1_len = strlen(pattern_1);
11889 char *matching_char = strstr(s_ptr, pattern_1);
11890 int result = ANIM_EVENT_NONE;
11892 if (matching_char == NULL)
11893 return ANIM_EVENT_NONE;
11895 result = ANIM_EVENT_CE_CHANGE;
11897 s_ptr = matching_char + pattern_1_len;
11899 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11900 if (*s_ptr >= '0' && *s_ptr <= '9')
11902 int gic_ce_nr = (*s_ptr++ - '0');
11904 if (*s_ptr >= '0' && *s_ptr <= '9')
11906 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11908 if (*s_ptr >= '0' && *s_ptr <= '9')
11909 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11912 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11913 return ANIM_EVENT_NONE;
11915 // custom element stored as 0 to 255
11918 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11922 // invalid custom element number specified
11924 return ANIM_EVENT_NONE;
11927 // check for change page number ("page_X" or "page_XX") (optional)
11928 if (strPrefix(s_ptr, pattern_2))
11930 s_ptr += strlen(pattern_2);
11932 if (*s_ptr >= '0' && *s_ptr <= '9')
11934 int gic_page_nr = (*s_ptr++ - '0');
11936 if (*s_ptr >= '0' && *s_ptr <= '9')
11937 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11939 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11940 return ANIM_EVENT_NONE;
11942 // change page stored as 1 to 32 (0 means "all change pages")
11944 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11948 // invalid animation part number specified
11950 return ANIM_EVENT_NONE;
11954 // discard result if next character is neither delimiter nor whitespace
11955 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11956 *s_ptr == ' ' || *s_ptr == '\t'))
11957 return ANIM_EVENT_NONE;
11962 static int get_anim_parameter_value(char *s)
11964 int event_value[] =
11972 char *pattern_1[] =
11980 char *pattern_2 = ".part_";
11981 char *matching_char = NULL;
11983 int pattern_1_len = 0;
11984 int result = ANIM_EVENT_NONE;
11987 result = get_anim_parameter_value_ce(s);
11989 if (result != ANIM_EVENT_NONE)
11992 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11994 matching_char = strstr(s_ptr, pattern_1[i]);
11995 pattern_1_len = strlen(pattern_1[i]);
11996 result = event_value[i];
11998 if (matching_char != NULL)
12002 if (matching_char == NULL)
12003 return ANIM_EVENT_NONE;
12005 s_ptr = matching_char + pattern_1_len;
12007 // check for main animation number ("anim_X" or "anim_XX")
12008 if (*s_ptr >= '0' && *s_ptr <= '9')
12010 int gic_anim_nr = (*s_ptr++ - '0');
12012 if (*s_ptr >= '0' && *s_ptr <= '9')
12013 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12015 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12016 return ANIM_EVENT_NONE;
12018 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12022 // invalid main animation number specified
12024 return ANIM_EVENT_NONE;
12027 // check for animation part number ("part_X" or "part_XX") (optional)
12028 if (strPrefix(s_ptr, pattern_2))
12030 s_ptr += strlen(pattern_2);
12032 if (*s_ptr >= '0' && *s_ptr <= '9')
12034 int gic_part_nr = (*s_ptr++ - '0');
12036 if (*s_ptr >= '0' && *s_ptr <= '9')
12037 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12039 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12040 return ANIM_EVENT_NONE;
12042 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12046 // invalid animation part number specified
12048 return ANIM_EVENT_NONE;
12052 // discard result if next character is neither delimiter nor whitespace
12053 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12054 *s_ptr == ' ' || *s_ptr == '\t'))
12055 return ANIM_EVENT_NONE;
12060 static int get_anim_parameter_values(char *s)
12062 int list_pos = ANIM_EVENT_UNDEFINED;
12063 int event_value = ANIM_EVENT_DEFAULT;
12065 if (string_has_parameter(s, "any"))
12066 event_value |= ANIM_EVENT_ANY;
12068 if (string_has_parameter(s, "click:self") ||
12069 string_has_parameter(s, "click") ||
12070 string_has_parameter(s, "self"))
12071 event_value |= ANIM_EVENT_SELF;
12073 if (string_has_parameter(s, "unclick:any"))
12074 event_value |= ANIM_EVENT_UNCLICK_ANY;
12076 // if animation event found, add it to global animation event list
12077 if (event_value != ANIM_EVENT_NONE)
12078 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12082 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12083 event_value = get_anim_parameter_value(s);
12085 // if animation event found, add it to global animation event list
12086 if (event_value != ANIM_EVENT_NONE)
12087 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12089 // continue with next part of the string, starting with next comma
12090 s = strchr(s + 1, ',');
12096 static int get_anim_action_parameter_value(char *token)
12098 // check most common default case first to massively speed things up
12099 if (strEqual(token, ARG_UNDEFINED))
12100 return ANIM_EVENT_ACTION_NONE;
12102 int result = getImageIDFromToken(token);
12106 char *gfx_token = getStringCat2("gfx.", token);
12108 result = getImageIDFromToken(gfx_token);
12110 checked_free(gfx_token);
12115 Key key = getKeyFromX11KeyName(token);
12117 if (key != KSYM_UNDEFINED)
12118 result = -(int)key;
12125 result = get_hash_from_string(token); // unsigned int => int
12126 result = ABS(result); // may be negative now
12127 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12129 setHashEntry(anim_url_hash, int2str(result, 0), token);
12134 result = ANIM_EVENT_ACTION_NONE;
12139 int get_parameter_value(char *value_raw, char *suffix, int type)
12141 char *value = getStringToLower(value_raw);
12142 int result = 0; // probably a save default value
12144 if (strEqual(suffix, ".direction"))
12146 result = (strEqual(value, "left") ? MV_LEFT :
12147 strEqual(value, "right") ? MV_RIGHT :
12148 strEqual(value, "up") ? MV_UP :
12149 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12151 else if (strEqual(suffix, ".position"))
12153 result = (strEqual(value, "left") ? POS_LEFT :
12154 strEqual(value, "right") ? POS_RIGHT :
12155 strEqual(value, "top") ? POS_TOP :
12156 strEqual(value, "upper") ? POS_UPPER :
12157 strEqual(value, "middle") ? POS_MIDDLE :
12158 strEqual(value, "lower") ? POS_LOWER :
12159 strEqual(value, "bottom") ? POS_BOTTOM :
12160 strEqual(value, "any") ? POS_ANY :
12161 strEqual(value, "ce") ? POS_CE :
12162 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12163 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12165 else if (strEqual(suffix, ".align"))
12167 result = (strEqual(value, "left") ? ALIGN_LEFT :
12168 strEqual(value, "right") ? ALIGN_RIGHT :
12169 strEqual(value, "center") ? ALIGN_CENTER :
12170 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12172 else if (strEqual(suffix, ".valign"))
12174 result = (strEqual(value, "top") ? VALIGN_TOP :
12175 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12176 strEqual(value, "middle") ? VALIGN_MIDDLE :
12177 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12179 else if (strEqual(suffix, ".anim_mode"))
12181 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12182 string_has_parameter(value, "loop") ? ANIM_LOOP :
12183 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12184 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12185 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12186 string_has_parameter(value, "random") ? ANIM_RANDOM :
12187 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12188 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12189 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12190 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12191 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12192 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12193 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12194 string_has_parameter(value, "all") ? ANIM_ALL :
12195 string_has_parameter(value, "tiled") ? ANIM_TILED :
12196 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12199 if (string_has_parameter(value, "once"))
12200 result |= ANIM_ONCE;
12202 if (string_has_parameter(value, "reverse"))
12203 result |= ANIM_REVERSE;
12205 if (string_has_parameter(value, "opaque_player"))
12206 result |= ANIM_OPAQUE_PLAYER;
12208 if (string_has_parameter(value, "static_panel"))
12209 result |= ANIM_STATIC_PANEL;
12211 else if (strEqual(suffix, ".init_event") ||
12212 strEqual(suffix, ".anim_event"))
12214 result = get_anim_parameter_values(value);
12216 else if (strEqual(suffix, ".init_delay_action") ||
12217 strEqual(suffix, ".anim_delay_action") ||
12218 strEqual(suffix, ".post_delay_action") ||
12219 strEqual(suffix, ".init_event_action") ||
12220 strEqual(suffix, ".anim_event_action"))
12222 result = get_anim_action_parameter_value(value_raw);
12224 else if (strEqual(suffix, ".class"))
12226 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12227 get_hash_from_string(value));
12229 else if (strEqual(suffix, ".style"))
12231 result = STYLE_DEFAULT;
12233 if (string_has_parameter(value, "accurate_borders"))
12234 result |= STYLE_ACCURATE_BORDERS;
12236 if (string_has_parameter(value, "inner_corners"))
12237 result |= STYLE_INNER_CORNERS;
12239 if (string_has_parameter(value, "reverse"))
12240 result |= STYLE_REVERSE;
12242 if (string_has_parameter(value, "leftmost_position"))
12243 result |= STYLE_LEFTMOST_POSITION;
12245 if (string_has_parameter(value, "block_clicks"))
12246 result |= STYLE_BLOCK;
12248 if (string_has_parameter(value, "passthrough_clicks"))
12249 result |= STYLE_PASSTHROUGH;
12251 if (string_has_parameter(value, "multiple_actions"))
12252 result |= STYLE_MULTIPLE_ACTIONS;
12254 if (string_has_parameter(value, "consume_ce_event"))
12255 result |= STYLE_CONSUME_CE_EVENT;
12257 else if (strEqual(suffix, ".fade_mode"))
12259 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12260 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12261 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12262 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12263 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12264 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12265 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12266 FADE_MODE_DEFAULT);
12268 else if (strEqual(suffix, ".auto_delay_unit"))
12270 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12271 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12272 AUTO_DELAY_UNIT_DEFAULT);
12274 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12276 result = gfx.get_font_from_token_function(value);
12278 else // generic parameter of type integer or boolean
12280 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12281 type == TYPE_INTEGER ? get_integer_from_string(value) :
12282 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12283 ARG_UNDEFINED_VALUE);
12291 static int get_token_parameter_value(char *token, char *value_raw)
12295 if (token == NULL || value_raw == NULL)
12296 return ARG_UNDEFINED_VALUE;
12298 suffix = strrchr(token, '.');
12299 if (suffix == NULL)
12302 if (strEqual(suffix, ".element"))
12303 return getElementFromToken(value_raw);
12305 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12306 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12309 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12310 boolean ignore_defaults)
12314 for (i = 0; image_config_vars[i].token != NULL; i++)
12316 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12318 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12319 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12323 *image_config_vars[i].value =
12324 get_token_parameter_value(image_config_vars[i].token, value);
12328 void InitMenuDesignSettings_Static(void)
12330 // always start with reliable default values from static default config
12331 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12334 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12338 // the following initializes hierarchical values from static configuration
12340 // special case: initialize "ARG_DEFAULT" values in static default config
12341 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12342 titlescreen_initial_first_default.fade_mode =
12343 title_initial_first_default.fade_mode;
12344 titlescreen_initial_first_default.fade_delay =
12345 title_initial_first_default.fade_delay;
12346 titlescreen_initial_first_default.post_delay =
12347 title_initial_first_default.post_delay;
12348 titlescreen_initial_first_default.auto_delay =
12349 title_initial_first_default.auto_delay;
12350 titlescreen_initial_first_default.auto_delay_unit =
12351 title_initial_first_default.auto_delay_unit;
12352 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12353 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12354 titlescreen_first_default.post_delay = title_first_default.post_delay;
12355 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12356 titlescreen_first_default.auto_delay_unit =
12357 title_first_default.auto_delay_unit;
12358 titlemessage_initial_first_default.fade_mode =
12359 title_initial_first_default.fade_mode;
12360 titlemessage_initial_first_default.fade_delay =
12361 title_initial_first_default.fade_delay;
12362 titlemessage_initial_first_default.post_delay =
12363 title_initial_first_default.post_delay;
12364 titlemessage_initial_first_default.auto_delay =
12365 title_initial_first_default.auto_delay;
12366 titlemessage_initial_first_default.auto_delay_unit =
12367 title_initial_first_default.auto_delay_unit;
12368 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12369 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12370 titlemessage_first_default.post_delay = title_first_default.post_delay;
12371 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12372 titlemessage_first_default.auto_delay_unit =
12373 title_first_default.auto_delay_unit;
12375 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12376 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12377 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12378 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12379 titlescreen_initial_default.auto_delay_unit =
12380 title_initial_default.auto_delay_unit;
12381 titlescreen_default.fade_mode = title_default.fade_mode;
12382 titlescreen_default.fade_delay = title_default.fade_delay;
12383 titlescreen_default.post_delay = title_default.post_delay;
12384 titlescreen_default.auto_delay = title_default.auto_delay;
12385 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12386 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12387 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12388 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12389 titlemessage_initial_default.auto_delay_unit =
12390 title_initial_default.auto_delay_unit;
12391 titlemessage_default.fade_mode = title_default.fade_mode;
12392 titlemessage_default.fade_delay = title_default.fade_delay;
12393 titlemessage_default.post_delay = title_default.post_delay;
12394 titlemessage_default.auto_delay = title_default.auto_delay;
12395 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12397 // special case: initialize "ARG_DEFAULT" values in static default config
12398 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12399 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12401 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12402 titlescreen_first[i] = titlescreen_first_default;
12403 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12404 titlemessage_first[i] = titlemessage_first_default;
12406 titlescreen_initial[i] = titlescreen_initial_default;
12407 titlescreen[i] = titlescreen_default;
12408 titlemessage_initial[i] = titlemessage_initial_default;
12409 titlemessage[i] = titlemessage_default;
12412 // special case: initialize "ARG_DEFAULT" values in static default config
12413 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12414 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12416 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12419 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12420 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12421 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12424 // special case: initialize "ARG_DEFAULT" values in static default config
12425 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12426 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12428 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12429 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12430 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12432 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12435 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12439 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12443 struct XY *dst, *src;
12445 game_buttons_xy[] =
12447 { &game.button.save, &game.button.stop },
12448 { &game.button.pause2, &game.button.pause },
12449 { &game.button.load, &game.button.play },
12450 { &game.button.undo, &game.button.stop },
12451 { &game.button.redo, &game.button.play },
12457 // special case: initialize later added SETUP list size from LEVELS value
12458 if (menu.list_size[GAME_MODE_SETUP] == -1)
12459 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12461 // set default position for snapshot buttons to stop/pause/play buttons
12462 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12463 if ((*game_buttons_xy[i].dst).x == -1 &&
12464 (*game_buttons_xy[i].dst).y == -1)
12465 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12467 // --------------------------------------------------------------------------
12468 // dynamic viewports (including playfield margins, borders and alignments)
12469 // --------------------------------------------------------------------------
12471 // dynamic viewports currently only supported for landscape mode
12472 int display_width = MAX(video.display_width, video.display_height);
12473 int display_height = MIN(video.display_width, video.display_height);
12475 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12477 struct RectWithBorder *vp_window = &viewport.window[i];
12478 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12479 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12480 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12481 boolean dynamic_window_width = (vp_window->min_width != -1);
12482 boolean dynamic_window_height = (vp_window->min_height != -1);
12483 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12484 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12486 // adjust window size if min/max width/height is specified
12488 if (vp_window->min_width != -1)
12490 int window_width = display_width;
12492 // when using static window height, use aspect ratio of display
12493 if (vp_window->min_height == -1)
12494 window_width = vp_window->height * display_width / display_height;
12496 vp_window->width = MAX(vp_window->min_width, window_width);
12499 if (vp_window->min_height != -1)
12501 int window_height = display_height;
12503 // when using static window width, use aspect ratio of display
12504 if (vp_window->min_width == -1)
12505 window_height = vp_window->width * display_height / display_width;
12507 vp_window->height = MAX(vp_window->min_height, window_height);
12510 if (vp_window->max_width != -1)
12511 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12513 if (vp_window->max_height != -1)
12514 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12516 int playfield_width = vp_window->width;
12517 int playfield_height = vp_window->height;
12519 // adjust playfield size and position according to specified margins
12521 playfield_width -= vp_playfield->margin_left;
12522 playfield_width -= vp_playfield->margin_right;
12524 playfield_height -= vp_playfield->margin_top;
12525 playfield_height -= vp_playfield->margin_bottom;
12527 // adjust playfield size if min/max width/height is specified
12529 if (vp_playfield->min_width != -1)
12530 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12532 if (vp_playfield->min_height != -1)
12533 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12535 if (vp_playfield->max_width != -1)
12536 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12538 if (vp_playfield->max_height != -1)
12539 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12541 // adjust playfield position according to specified alignment
12543 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12544 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12545 else if (vp_playfield->align == ALIGN_CENTER)
12546 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12547 else if (vp_playfield->align == ALIGN_RIGHT)
12548 vp_playfield->x += playfield_width - vp_playfield->width;
12550 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12551 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12552 else if (vp_playfield->valign == VALIGN_MIDDLE)
12553 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12554 else if (vp_playfield->valign == VALIGN_BOTTOM)
12555 vp_playfield->y += playfield_height - vp_playfield->height;
12557 vp_playfield->x += vp_playfield->margin_left;
12558 vp_playfield->y += vp_playfield->margin_top;
12560 // adjust individual playfield borders if only default border is specified
12562 if (vp_playfield->border_left == -1)
12563 vp_playfield->border_left = vp_playfield->border_size;
12564 if (vp_playfield->border_right == -1)
12565 vp_playfield->border_right = vp_playfield->border_size;
12566 if (vp_playfield->border_top == -1)
12567 vp_playfield->border_top = vp_playfield->border_size;
12568 if (vp_playfield->border_bottom == -1)
12569 vp_playfield->border_bottom = vp_playfield->border_size;
12571 // set dynamic playfield borders if borders are specified as undefined
12572 // (but only if window size was dynamic and playfield size was static)
12574 if (dynamic_window_width && !dynamic_playfield_width)
12576 if (vp_playfield->border_left == -1)
12578 vp_playfield->border_left = (vp_playfield->x -
12579 vp_playfield->margin_left);
12580 vp_playfield->x -= vp_playfield->border_left;
12581 vp_playfield->width += vp_playfield->border_left;
12584 if (vp_playfield->border_right == -1)
12586 vp_playfield->border_right = (vp_window->width -
12588 vp_playfield->width -
12589 vp_playfield->margin_right);
12590 vp_playfield->width += vp_playfield->border_right;
12594 if (dynamic_window_height && !dynamic_playfield_height)
12596 if (vp_playfield->border_top == -1)
12598 vp_playfield->border_top = (vp_playfield->y -
12599 vp_playfield->margin_top);
12600 vp_playfield->y -= vp_playfield->border_top;
12601 vp_playfield->height += vp_playfield->border_top;
12604 if (vp_playfield->border_bottom == -1)
12606 vp_playfield->border_bottom = (vp_window->height -
12608 vp_playfield->height -
12609 vp_playfield->margin_bottom);
12610 vp_playfield->height += vp_playfield->border_bottom;
12614 // adjust playfield size to be a multiple of a defined alignment tile size
12616 int align_size = vp_playfield->align_size;
12617 int playfield_xtiles = vp_playfield->width / align_size;
12618 int playfield_ytiles = vp_playfield->height / align_size;
12619 int playfield_width_corrected = playfield_xtiles * align_size;
12620 int playfield_height_corrected = playfield_ytiles * align_size;
12621 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12622 i == GFX_SPECIAL_ARG_EDITOR);
12624 if (is_playfield_mode &&
12625 dynamic_playfield_width &&
12626 vp_playfield->width != playfield_width_corrected)
12628 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12630 vp_playfield->width = playfield_width_corrected;
12632 if (vp_playfield->align == ALIGN_LEFT)
12634 vp_playfield->border_left += playfield_xdiff;
12636 else if (vp_playfield->align == ALIGN_RIGHT)
12638 vp_playfield->border_right += playfield_xdiff;
12640 else if (vp_playfield->align == ALIGN_CENTER)
12642 int border_left_diff = playfield_xdiff / 2;
12643 int border_right_diff = playfield_xdiff - border_left_diff;
12645 vp_playfield->border_left += border_left_diff;
12646 vp_playfield->border_right += border_right_diff;
12650 if (is_playfield_mode &&
12651 dynamic_playfield_height &&
12652 vp_playfield->height != playfield_height_corrected)
12654 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12656 vp_playfield->height = playfield_height_corrected;
12658 if (vp_playfield->valign == VALIGN_TOP)
12660 vp_playfield->border_top += playfield_ydiff;
12662 else if (vp_playfield->align == VALIGN_BOTTOM)
12664 vp_playfield->border_right += playfield_ydiff;
12666 else if (vp_playfield->align == VALIGN_MIDDLE)
12668 int border_top_diff = playfield_ydiff / 2;
12669 int border_bottom_diff = playfield_ydiff - border_top_diff;
12671 vp_playfield->border_top += border_top_diff;
12672 vp_playfield->border_bottom += border_bottom_diff;
12676 // adjust door positions according to specified alignment
12678 for (j = 0; j < 2; j++)
12680 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12682 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12683 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12684 else if (vp_door->align == ALIGN_CENTER)
12685 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12686 else if (vp_door->align == ALIGN_RIGHT)
12687 vp_door->x += vp_window->width - vp_door->width;
12689 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12690 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12691 else if (vp_door->valign == VALIGN_MIDDLE)
12692 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12693 else if (vp_door->valign == VALIGN_BOTTOM)
12694 vp_door->y += vp_window->height - vp_door->height;
12699 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12703 struct XYTileSize *dst, *src;
12706 editor_buttons_xy[] =
12709 &editor.button.element_left, &editor.palette.element_left,
12710 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12713 &editor.button.element_middle, &editor.palette.element_middle,
12714 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12717 &editor.button.element_right, &editor.palette.element_right,
12718 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12725 // set default position for element buttons to element graphics
12726 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12728 if ((*editor_buttons_xy[i].dst).x == -1 &&
12729 (*editor_buttons_xy[i].dst).y == -1)
12731 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12733 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12735 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12739 // adjust editor palette rows and columns if specified to be dynamic
12741 if (editor.palette.cols == -1)
12743 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12744 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12745 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12747 editor.palette.cols = (vp_width - sc_width) / bt_width;
12749 if (editor.palette.x == -1)
12751 int palette_width = editor.palette.cols * bt_width + sc_width;
12753 editor.palette.x = (vp_width - palette_width) / 2;
12757 if (editor.palette.rows == -1)
12759 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12760 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12761 int tx_height = getFontHeight(FONT_TEXT_2);
12763 editor.palette.rows = (vp_height - tx_height) / bt_height;
12765 if (editor.palette.y == -1)
12767 int palette_height = editor.palette.rows * bt_height + tx_height;
12769 editor.palette.y = (vp_height - palette_height) / 2;
12774 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12775 boolean initialize)
12777 // special case: check if network and preview player positions are redefined,
12778 // to compare this later against the main menu level preview being redefined
12779 struct TokenIntPtrInfo menu_config_players[] =
12781 { "main.network_players.x", &menu.main.network_players.redefined },
12782 { "main.network_players.y", &menu.main.network_players.redefined },
12783 { "main.preview_players.x", &menu.main.preview_players.redefined },
12784 { "main.preview_players.y", &menu.main.preview_players.redefined },
12785 { "preview.x", &preview.redefined },
12786 { "preview.y", &preview.redefined }
12792 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12793 *menu_config_players[i].value = FALSE;
12797 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12798 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12799 *menu_config_players[i].value = TRUE;
12803 static void InitMenuDesignSettings_PreviewPlayers(void)
12805 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12808 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12810 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12813 static void LoadMenuDesignSettingsFromFilename(char *filename)
12815 static struct TitleFadingInfo tfi;
12816 static struct TitleMessageInfo tmi;
12817 static struct TokenInfo title_tokens[] =
12819 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12820 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12821 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12822 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12823 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12827 static struct TokenInfo titlemessage_tokens[] =
12829 { TYPE_INTEGER, &tmi.x, ".x" },
12830 { TYPE_INTEGER, &tmi.y, ".y" },
12831 { TYPE_INTEGER, &tmi.width, ".width" },
12832 { TYPE_INTEGER, &tmi.height, ".height" },
12833 { TYPE_INTEGER, &tmi.chars, ".chars" },
12834 { TYPE_INTEGER, &tmi.lines, ".lines" },
12835 { TYPE_INTEGER, &tmi.align, ".align" },
12836 { TYPE_INTEGER, &tmi.valign, ".valign" },
12837 { TYPE_INTEGER, &tmi.font, ".font" },
12838 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12839 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12840 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12841 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12842 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12843 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12844 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12845 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12846 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12852 struct TitleFadingInfo *info;
12857 // initialize first titles from "enter screen" definitions, if defined
12858 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12859 { &title_first_default, "menu.enter_screen.TITLE" },
12861 // initialize title screens from "next screen" definitions, if defined
12862 { &title_initial_default, "menu.next_screen.TITLE" },
12863 { &title_default, "menu.next_screen.TITLE" },
12869 struct TitleMessageInfo *array;
12872 titlemessage_arrays[] =
12874 // initialize first titles from "enter screen" definitions, if defined
12875 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12876 { titlescreen_first, "menu.enter_screen.TITLE" },
12877 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12878 { titlemessage_first, "menu.enter_screen.TITLE" },
12880 // initialize titles from "next screen" definitions, if defined
12881 { titlescreen_initial, "menu.next_screen.TITLE" },
12882 { titlescreen, "menu.next_screen.TITLE" },
12883 { titlemessage_initial, "menu.next_screen.TITLE" },
12884 { titlemessage, "menu.next_screen.TITLE" },
12886 // overwrite titles with title definitions, if defined
12887 { titlescreen_initial_first, "[title_initial]" },
12888 { titlescreen_first, "[title]" },
12889 { titlemessage_initial_first, "[title_initial]" },
12890 { titlemessage_first, "[title]" },
12892 { titlescreen_initial, "[title_initial]" },
12893 { titlescreen, "[title]" },
12894 { titlemessage_initial, "[title_initial]" },
12895 { titlemessage, "[title]" },
12897 // overwrite titles with title screen/message definitions, if defined
12898 { titlescreen_initial_first, "[titlescreen_initial]" },
12899 { titlescreen_first, "[titlescreen]" },
12900 { titlemessage_initial_first, "[titlemessage_initial]" },
12901 { titlemessage_first, "[titlemessage]" },
12903 { titlescreen_initial, "[titlescreen_initial]" },
12904 { titlescreen, "[titlescreen]" },
12905 { titlemessage_initial, "[titlemessage_initial]" },
12906 { titlemessage, "[titlemessage]" },
12910 SetupFileHash *setup_file_hash;
12913 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12916 // the following initializes hierarchical values from dynamic configuration
12918 // special case: initialize with default values that may be overwritten
12919 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12920 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12922 struct TokenIntPtrInfo menu_config[] =
12924 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12925 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12926 { "menu.list_size", &menu.list_size[i] }
12929 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12931 char *token = menu_config[j].token;
12932 char *value = getHashEntry(setup_file_hash, token);
12935 *menu_config[j].value = get_integer_from_string(value);
12939 // special case: initialize with default values that may be overwritten
12940 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12941 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12943 struct TokenIntPtrInfo menu_config[] =
12945 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12946 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12947 { "menu.list_size.INFO", &menu.list_size_info[i] },
12948 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12949 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12952 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12954 char *token = menu_config[j].token;
12955 char *value = getHashEntry(setup_file_hash, token);
12958 *menu_config[j].value = get_integer_from_string(value);
12962 // special case: initialize with default values that may be overwritten
12963 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12964 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12966 struct TokenIntPtrInfo menu_config[] =
12968 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12969 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12972 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12974 char *token = menu_config[j].token;
12975 char *value = getHashEntry(setup_file_hash, token);
12978 *menu_config[j].value = get_integer_from_string(value);
12982 // special case: initialize with default values that may be overwritten
12983 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12984 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12986 struct TokenIntPtrInfo menu_config[] =
12988 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12989 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12990 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12991 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12992 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12993 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12994 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12995 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12996 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12997 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13000 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13002 char *token = menu_config[j].token;
13003 char *value = getHashEntry(setup_file_hash, token);
13006 *menu_config[j].value = get_integer_from_string(value);
13010 // special case: initialize with default values that may be overwritten
13011 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13012 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13014 struct TokenIntPtrInfo menu_config[] =
13016 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13017 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13018 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13019 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13020 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13021 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13022 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13023 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13024 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13027 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13029 char *token = menu_config[j].token;
13030 char *value = getHashEntry(setup_file_hash, token);
13033 *menu_config[j].value = get_token_parameter_value(token, value);
13037 // special case: initialize with default values that may be overwritten
13038 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13039 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13043 char *token_prefix;
13044 struct RectWithBorder *struct_ptr;
13048 { "viewport.window", &viewport.window[i] },
13049 { "viewport.playfield", &viewport.playfield[i] },
13050 { "viewport.door_1", &viewport.door_1[i] },
13051 { "viewport.door_2", &viewport.door_2[i] }
13054 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13056 struct TokenIntPtrInfo vp_config[] =
13058 { ".x", &vp_struct[j].struct_ptr->x },
13059 { ".y", &vp_struct[j].struct_ptr->y },
13060 { ".width", &vp_struct[j].struct_ptr->width },
13061 { ".height", &vp_struct[j].struct_ptr->height },
13062 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13063 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13064 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13065 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13066 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13067 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13068 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13069 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13070 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13071 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13072 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13073 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13074 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13075 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13076 { ".align", &vp_struct[j].struct_ptr->align },
13077 { ".valign", &vp_struct[j].struct_ptr->valign }
13080 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13082 char *token = getStringCat2(vp_struct[j].token_prefix,
13083 vp_config[k].token);
13084 char *value = getHashEntry(setup_file_hash, token);
13087 *vp_config[k].value = get_token_parameter_value(token, value);
13094 // special case: initialize with default values that may be overwritten
13095 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13096 for (i = 0; title_info[i].info != NULL; i++)
13098 struct TitleFadingInfo *info = title_info[i].info;
13099 char *base_token = title_info[i].text;
13101 for (j = 0; title_tokens[j].type != -1; j++)
13103 char *token = getStringCat2(base_token, title_tokens[j].text);
13104 char *value = getHashEntry(setup_file_hash, token);
13108 int parameter_value = get_token_parameter_value(token, value);
13112 *(int *)title_tokens[j].value = (int)parameter_value;
13121 // special case: initialize with default values that may be overwritten
13122 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13123 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13125 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13126 char *base_token = titlemessage_arrays[i].text;
13128 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13130 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13131 char *value = getHashEntry(setup_file_hash, token);
13135 int parameter_value = get_token_parameter_value(token, value);
13137 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13141 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13142 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13144 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13154 // read (and overwrite with) values that may be specified in config file
13155 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13157 // special case: check if network and preview player positions are redefined
13158 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13160 freeSetupFileHash(setup_file_hash);
13163 void LoadMenuDesignSettings(void)
13165 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13167 InitMenuDesignSettings_Static();
13168 InitMenuDesignSettings_SpecialPreProcessing();
13169 InitMenuDesignSettings_PreviewPlayers();
13171 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13173 // first look for special settings configured in level series config
13174 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13176 if (fileExists(filename_base))
13177 LoadMenuDesignSettingsFromFilename(filename_base);
13180 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13182 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13183 LoadMenuDesignSettingsFromFilename(filename_local);
13185 InitMenuDesignSettings_SpecialPostProcessing();
13188 void LoadMenuDesignSettings_AfterGraphics(void)
13190 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13193 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13194 boolean ignore_defaults)
13198 for (i = 0; sound_config_vars[i].token != NULL; i++)
13200 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13202 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13203 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13207 *sound_config_vars[i].value =
13208 get_token_parameter_value(sound_config_vars[i].token, value);
13212 void InitSoundSettings_Static(void)
13214 // always start with reliable default values from static default config
13215 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13218 static void LoadSoundSettingsFromFilename(char *filename)
13220 SetupFileHash *setup_file_hash;
13222 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13225 // read (and overwrite with) values that may be specified in config file
13226 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13228 freeSetupFileHash(setup_file_hash);
13231 void LoadSoundSettings(void)
13233 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13235 InitSoundSettings_Static();
13237 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13239 // first look for special settings configured in level series config
13240 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13242 if (fileExists(filename_base))
13243 LoadSoundSettingsFromFilename(filename_base);
13246 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13248 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13249 LoadSoundSettingsFromFilename(filename_local);
13252 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13254 char *filename = getEditorSetupFilename();
13255 SetupFileList *setup_file_list, *list;
13256 SetupFileHash *element_hash;
13257 int num_unknown_tokens = 0;
13260 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13263 element_hash = newSetupFileHash();
13265 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13266 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13268 // determined size may be larger than needed (due to unknown elements)
13270 for (list = setup_file_list; list != NULL; list = list->next)
13273 // add space for up to 3 more elements for padding that may be needed
13274 *num_elements += 3;
13276 // free memory for old list of elements, if needed
13277 checked_free(*elements);
13279 // allocate memory for new list of elements
13280 *elements = checked_malloc(*num_elements * sizeof(int));
13283 for (list = setup_file_list; list != NULL; list = list->next)
13285 char *value = getHashEntry(element_hash, list->token);
13287 if (value == NULL) // try to find obsolete token mapping
13289 char *mapped_token = get_mapped_token(list->token);
13291 if (mapped_token != NULL)
13293 value = getHashEntry(element_hash, mapped_token);
13295 free(mapped_token);
13301 (*elements)[(*num_elements)++] = atoi(value);
13305 if (num_unknown_tokens == 0)
13308 Warn("unknown token(s) found in config file:");
13309 Warn("- config file: '%s'", filename);
13311 num_unknown_tokens++;
13314 Warn("- token: '%s'", list->token);
13318 if (num_unknown_tokens > 0)
13321 while (*num_elements % 4) // pad with empty elements, if needed
13322 (*elements)[(*num_elements)++] = EL_EMPTY;
13324 freeSetupFileList(setup_file_list);
13325 freeSetupFileHash(element_hash);
13328 for (i = 0; i < *num_elements; i++)
13329 Debug("editor", "element '%s' [%d]\n",
13330 element_info[(*elements)[i]].token_name, (*elements)[i]);
13334 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13337 SetupFileHash *setup_file_hash = NULL;
13338 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13339 char *filename_music, *filename_prefix, *filename_info;
13345 token_to_value_ptr[] =
13347 { "title_header", &tmp_music_file_info.title_header },
13348 { "artist_header", &tmp_music_file_info.artist_header },
13349 { "album_header", &tmp_music_file_info.album_header },
13350 { "year_header", &tmp_music_file_info.year_header },
13351 { "played_header", &tmp_music_file_info.played_header },
13353 { "title", &tmp_music_file_info.title },
13354 { "artist", &tmp_music_file_info.artist },
13355 { "album", &tmp_music_file_info.album },
13356 { "year", &tmp_music_file_info.year },
13357 { "played", &tmp_music_file_info.played },
13363 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13364 getCustomMusicFilename(basename));
13366 if (filename_music == NULL)
13369 // ---------- try to replace file extension ----------
13371 filename_prefix = getStringCopy(filename_music);
13372 if (strrchr(filename_prefix, '.') != NULL)
13373 *strrchr(filename_prefix, '.') = '\0';
13374 filename_info = getStringCat2(filename_prefix, ".txt");
13376 if (fileExists(filename_info))
13377 setup_file_hash = loadSetupFileHash(filename_info);
13379 free(filename_prefix);
13380 free(filename_info);
13382 if (setup_file_hash == NULL)
13384 // ---------- try to add file extension ----------
13386 filename_prefix = getStringCopy(filename_music);
13387 filename_info = getStringCat2(filename_prefix, ".txt");
13389 if (fileExists(filename_info))
13390 setup_file_hash = loadSetupFileHash(filename_info);
13392 free(filename_prefix);
13393 free(filename_info);
13396 if (setup_file_hash == NULL)
13399 // ---------- music file info found ----------
13401 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13403 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13405 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13407 *token_to_value_ptr[i].value_ptr =
13408 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13411 tmp_music_file_info.basename = getStringCopy(basename);
13412 tmp_music_file_info.music = music;
13413 tmp_music_file_info.is_sound = is_sound;
13415 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13416 *new_music_file_info = tmp_music_file_info;
13418 return new_music_file_info;
13421 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13423 return get_music_file_info_ext(basename, music, FALSE);
13426 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13428 return get_music_file_info_ext(basename, sound, TRUE);
13431 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13432 char *basename, boolean is_sound)
13434 for (; list != NULL; list = list->next)
13435 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13441 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13443 return music_info_listed_ext(list, basename, FALSE);
13446 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13448 return music_info_listed_ext(list, basename, TRUE);
13451 void LoadMusicInfo(void)
13453 int num_music_noconf = getMusicListSize_NoConf();
13454 int num_music = getMusicListSize();
13455 int num_sounds = getSoundListSize();
13456 struct FileInfo *music, *sound;
13457 struct MusicFileInfo *next, **new;
13461 while (music_file_info != NULL)
13463 next = music_file_info->next;
13465 checked_free(music_file_info->basename);
13467 checked_free(music_file_info->title_header);
13468 checked_free(music_file_info->artist_header);
13469 checked_free(music_file_info->album_header);
13470 checked_free(music_file_info->year_header);
13471 checked_free(music_file_info->played_header);
13473 checked_free(music_file_info->title);
13474 checked_free(music_file_info->artist);
13475 checked_free(music_file_info->album);
13476 checked_free(music_file_info->year);
13477 checked_free(music_file_info->played);
13479 free(music_file_info);
13481 music_file_info = next;
13484 new = &music_file_info;
13486 // get (configured or unconfigured) music file info for all levels
13487 for (i = leveldir_current->first_level;
13488 i <= leveldir_current->last_level; i++)
13492 if (levelset.music[i] != MUS_UNDEFINED)
13494 // get music file info for configured level music
13495 music_nr = levelset.music[i];
13497 else if (num_music_noconf > 0)
13499 // get music file info for unconfigured level music
13500 int level_pos = i - leveldir_current->first_level;
13502 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13509 char *basename = getMusicInfoEntryFilename(music_nr);
13511 if (basename == NULL)
13514 if (!music_info_listed(music_file_info, basename))
13516 *new = get_music_file_info(basename, music_nr);
13519 new = &(*new)->next;
13523 // get music file info for all remaining configured music files
13524 for (i = 0; i < num_music; i++)
13526 music = getMusicListEntry(i);
13528 if (music->filename == NULL)
13531 if (strEqual(music->filename, UNDEFINED_FILENAME))
13534 // a configured file may be not recognized as music
13535 if (!FileIsMusic(music->filename))
13538 if (!music_info_listed(music_file_info, music->filename))
13540 *new = get_music_file_info(music->filename, i);
13543 new = &(*new)->next;
13547 // get sound file info for all configured sound files
13548 for (i = 0; i < num_sounds; i++)
13550 sound = getSoundListEntry(i);
13552 if (sound->filename == NULL)
13555 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13558 // a configured file may be not recognized as sound
13559 if (!FileIsSound(sound->filename))
13562 if (!sound_info_listed(music_file_info, sound->filename))
13564 *new = get_sound_file_info(sound->filename, i);
13566 new = &(*new)->next;
13570 // add pointers to previous list nodes
13572 struct MusicFileInfo *node = music_file_info;
13574 while (node != NULL)
13577 node->next->prev = node;
13583 static void add_helpanim_entry(int element, int action, int direction,
13584 int delay, int *num_list_entries)
13586 struct HelpAnimInfo *new_list_entry;
13587 (*num_list_entries)++;
13590 checked_realloc(helpanim_info,
13591 *num_list_entries * sizeof(struct HelpAnimInfo));
13592 new_list_entry = &helpanim_info[*num_list_entries - 1];
13594 new_list_entry->element = element;
13595 new_list_entry->action = action;
13596 new_list_entry->direction = direction;
13597 new_list_entry->delay = delay;
13600 static void print_unknown_token(char *filename, char *token, int token_nr)
13605 Warn("unknown token(s) found in config file:");
13606 Warn("- config file: '%s'", filename);
13609 Warn("- token: '%s'", token);
13612 static void print_unknown_token_end(int token_nr)
13618 void LoadHelpAnimInfo(void)
13620 char *filename = getHelpAnimFilename();
13621 SetupFileList *setup_file_list = NULL, *list;
13622 SetupFileHash *element_hash, *action_hash, *direction_hash;
13623 int num_list_entries = 0;
13624 int num_unknown_tokens = 0;
13627 if (fileExists(filename))
13628 setup_file_list = loadSetupFileList(filename);
13630 if (setup_file_list == NULL)
13632 // use reliable default values from static configuration
13633 SetupFileList *insert_ptr;
13635 insert_ptr = setup_file_list =
13636 newSetupFileList(helpanim_config[0].token,
13637 helpanim_config[0].value);
13639 for (i = 1; helpanim_config[i].token; i++)
13640 insert_ptr = addListEntry(insert_ptr,
13641 helpanim_config[i].token,
13642 helpanim_config[i].value);
13645 element_hash = newSetupFileHash();
13646 action_hash = newSetupFileHash();
13647 direction_hash = newSetupFileHash();
13649 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13650 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13652 for (i = 0; i < NUM_ACTIONS; i++)
13653 setHashEntry(action_hash, element_action_info[i].suffix,
13654 i_to_a(element_action_info[i].value));
13656 // do not store direction index (bit) here, but direction value!
13657 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13658 setHashEntry(direction_hash, element_direction_info[i].suffix,
13659 i_to_a(1 << element_direction_info[i].value));
13661 for (list = setup_file_list; list != NULL; list = list->next)
13663 char *element_token, *action_token, *direction_token;
13664 char *element_value, *action_value, *direction_value;
13665 int delay = atoi(list->value);
13667 if (strEqual(list->token, "end"))
13669 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13674 /* first try to break element into element/action/direction parts;
13675 if this does not work, also accept combined "element[.act][.dir]"
13676 elements (like "dynamite.active"), which are unique elements */
13678 if (strchr(list->token, '.') == NULL) // token contains no '.'
13680 element_value = getHashEntry(element_hash, list->token);
13681 if (element_value != NULL) // element found
13682 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13683 &num_list_entries);
13686 // no further suffixes found -- this is not an element
13687 print_unknown_token(filename, list->token, num_unknown_tokens++);
13693 // token has format "<prefix>.<something>"
13695 action_token = strchr(list->token, '.'); // suffix may be action ...
13696 direction_token = action_token; // ... or direction
13698 element_token = getStringCopy(list->token);
13699 *strchr(element_token, '.') = '\0';
13701 element_value = getHashEntry(element_hash, element_token);
13703 if (element_value == NULL) // this is no element
13705 element_value = getHashEntry(element_hash, list->token);
13706 if (element_value != NULL) // combined element found
13707 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13708 &num_list_entries);
13710 print_unknown_token(filename, list->token, num_unknown_tokens++);
13712 free(element_token);
13717 action_value = getHashEntry(action_hash, action_token);
13719 if (action_value != NULL) // action found
13721 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13722 &num_list_entries);
13724 free(element_token);
13729 direction_value = getHashEntry(direction_hash, direction_token);
13731 if (direction_value != NULL) // direction found
13733 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13734 &num_list_entries);
13736 free(element_token);
13741 if (strchr(action_token + 1, '.') == NULL)
13743 // no further suffixes found -- this is not an action nor direction
13745 element_value = getHashEntry(element_hash, list->token);
13746 if (element_value != NULL) // combined element found
13747 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13748 &num_list_entries);
13750 print_unknown_token(filename, list->token, num_unknown_tokens++);
13752 free(element_token);
13757 // token has format "<prefix>.<suffix>.<something>"
13759 direction_token = strchr(action_token + 1, '.');
13761 action_token = getStringCopy(action_token);
13762 *strchr(action_token + 1, '.') = '\0';
13764 action_value = getHashEntry(action_hash, action_token);
13766 if (action_value == NULL) // this is no action
13768 element_value = getHashEntry(element_hash, list->token);
13769 if (element_value != NULL) // combined element found
13770 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13771 &num_list_entries);
13773 print_unknown_token(filename, list->token, num_unknown_tokens++);
13775 free(element_token);
13776 free(action_token);
13781 direction_value = getHashEntry(direction_hash, direction_token);
13783 if (direction_value != NULL) // direction found
13785 add_helpanim_entry(atoi(element_value), atoi(action_value),
13786 atoi(direction_value), delay, &num_list_entries);
13788 free(element_token);
13789 free(action_token);
13794 // this is no direction
13796 element_value = getHashEntry(element_hash, list->token);
13797 if (element_value != NULL) // combined element found
13798 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13799 &num_list_entries);
13801 print_unknown_token(filename, list->token, num_unknown_tokens++);
13803 free(element_token);
13804 free(action_token);
13807 print_unknown_token_end(num_unknown_tokens);
13809 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13810 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13812 freeSetupFileList(setup_file_list);
13813 freeSetupFileHash(element_hash);
13814 freeSetupFileHash(action_hash);
13815 freeSetupFileHash(direction_hash);
13818 for (i = 0; i < num_list_entries; i++)
13819 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13820 EL_NAME(helpanim_info[i].element),
13821 helpanim_info[i].element,
13822 helpanim_info[i].action,
13823 helpanim_info[i].direction,
13824 helpanim_info[i].delay);
13828 void LoadHelpTextInfo(void)
13830 char *filename = getHelpTextFilename();
13833 if (helptext_info != NULL)
13835 freeSetupFileHash(helptext_info);
13836 helptext_info = NULL;
13839 if (fileExists(filename))
13840 helptext_info = loadSetupFileHash(filename);
13842 if (helptext_info == NULL)
13844 // use reliable default values from static configuration
13845 helptext_info = newSetupFileHash();
13847 for (i = 0; helptext_config[i].token; i++)
13848 setHashEntry(helptext_info,
13849 helptext_config[i].token,
13850 helptext_config[i].value);
13854 BEGIN_HASH_ITERATION(helptext_info, itr)
13856 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13857 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13859 END_HASH_ITERATION(hash, itr)
13864 // ----------------------------------------------------------------------------
13866 // ----------------------------------------------------------------------------
13868 #define MAX_NUM_CONVERT_LEVELS 1000
13870 void ConvertLevels(void)
13872 static LevelDirTree *convert_leveldir = NULL;
13873 static int convert_level_nr = -1;
13874 static int num_levels_handled = 0;
13875 static int num_levels_converted = 0;
13876 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13879 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13880 global.convert_leveldir);
13882 if (convert_leveldir == NULL)
13883 Fail("no such level identifier: '%s'", global.convert_leveldir);
13885 leveldir_current = convert_leveldir;
13887 if (global.convert_level_nr != -1)
13889 convert_leveldir->first_level = global.convert_level_nr;
13890 convert_leveldir->last_level = global.convert_level_nr;
13893 convert_level_nr = convert_leveldir->first_level;
13895 PrintLine("=", 79);
13896 Print("Converting levels\n");
13897 PrintLine("-", 79);
13898 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13899 Print("Level series name: '%s'\n", convert_leveldir->name);
13900 Print("Level series author: '%s'\n", convert_leveldir->author);
13901 Print("Number of levels: %d\n", convert_leveldir->levels);
13902 PrintLine("=", 79);
13905 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13906 levels_failed[i] = FALSE;
13908 while (convert_level_nr <= convert_leveldir->last_level)
13910 char *level_filename;
13913 level_nr = convert_level_nr++;
13915 Print("Level %03d: ", level_nr);
13917 LoadLevel(level_nr);
13918 if (level.no_level_file || level.no_valid_file)
13920 Print("(no level)\n");
13924 Print("converting level ... ");
13927 // special case: conversion of some EMC levels as requested by ACME
13928 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13931 level_filename = getDefaultLevelFilename(level_nr);
13932 new_level = !fileExists(level_filename);
13936 SaveLevel(level_nr);
13938 num_levels_converted++;
13940 Print("converted.\n");
13944 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13945 levels_failed[level_nr] = TRUE;
13947 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13950 num_levels_handled++;
13954 PrintLine("=", 79);
13955 Print("Number of levels handled: %d\n", num_levels_handled);
13956 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13957 (num_levels_handled ?
13958 num_levels_converted * 100 / num_levels_handled : 0));
13959 PrintLine("-", 79);
13960 Print("Summary (for automatic parsing by scripts):\n");
13961 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13962 convert_leveldir->identifier, num_levels_converted,
13963 num_levels_handled,
13964 (num_levels_handled ?
13965 num_levels_converted * 100 / num_levels_handled : 0));
13967 if (num_levels_handled != num_levels_converted)
13969 Print(", FAILED:");
13970 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13971 if (levels_failed[i])
13976 PrintLine("=", 79);
13978 CloseAllAndExit(0);
13982 // ----------------------------------------------------------------------------
13983 // create and save images for use in level sketches (raw BMP format)
13984 // ----------------------------------------------------------------------------
13986 void CreateLevelSketchImages(void)
13992 InitElementPropertiesGfxElement();
13994 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13995 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13997 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13999 int element = getMappedElement(i);
14000 char basename1[16];
14001 char basename2[16];
14005 sprintf(basename1, "%04d.bmp", i);
14006 sprintf(basename2, "%04ds.bmp", i);
14008 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14009 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14011 DrawSizedElement(0, 0, element, TILESIZE);
14012 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14014 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14015 Fail("cannot save level sketch image file '%s'", filename1);
14017 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14018 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14020 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14021 Fail("cannot save level sketch image file '%s'", filename2);
14026 // create corresponding SQL statements (for normal and small images)
14029 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14030 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14033 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14034 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14036 // optional: create content for forum level sketch demonstration post
14038 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14041 FreeBitmap(bitmap1);
14042 FreeBitmap(bitmap2);
14045 fprintf(stderr, "\n");
14047 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14049 CloseAllAndExit(0);
14053 // ----------------------------------------------------------------------------
14054 // create and save images for element collecting animations (raw BMP format)
14055 // ----------------------------------------------------------------------------
14057 static boolean createCollectImage(int element)
14059 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14062 void CreateCollectElementImages(void)
14066 int anim_frames = num_steps - 1;
14067 int tile_size = TILESIZE;
14068 int anim_width = tile_size * anim_frames;
14069 int anim_height = tile_size;
14070 int num_collect_images = 0;
14071 int pos_collect_images = 0;
14073 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14074 if (createCollectImage(i))
14075 num_collect_images++;
14077 Info("Creating %d element collecting animation images ...",
14078 num_collect_images);
14080 int dst_width = anim_width * 2;
14081 int dst_height = anim_height * num_collect_images / 2;
14082 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14083 char *basename_bmp = "RocksCollect.bmp";
14084 char *basename_png = "RocksCollect.png";
14085 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14086 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14087 int len_filename_bmp = strlen(filename_bmp);
14088 int len_filename_png = strlen(filename_png);
14089 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14090 char cmd_convert[max_command_len];
14092 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14096 // force using RGBA surface for destination bitmap
14097 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14098 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14100 dst_bitmap->surface =
14101 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14103 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14105 if (!createCollectImage(i))
14108 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14109 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14110 int graphic = el2img(i);
14111 char *token_name = element_info[i].token_name;
14112 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14113 Bitmap *src_bitmap;
14116 Info("- creating collecting image for '%s' ...", token_name);
14118 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14120 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14121 tile_size, tile_size, 0, 0);
14123 // force using RGBA surface for temporary bitmap (using transparent black)
14124 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14125 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14127 tmp_bitmap->surface =
14128 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14130 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14132 for (j = 0; j < anim_frames; j++)
14134 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14135 int frame_size = frame_size_final * num_steps;
14136 int offset = (tile_size - frame_size_final) / 2;
14137 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14139 while (frame_size > frame_size_final)
14143 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14145 FreeBitmap(frame_bitmap);
14147 frame_bitmap = half_bitmap;
14150 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14151 frame_size_final, frame_size_final,
14152 dst_x + j * tile_size + offset, dst_y + offset);
14154 FreeBitmap(frame_bitmap);
14157 tmp_bitmap->surface_masked = NULL;
14159 FreeBitmap(tmp_bitmap);
14161 pos_collect_images++;
14164 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14165 Fail("cannot save element collecting image file '%s'", filename_bmp);
14167 FreeBitmap(dst_bitmap);
14169 Info("Converting image file from BMP to PNG ...");
14171 if (system(cmd_convert) != 0)
14172 Fail("converting image file failed");
14174 unlink(filename_bmp);
14178 CloseAllAndExit(0);
14182 // ----------------------------------------------------------------------------
14183 // create and save images for custom and group elements (raw BMP format)
14184 // ----------------------------------------------------------------------------
14186 void CreateCustomElementImages(char *directory)
14188 char *src_basename = "RocksCE-template.ilbm";
14189 char *dst_basename = "RocksCE.bmp";
14190 char *src_filename = getPath2(directory, src_basename);
14191 char *dst_filename = getPath2(directory, dst_basename);
14192 Bitmap *src_bitmap;
14194 int yoffset_ce = 0;
14195 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14198 InitVideoDefaults();
14200 ReCreateBitmap(&backbuffer, video.width, video.height);
14202 src_bitmap = LoadImage(src_filename);
14204 bitmap = CreateBitmap(TILEX * 16 * 2,
14205 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14208 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14215 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14216 TILEX * x, TILEY * y + yoffset_ce);
14218 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14220 TILEX * x + TILEX * 16,
14221 TILEY * y + yoffset_ce);
14223 for (j = 2; j >= 0; j--)
14227 BlitBitmap(src_bitmap, bitmap,
14228 TILEX + c * 7, 0, 6, 10,
14229 TILEX * x + 6 + j * 7,
14230 TILEY * y + 11 + yoffset_ce);
14232 BlitBitmap(src_bitmap, bitmap,
14233 TILEX + c * 8, TILEY, 6, 10,
14234 TILEX * 16 + TILEX * x + 6 + j * 8,
14235 TILEY * y + 10 + yoffset_ce);
14241 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14248 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14249 TILEX * x, TILEY * y + yoffset_ge);
14251 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14253 TILEX * x + TILEX * 16,
14254 TILEY * y + yoffset_ge);
14256 for (j = 1; j >= 0; j--)
14260 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14261 TILEX * x + 6 + j * 10,
14262 TILEY * y + 11 + yoffset_ge);
14264 BlitBitmap(src_bitmap, bitmap,
14265 TILEX + c * 8, TILEY + 12, 6, 10,
14266 TILEX * 16 + TILEX * x + 10 + j * 8,
14267 TILEY * y + 10 + yoffset_ge);
14273 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14274 Fail("cannot save CE graphics file '%s'", dst_filename);
14276 FreeBitmap(bitmap);
14278 CloseAllAndExit(0);