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++)
3758 cave->level_time[i] = level->time;
3759 cave->level_diamonds[i] = level->gems_needed;
3760 cave->level_magic_wall_time[i] = level->time_magic_wall;
3763 cave->level_speed[i] = level->bd_cycle_delay_ms;
3764 cave->level_ckdelay[i] = level->bd_cycle_delay_c64;
3765 cave->level_hatching_delay_frame[i] = level->bd_hatching_delay_cycles;
3766 cave->level_hatching_delay_time[i] = level->bd_hatching_delay_seconds;
3769 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3773 cave->diamond_value = level->score[SC_EMERALD];
3774 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3777 cave->scheduling = level->bd_scheduling_type;
3778 cave->pal_timing = level->bd_pal_timing;
3780 // compatibility settings
3781 cave->lineshift = level->bd_line_shifting_borders;
3782 cave->wraparound_objects = level->bd_wraparound_objects;
3783 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3784 cave->short_explosions = level->bd_short_explosions;
3785 cave->gravity_affects_all = level->bd_gravity_affects_all;
3788 cave->intermission = level->bd_intermission;
3790 // player properties
3791 cave->diagonal_movements = level->bd_diagonal_movements;
3792 cave->active_is_first_found = level->bd_topmost_player_active;
3795 strncpy(cave->name, level->name, sizeof(GdString));
3796 cave->name[sizeof(GdString) - 1] = '\0';
3798 // playfield elements
3799 for (x = 0; x < cave->w; x++)
3800 for (y = 0; y < cave->h; y++)
3801 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3804 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3806 struct LevelInfo_BD *level_bd = level->native_bd_level;
3807 GdCave *cave = level_bd->cave;
3808 int bd_level_nr = level_bd->level_nr;
3811 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3812 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3815 level->time = cave->level_time[bd_level_nr];
3816 level->gems_needed = cave->level_diamonds[bd_level_nr];
3817 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3820 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3821 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3822 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3823 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3826 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3827 level->score[SC_EMERALD] = cave->diamond_value;
3828 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3831 level->bd_scheduling_type = cave->scheduling;
3832 level->bd_pal_timing = cave->pal_timing;
3834 // compatibility settings
3835 level->bd_line_shifting_borders = cave->lineshift;
3836 level->bd_wraparound_objects = cave->wraparound_objects;
3837 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
3838 level->bd_short_explosions = cave->short_explosions;
3839 level->bd_gravity_affects_all = cave->gravity_affects_all;
3842 level->bd_intermission = cave->intermission;
3844 // player properties
3845 level->bd_diagonal_movements = cave->diagonal_movements;
3846 level->bd_topmost_player_active = cave->active_is_first_found;
3849 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3851 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3852 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3854 // playfield elements
3855 for (x = 0; x < level->fieldx; x++)
3856 for (y = 0; y < level->fieldy; y++)
3857 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3859 checked_free(cave_name);
3862 static void setTapeInfoToDefaults(void);
3864 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3866 struct LevelInfo_BD *level_bd = level->native_bd_level;
3867 GdCave *cave = level_bd->cave;
3868 GdReplay *replay = level_bd->replay;
3874 // always start with reliable default values
3875 setTapeInfoToDefaults();
3877 tape.level_nr = level_nr; // (currently not used)
3878 tape.random_seed = replay->seed;
3880 TapeSetDateFromIsoDateString(replay->date);
3883 tape.pos[tape.counter].delay = 0;
3885 tape.bd_replay = TRUE;
3887 // all time calculations only used to display approximate tape time
3888 int cave_speed = cave->speed;
3889 int milliseconds_game = 0;
3890 int milliseconds_elapsed = 20;
3892 for (i = 0; i < replay->movements->len; i++)
3894 int replay_action = replay->movements->data[i];
3895 int tape_action = map_action_BD_to_RND(replay_action);
3896 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3897 boolean success = 0;
3901 success = TapeAddAction(action);
3903 milliseconds_game += milliseconds_elapsed;
3905 if (milliseconds_game >= cave_speed)
3907 milliseconds_game -= cave_speed;
3914 tape.pos[tape.counter].delay = 0;
3915 tape.pos[tape.counter].action[0] = 0;
3919 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3925 TapeHaltRecording();
3929 // ----------------------------------------------------------------------------
3930 // functions for loading EM level
3931 // ----------------------------------------------------------------------------
3933 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3935 static int ball_xy[8][2] =
3946 struct LevelInfo_EM *level_em = level->native_em_level;
3947 struct CAVE *cav = level_em->cav;
3950 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3951 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3953 cav->time_seconds = level->time;
3954 cav->gems_needed = level->gems_needed;
3956 cav->emerald_score = level->score[SC_EMERALD];
3957 cav->diamond_score = level->score[SC_DIAMOND];
3958 cav->alien_score = level->score[SC_ROBOT];
3959 cav->tank_score = level->score[SC_SPACESHIP];
3960 cav->bug_score = level->score[SC_BUG];
3961 cav->eater_score = level->score[SC_YAMYAM];
3962 cav->nut_score = level->score[SC_NUT];
3963 cav->dynamite_score = level->score[SC_DYNAMITE];
3964 cav->key_score = level->score[SC_KEY];
3965 cav->exit_score = level->score[SC_TIME_BONUS];
3967 cav->num_eater_arrays = level->num_yamyam_contents;
3969 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3970 for (y = 0; y < 3; y++)
3971 for (x = 0; x < 3; x++)
3972 cav->eater_array[i][y * 3 + x] =
3973 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3975 cav->amoeba_time = level->amoeba_speed;
3976 cav->wonderwall_time = level->time_magic_wall;
3977 cav->wheel_time = level->time_wheel;
3979 cav->android_move_time = level->android_move_time;
3980 cav->android_clone_time = level->android_clone_time;
3981 cav->ball_random = level->ball_random;
3982 cav->ball_active = level->ball_active_initial;
3983 cav->ball_time = level->ball_time;
3984 cav->num_ball_arrays = level->num_ball_contents;
3986 cav->lenses_score = level->lenses_score;
3987 cav->magnify_score = level->magnify_score;
3988 cav->slurp_score = level->slurp_score;
3990 cav->lenses_time = level->lenses_time;
3991 cav->magnify_time = level->magnify_time;
3993 cav->wind_time = 9999;
3994 cav->wind_direction =
3995 map_direction_RND_to_EM(level->wind_direction_initial);
3997 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3998 for (j = 0; j < 8; j++)
3999 cav->ball_array[i][j] =
4000 map_element_RND_to_EM_cave(level->ball_content[i].
4001 e[ball_xy[j][0]][ball_xy[j][1]]);
4003 map_android_clone_elements_RND_to_EM(level);
4005 // first fill the complete playfield with the empty space element
4006 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4007 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4008 cav->cave[x][y] = Cblank;
4010 // then copy the real level contents from level file into the playfield
4011 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4013 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4015 if (level->field[x][y] == EL_AMOEBA_DEAD)
4016 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4018 cav->cave[x][y] = new_element;
4021 for (i = 0; i < MAX_PLAYERS; i++)
4023 cav->player_x[i] = -1;
4024 cav->player_y[i] = -1;
4027 // initialize player positions and delete players from the playfield
4028 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4030 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4032 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4034 cav->player_x[player_nr] = x;
4035 cav->player_y[player_nr] = y;
4037 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4042 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4044 static int ball_xy[8][2] =
4055 struct LevelInfo_EM *level_em = level->native_em_level;
4056 struct CAVE *cav = level_em->cav;
4059 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4060 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4062 level->time = cav->time_seconds;
4063 level->gems_needed = cav->gems_needed;
4065 sprintf(level->name, "Level %d", level->file_info.nr);
4067 level->score[SC_EMERALD] = cav->emerald_score;
4068 level->score[SC_DIAMOND] = cav->diamond_score;
4069 level->score[SC_ROBOT] = cav->alien_score;
4070 level->score[SC_SPACESHIP] = cav->tank_score;
4071 level->score[SC_BUG] = cav->bug_score;
4072 level->score[SC_YAMYAM] = cav->eater_score;
4073 level->score[SC_NUT] = cav->nut_score;
4074 level->score[SC_DYNAMITE] = cav->dynamite_score;
4075 level->score[SC_KEY] = cav->key_score;
4076 level->score[SC_TIME_BONUS] = cav->exit_score;
4078 level->num_yamyam_contents = cav->num_eater_arrays;
4080 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4081 for (y = 0; y < 3; y++)
4082 for (x = 0; x < 3; x++)
4083 level->yamyam_content[i].e[x][y] =
4084 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4086 level->amoeba_speed = cav->amoeba_time;
4087 level->time_magic_wall = cav->wonderwall_time;
4088 level->time_wheel = cav->wheel_time;
4090 level->android_move_time = cav->android_move_time;
4091 level->android_clone_time = cav->android_clone_time;
4092 level->ball_random = cav->ball_random;
4093 level->ball_active_initial = cav->ball_active;
4094 level->ball_time = cav->ball_time;
4095 level->num_ball_contents = cav->num_ball_arrays;
4097 level->lenses_score = cav->lenses_score;
4098 level->magnify_score = cav->magnify_score;
4099 level->slurp_score = cav->slurp_score;
4101 level->lenses_time = cav->lenses_time;
4102 level->magnify_time = cav->magnify_time;
4104 level->wind_direction_initial =
4105 map_direction_EM_to_RND(cav->wind_direction);
4107 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4108 for (j = 0; j < 8; j++)
4109 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4110 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4112 map_android_clone_elements_EM_to_RND(level);
4114 // convert the playfield (some elements need special treatment)
4115 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4117 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4119 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4120 new_element = EL_AMOEBA_DEAD;
4122 level->field[x][y] = new_element;
4125 for (i = 0; i < MAX_PLAYERS; i++)
4127 // in case of all players set to the same field, use the first player
4128 int nr = MAX_PLAYERS - i - 1;
4129 int jx = cav->player_x[nr];
4130 int jy = cav->player_y[nr];
4132 if (jx != -1 && jy != -1)
4133 level->field[jx][jy] = EL_PLAYER_1 + nr;
4136 // time score is counted for each 10 seconds left in Emerald Mine levels
4137 level->time_score_base = 10;
4141 // ----------------------------------------------------------------------------
4142 // functions for loading SP level
4143 // ----------------------------------------------------------------------------
4145 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4147 struct LevelInfo_SP *level_sp = level->native_sp_level;
4148 LevelInfoType *header = &level_sp->header;
4151 level_sp->width = level->fieldx;
4152 level_sp->height = level->fieldy;
4154 for (x = 0; x < level->fieldx; x++)
4155 for (y = 0; y < level->fieldy; y++)
4156 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4158 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4160 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4161 header->LevelTitle[i] = level->name[i];
4162 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4164 header->InfotronsNeeded = level->gems_needed;
4166 header->SpecialPortCount = 0;
4168 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4170 boolean gravity_port_found = FALSE;
4171 boolean gravity_port_valid = FALSE;
4172 int gravity_port_flag;
4173 int gravity_port_base_element;
4174 int element = level->field[x][y];
4176 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4177 element <= EL_SP_GRAVITY_ON_PORT_UP)
4179 gravity_port_found = TRUE;
4180 gravity_port_valid = TRUE;
4181 gravity_port_flag = 1;
4182 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4184 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4185 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4187 gravity_port_found = TRUE;
4188 gravity_port_valid = TRUE;
4189 gravity_port_flag = 0;
4190 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4192 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4193 element <= EL_SP_GRAVITY_PORT_UP)
4195 // change R'n'D style gravity inverting special port to normal port
4196 // (there are no gravity inverting ports in native Supaplex engine)
4198 gravity_port_found = TRUE;
4199 gravity_port_valid = FALSE;
4200 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4203 if (gravity_port_found)
4205 if (gravity_port_valid &&
4206 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4208 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4210 port->PortLocation = (y * level->fieldx + x) * 2;
4211 port->Gravity = gravity_port_flag;
4213 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4215 header->SpecialPortCount++;
4219 // change special gravity port to normal port
4221 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4224 level_sp->playfield[x][y] = element - EL_SP_START;
4229 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4231 struct LevelInfo_SP *level_sp = level->native_sp_level;
4232 LevelInfoType *header = &level_sp->header;
4233 boolean num_invalid_elements = 0;
4236 level->fieldx = level_sp->width;
4237 level->fieldy = level_sp->height;
4239 for (x = 0; x < level->fieldx; x++)
4241 for (y = 0; y < level->fieldy; y++)
4243 int element_old = level_sp->playfield[x][y];
4244 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4246 if (element_new == EL_UNKNOWN)
4248 num_invalid_elements++;
4250 Debug("level:native:SP", "invalid element %d at position %d, %d",
4254 level->field[x][y] = element_new;
4258 if (num_invalid_elements > 0)
4259 Warn("found %d invalid elements%s", num_invalid_elements,
4260 (!options.debug ? " (use '--debug' for more details)" : ""));
4262 for (i = 0; i < MAX_PLAYERS; i++)
4263 level->initial_player_gravity[i] =
4264 (header->InitialGravity == 1 ? TRUE : FALSE);
4266 // skip leading spaces
4267 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4268 if (header->LevelTitle[i] != ' ')
4272 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4273 level->name[j] = header->LevelTitle[i];
4274 level->name[j] = '\0';
4276 // cut trailing spaces
4278 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4279 level->name[j - 1] = '\0';
4281 level->gems_needed = header->InfotronsNeeded;
4283 for (i = 0; i < header->SpecialPortCount; i++)
4285 SpecialPortType *port = &header->SpecialPort[i];
4286 int port_location = port->PortLocation;
4287 int gravity = port->Gravity;
4288 int port_x, port_y, port_element;
4290 port_x = (port_location / 2) % level->fieldx;
4291 port_y = (port_location / 2) / level->fieldx;
4293 if (port_x < 0 || port_x >= level->fieldx ||
4294 port_y < 0 || port_y >= level->fieldy)
4296 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4301 port_element = level->field[port_x][port_y];
4303 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4304 port_element > EL_SP_GRAVITY_PORT_UP)
4306 Warn("no special port at position (%d, %d)", port_x, port_y);
4311 // change previous (wrong) gravity inverting special port to either
4312 // gravity enabling special port or gravity disabling special port
4313 level->field[port_x][port_y] +=
4314 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4315 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4318 // change special gravity ports without database entries to normal ports
4319 for (x = 0; x < level->fieldx; x++)
4320 for (y = 0; y < level->fieldy; y++)
4321 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4322 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4323 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4325 level->time = 0; // no time limit
4326 level->amoeba_speed = 0;
4327 level->time_magic_wall = 0;
4328 level->time_wheel = 0;
4329 level->amoeba_content = EL_EMPTY;
4331 // original Supaplex does not use score values -- rate by playing time
4332 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4333 level->score[i] = 0;
4335 level->rate_time_over_score = TRUE;
4337 // there are no yamyams in supaplex levels
4338 for (i = 0; i < level->num_yamyam_contents; i++)
4339 for (x = 0; x < 3; x++)
4340 for (y = 0; y < 3; y++)
4341 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4344 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4346 struct LevelInfo_SP *level_sp = level->native_sp_level;
4347 struct DemoInfo_SP *demo = &level_sp->demo;
4350 // always start with reliable default values
4351 demo->is_available = FALSE;
4354 if (TAPE_IS_EMPTY(tape))
4357 demo->level_nr = tape.level_nr; // (currently not used)
4359 level_sp->header.DemoRandomSeed = tape.random_seed;
4363 for (i = 0; i < tape.length; i++)
4365 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4366 int demo_repeat = tape.pos[i].delay;
4367 int demo_entries = (demo_repeat + 15) / 16;
4369 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4371 Warn("tape truncated: size exceeds maximum SP demo size %d",
4377 for (j = 0; j < demo_repeat / 16; j++)
4378 demo->data[demo->length++] = 0xf0 | demo_action;
4380 if (demo_repeat % 16)
4381 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4384 demo->is_available = TRUE;
4387 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4389 struct LevelInfo_SP *level_sp = level->native_sp_level;
4390 struct DemoInfo_SP *demo = &level_sp->demo;
4391 char *filename = level->file_info.filename;
4394 // always start with reliable default values
4395 setTapeInfoToDefaults();
4397 if (!demo->is_available)
4400 tape.level_nr = demo->level_nr; // (currently not used)
4401 tape.random_seed = level_sp->header.DemoRandomSeed;
4403 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4406 tape.pos[tape.counter].delay = 0;
4408 for (i = 0; i < demo->length; i++)
4410 int demo_action = demo->data[i] & 0x0f;
4411 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4412 int tape_action = map_key_SP_to_RND(demo_action);
4413 int tape_repeat = demo_repeat + 1;
4414 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4415 boolean success = 0;
4418 for (j = 0; j < tape_repeat; j++)
4419 success = TapeAddAction(action);
4423 Warn("SP demo truncated: size exceeds maximum tape size %d",
4430 TapeHaltRecording();
4434 // ----------------------------------------------------------------------------
4435 // functions for loading MM level
4436 // ----------------------------------------------------------------------------
4438 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4440 struct LevelInfo_MM *level_mm = level->native_mm_level;
4443 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4444 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4446 level_mm->time = level->time;
4447 level_mm->kettles_needed = level->gems_needed;
4448 level_mm->auto_count_kettles = level->auto_count_gems;
4450 level_mm->mm_laser_red = level->mm_laser_red;
4451 level_mm->mm_laser_green = level->mm_laser_green;
4452 level_mm->mm_laser_blue = level->mm_laser_blue;
4454 level_mm->df_laser_red = level->df_laser_red;
4455 level_mm->df_laser_green = level->df_laser_green;
4456 level_mm->df_laser_blue = level->df_laser_blue;
4458 strcpy(level_mm->name, level->name);
4459 strcpy(level_mm->author, level->author);
4461 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4462 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4463 level_mm->score[SC_KEY] = level->score[SC_KEY];
4464 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4465 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4467 level_mm->amoeba_speed = level->amoeba_speed;
4468 level_mm->time_fuse = level->mm_time_fuse;
4469 level_mm->time_bomb = level->mm_time_bomb;
4470 level_mm->time_ball = level->mm_time_ball;
4471 level_mm->time_block = level->mm_time_block;
4473 level_mm->num_ball_contents = level->num_mm_ball_contents;
4474 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4475 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4476 level_mm->explode_ball = level->explode_mm_ball;
4478 for (i = 0; i < level->num_mm_ball_contents; i++)
4479 level_mm->ball_content[i] =
4480 map_element_RND_to_MM(level->mm_ball_content[i]);
4482 for (x = 0; x < level->fieldx; x++)
4483 for (y = 0; y < level->fieldy; y++)
4485 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4488 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4490 struct LevelInfo_MM *level_mm = level->native_mm_level;
4493 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4494 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4496 level->time = level_mm->time;
4497 level->gems_needed = level_mm->kettles_needed;
4498 level->auto_count_gems = level_mm->auto_count_kettles;
4500 level->mm_laser_red = level_mm->mm_laser_red;
4501 level->mm_laser_green = level_mm->mm_laser_green;
4502 level->mm_laser_blue = level_mm->mm_laser_blue;
4504 level->df_laser_red = level_mm->df_laser_red;
4505 level->df_laser_green = level_mm->df_laser_green;
4506 level->df_laser_blue = level_mm->df_laser_blue;
4508 strcpy(level->name, level_mm->name);
4510 // only overwrite author from 'levelinfo.conf' if author defined in level
4511 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4512 strcpy(level->author, level_mm->author);
4514 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4515 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4516 level->score[SC_KEY] = level_mm->score[SC_KEY];
4517 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4518 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4520 level->amoeba_speed = level_mm->amoeba_speed;
4521 level->mm_time_fuse = level_mm->time_fuse;
4522 level->mm_time_bomb = level_mm->time_bomb;
4523 level->mm_time_ball = level_mm->time_ball;
4524 level->mm_time_block = level_mm->time_block;
4526 level->num_mm_ball_contents = level_mm->num_ball_contents;
4527 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4528 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4529 level->explode_mm_ball = level_mm->explode_ball;
4531 for (i = 0; i < level->num_mm_ball_contents; i++)
4532 level->mm_ball_content[i] =
4533 map_element_MM_to_RND(level_mm->ball_content[i]);
4535 for (x = 0; x < level->fieldx; x++)
4536 for (y = 0; y < level->fieldy; y++)
4537 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4541 // ----------------------------------------------------------------------------
4542 // functions for loading DC level
4543 // ----------------------------------------------------------------------------
4545 #define DC_LEVEL_HEADER_SIZE 344
4547 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4550 static int last_data_encoded;
4554 int diff_hi, diff_lo;
4555 int data_hi, data_lo;
4556 unsigned short data_decoded;
4560 last_data_encoded = 0;
4567 diff = data_encoded - last_data_encoded;
4568 diff_hi = diff & ~0xff;
4569 diff_lo = diff & 0xff;
4573 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4574 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4575 data_hi = data_hi & 0xff00;
4577 data_decoded = data_hi | data_lo;
4579 last_data_encoded = data_encoded;
4581 offset1 = (offset1 + 1) % 31;
4582 offset2 = offset2 & 0xff;
4584 return data_decoded;
4587 static int getMappedElement_DC(int element)
4595 // 0x0117 - 0x036e: (?)
4598 // 0x042d - 0x0684: (?)
4614 element = EL_CRYSTAL;
4617 case 0x0e77: // quicksand (boulder)
4618 element = EL_QUICKSAND_FAST_FULL;
4621 case 0x0e99: // slow quicksand (boulder)
4622 element = EL_QUICKSAND_FULL;
4626 element = EL_EM_EXIT_OPEN;
4630 element = EL_EM_EXIT_CLOSED;
4634 element = EL_EM_STEEL_EXIT_OPEN;
4638 element = EL_EM_STEEL_EXIT_CLOSED;
4641 case 0x0f4f: // dynamite (lit 1)
4642 element = EL_EM_DYNAMITE_ACTIVE;
4645 case 0x0f57: // dynamite (lit 2)
4646 element = EL_EM_DYNAMITE_ACTIVE;
4649 case 0x0f5f: // dynamite (lit 3)
4650 element = EL_EM_DYNAMITE_ACTIVE;
4653 case 0x0f67: // dynamite (lit 4)
4654 element = EL_EM_DYNAMITE_ACTIVE;
4661 element = EL_AMOEBA_WET;
4665 element = EL_AMOEBA_DROP;
4669 element = EL_DC_MAGIC_WALL;
4673 element = EL_SPACESHIP_UP;
4677 element = EL_SPACESHIP_DOWN;
4681 element = EL_SPACESHIP_LEFT;
4685 element = EL_SPACESHIP_RIGHT;
4689 element = EL_BUG_UP;
4693 element = EL_BUG_DOWN;
4697 element = EL_BUG_LEFT;
4701 element = EL_BUG_RIGHT;
4705 element = EL_MOLE_UP;
4709 element = EL_MOLE_DOWN;
4713 element = EL_MOLE_LEFT;
4717 element = EL_MOLE_RIGHT;
4725 element = EL_YAMYAM_UP;
4729 element = EL_SWITCHGATE_OPEN;
4733 element = EL_SWITCHGATE_CLOSED;
4737 element = EL_DC_SWITCHGATE_SWITCH_UP;
4741 element = EL_TIMEGATE_CLOSED;
4744 case 0x144c: // conveyor belt switch (green)
4745 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4748 case 0x144f: // conveyor belt switch (red)
4749 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4752 case 0x1452: // conveyor belt switch (blue)
4753 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4757 element = EL_CONVEYOR_BELT_3_MIDDLE;
4761 element = EL_CONVEYOR_BELT_3_LEFT;
4765 element = EL_CONVEYOR_BELT_3_RIGHT;
4769 element = EL_CONVEYOR_BELT_1_MIDDLE;
4773 element = EL_CONVEYOR_BELT_1_LEFT;
4777 element = EL_CONVEYOR_BELT_1_RIGHT;
4781 element = EL_CONVEYOR_BELT_4_MIDDLE;
4785 element = EL_CONVEYOR_BELT_4_LEFT;
4789 element = EL_CONVEYOR_BELT_4_RIGHT;
4793 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4797 element = EL_EXPANDABLE_WALL_VERTICAL;
4801 element = EL_EXPANDABLE_WALL_ANY;
4804 case 0x14ce: // growing steel wall (left/right)
4805 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4808 case 0x14df: // growing steel wall (up/down)
4809 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4812 case 0x14e8: // growing steel wall (up/down/left/right)
4813 element = EL_EXPANDABLE_STEELWALL_ANY;
4817 element = EL_SHIELD_DEADLY;
4821 element = EL_EXTRA_TIME;
4829 element = EL_EMPTY_SPACE;
4832 case 0x1578: // quicksand (empty)
4833 element = EL_QUICKSAND_FAST_EMPTY;
4836 case 0x1579: // slow quicksand (empty)
4837 element = EL_QUICKSAND_EMPTY;
4847 element = EL_EM_DYNAMITE;
4850 case 0x15a1: // key (red)
4851 element = EL_EM_KEY_1;
4854 case 0x15a2: // key (yellow)
4855 element = EL_EM_KEY_2;
4858 case 0x15a3: // key (blue)
4859 element = EL_EM_KEY_4;
4862 case 0x15a4: // key (green)
4863 element = EL_EM_KEY_3;
4866 case 0x15a5: // key (white)
4867 element = EL_DC_KEY_WHITE;
4871 element = EL_WALL_SLIPPERY;
4878 case 0x15a8: // wall (not round)
4882 case 0x15a9: // (blue)
4883 element = EL_CHAR_A;
4886 case 0x15aa: // (blue)
4887 element = EL_CHAR_B;
4890 case 0x15ab: // (blue)
4891 element = EL_CHAR_C;
4894 case 0x15ac: // (blue)
4895 element = EL_CHAR_D;
4898 case 0x15ad: // (blue)
4899 element = EL_CHAR_E;
4902 case 0x15ae: // (blue)
4903 element = EL_CHAR_F;
4906 case 0x15af: // (blue)
4907 element = EL_CHAR_G;
4910 case 0x15b0: // (blue)
4911 element = EL_CHAR_H;
4914 case 0x15b1: // (blue)
4915 element = EL_CHAR_I;
4918 case 0x15b2: // (blue)
4919 element = EL_CHAR_J;
4922 case 0x15b3: // (blue)
4923 element = EL_CHAR_K;
4926 case 0x15b4: // (blue)
4927 element = EL_CHAR_L;
4930 case 0x15b5: // (blue)
4931 element = EL_CHAR_M;
4934 case 0x15b6: // (blue)
4935 element = EL_CHAR_N;
4938 case 0x15b7: // (blue)
4939 element = EL_CHAR_O;
4942 case 0x15b8: // (blue)
4943 element = EL_CHAR_P;
4946 case 0x15b9: // (blue)
4947 element = EL_CHAR_Q;
4950 case 0x15ba: // (blue)
4951 element = EL_CHAR_R;
4954 case 0x15bb: // (blue)
4955 element = EL_CHAR_S;
4958 case 0x15bc: // (blue)
4959 element = EL_CHAR_T;
4962 case 0x15bd: // (blue)
4963 element = EL_CHAR_U;
4966 case 0x15be: // (blue)
4967 element = EL_CHAR_V;
4970 case 0x15bf: // (blue)
4971 element = EL_CHAR_W;
4974 case 0x15c0: // (blue)
4975 element = EL_CHAR_X;
4978 case 0x15c1: // (blue)
4979 element = EL_CHAR_Y;
4982 case 0x15c2: // (blue)
4983 element = EL_CHAR_Z;
4986 case 0x15c3: // (blue)
4987 element = EL_CHAR_AUMLAUT;
4990 case 0x15c4: // (blue)
4991 element = EL_CHAR_OUMLAUT;
4994 case 0x15c5: // (blue)
4995 element = EL_CHAR_UUMLAUT;
4998 case 0x15c6: // (blue)
4999 element = EL_CHAR_0;
5002 case 0x15c7: // (blue)
5003 element = EL_CHAR_1;
5006 case 0x15c8: // (blue)
5007 element = EL_CHAR_2;
5010 case 0x15c9: // (blue)
5011 element = EL_CHAR_3;
5014 case 0x15ca: // (blue)
5015 element = EL_CHAR_4;
5018 case 0x15cb: // (blue)
5019 element = EL_CHAR_5;
5022 case 0x15cc: // (blue)
5023 element = EL_CHAR_6;
5026 case 0x15cd: // (blue)
5027 element = EL_CHAR_7;
5030 case 0x15ce: // (blue)
5031 element = EL_CHAR_8;
5034 case 0x15cf: // (blue)
5035 element = EL_CHAR_9;
5038 case 0x15d0: // (blue)
5039 element = EL_CHAR_PERIOD;
5042 case 0x15d1: // (blue)
5043 element = EL_CHAR_EXCLAM;
5046 case 0x15d2: // (blue)
5047 element = EL_CHAR_COLON;
5050 case 0x15d3: // (blue)
5051 element = EL_CHAR_LESS;
5054 case 0x15d4: // (blue)
5055 element = EL_CHAR_GREATER;
5058 case 0x15d5: // (blue)
5059 element = EL_CHAR_QUESTION;
5062 case 0x15d6: // (blue)
5063 element = EL_CHAR_COPYRIGHT;
5066 case 0x15d7: // (blue)
5067 element = EL_CHAR_UP;
5070 case 0x15d8: // (blue)
5071 element = EL_CHAR_DOWN;
5074 case 0x15d9: // (blue)
5075 element = EL_CHAR_BUTTON;
5078 case 0x15da: // (blue)
5079 element = EL_CHAR_PLUS;
5082 case 0x15db: // (blue)
5083 element = EL_CHAR_MINUS;
5086 case 0x15dc: // (blue)
5087 element = EL_CHAR_APOSTROPHE;
5090 case 0x15dd: // (blue)
5091 element = EL_CHAR_PARENLEFT;
5094 case 0x15de: // (blue)
5095 element = EL_CHAR_PARENRIGHT;
5098 case 0x15df: // (green)
5099 element = EL_CHAR_A;
5102 case 0x15e0: // (green)
5103 element = EL_CHAR_B;
5106 case 0x15e1: // (green)
5107 element = EL_CHAR_C;
5110 case 0x15e2: // (green)
5111 element = EL_CHAR_D;
5114 case 0x15e3: // (green)
5115 element = EL_CHAR_E;
5118 case 0x15e4: // (green)
5119 element = EL_CHAR_F;
5122 case 0x15e5: // (green)
5123 element = EL_CHAR_G;
5126 case 0x15e6: // (green)
5127 element = EL_CHAR_H;
5130 case 0x15e7: // (green)
5131 element = EL_CHAR_I;
5134 case 0x15e8: // (green)
5135 element = EL_CHAR_J;
5138 case 0x15e9: // (green)
5139 element = EL_CHAR_K;
5142 case 0x15ea: // (green)
5143 element = EL_CHAR_L;
5146 case 0x15eb: // (green)
5147 element = EL_CHAR_M;
5150 case 0x15ec: // (green)
5151 element = EL_CHAR_N;
5154 case 0x15ed: // (green)
5155 element = EL_CHAR_O;
5158 case 0x15ee: // (green)
5159 element = EL_CHAR_P;
5162 case 0x15ef: // (green)
5163 element = EL_CHAR_Q;
5166 case 0x15f0: // (green)
5167 element = EL_CHAR_R;
5170 case 0x15f1: // (green)
5171 element = EL_CHAR_S;
5174 case 0x15f2: // (green)
5175 element = EL_CHAR_T;
5178 case 0x15f3: // (green)
5179 element = EL_CHAR_U;
5182 case 0x15f4: // (green)
5183 element = EL_CHAR_V;
5186 case 0x15f5: // (green)
5187 element = EL_CHAR_W;
5190 case 0x15f6: // (green)
5191 element = EL_CHAR_X;
5194 case 0x15f7: // (green)
5195 element = EL_CHAR_Y;
5198 case 0x15f8: // (green)
5199 element = EL_CHAR_Z;
5202 case 0x15f9: // (green)
5203 element = EL_CHAR_AUMLAUT;
5206 case 0x15fa: // (green)
5207 element = EL_CHAR_OUMLAUT;
5210 case 0x15fb: // (green)
5211 element = EL_CHAR_UUMLAUT;
5214 case 0x15fc: // (green)
5215 element = EL_CHAR_0;
5218 case 0x15fd: // (green)
5219 element = EL_CHAR_1;
5222 case 0x15fe: // (green)
5223 element = EL_CHAR_2;
5226 case 0x15ff: // (green)
5227 element = EL_CHAR_3;
5230 case 0x1600: // (green)
5231 element = EL_CHAR_4;
5234 case 0x1601: // (green)
5235 element = EL_CHAR_5;
5238 case 0x1602: // (green)
5239 element = EL_CHAR_6;
5242 case 0x1603: // (green)
5243 element = EL_CHAR_7;
5246 case 0x1604: // (green)
5247 element = EL_CHAR_8;
5250 case 0x1605: // (green)
5251 element = EL_CHAR_9;
5254 case 0x1606: // (green)
5255 element = EL_CHAR_PERIOD;
5258 case 0x1607: // (green)
5259 element = EL_CHAR_EXCLAM;
5262 case 0x1608: // (green)
5263 element = EL_CHAR_COLON;
5266 case 0x1609: // (green)
5267 element = EL_CHAR_LESS;
5270 case 0x160a: // (green)
5271 element = EL_CHAR_GREATER;
5274 case 0x160b: // (green)
5275 element = EL_CHAR_QUESTION;
5278 case 0x160c: // (green)
5279 element = EL_CHAR_COPYRIGHT;
5282 case 0x160d: // (green)
5283 element = EL_CHAR_UP;
5286 case 0x160e: // (green)
5287 element = EL_CHAR_DOWN;
5290 case 0x160f: // (green)
5291 element = EL_CHAR_BUTTON;
5294 case 0x1610: // (green)
5295 element = EL_CHAR_PLUS;
5298 case 0x1611: // (green)
5299 element = EL_CHAR_MINUS;
5302 case 0x1612: // (green)
5303 element = EL_CHAR_APOSTROPHE;
5306 case 0x1613: // (green)
5307 element = EL_CHAR_PARENLEFT;
5310 case 0x1614: // (green)
5311 element = EL_CHAR_PARENRIGHT;
5314 case 0x1615: // (blue steel)
5315 element = EL_STEEL_CHAR_A;
5318 case 0x1616: // (blue steel)
5319 element = EL_STEEL_CHAR_B;
5322 case 0x1617: // (blue steel)
5323 element = EL_STEEL_CHAR_C;
5326 case 0x1618: // (blue steel)
5327 element = EL_STEEL_CHAR_D;
5330 case 0x1619: // (blue steel)
5331 element = EL_STEEL_CHAR_E;
5334 case 0x161a: // (blue steel)
5335 element = EL_STEEL_CHAR_F;
5338 case 0x161b: // (blue steel)
5339 element = EL_STEEL_CHAR_G;
5342 case 0x161c: // (blue steel)
5343 element = EL_STEEL_CHAR_H;
5346 case 0x161d: // (blue steel)
5347 element = EL_STEEL_CHAR_I;
5350 case 0x161e: // (blue steel)
5351 element = EL_STEEL_CHAR_J;
5354 case 0x161f: // (blue steel)
5355 element = EL_STEEL_CHAR_K;
5358 case 0x1620: // (blue steel)
5359 element = EL_STEEL_CHAR_L;
5362 case 0x1621: // (blue steel)
5363 element = EL_STEEL_CHAR_M;
5366 case 0x1622: // (blue steel)
5367 element = EL_STEEL_CHAR_N;
5370 case 0x1623: // (blue steel)
5371 element = EL_STEEL_CHAR_O;
5374 case 0x1624: // (blue steel)
5375 element = EL_STEEL_CHAR_P;
5378 case 0x1625: // (blue steel)
5379 element = EL_STEEL_CHAR_Q;
5382 case 0x1626: // (blue steel)
5383 element = EL_STEEL_CHAR_R;
5386 case 0x1627: // (blue steel)
5387 element = EL_STEEL_CHAR_S;
5390 case 0x1628: // (blue steel)
5391 element = EL_STEEL_CHAR_T;
5394 case 0x1629: // (blue steel)
5395 element = EL_STEEL_CHAR_U;
5398 case 0x162a: // (blue steel)
5399 element = EL_STEEL_CHAR_V;
5402 case 0x162b: // (blue steel)
5403 element = EL_STEEL_CHAR_W;
5406 case 0x162c: // (blue steel)
5407 element = EL_STEEL_CHAR_X;
5410 case 0x162d: // (blue steel)
5411 element = EL_STEEL_CHAR_Y;
5414 case 0x162e: // (blue steel)
5415 element = EL_STEEL_CHAR_Z;
5418 case 0x162f: // (blue steel)
5419 element = EL_STEEL_CHAR_AUMLAUT;
5422 case 0x1630: // (blue steel)
5423 element = EL_STEEL_CHAR_OUMLAUT;
5426 case 0x1631: // (blue steel)
5427 element = EL_STEEL_CHAR_UUMLAUT;
5430 case 0x1632: // (blue steel)
5431 element = EL_STEEL_CHAR_0;
5434 case 0x1633: // (blue steel)
5435 element = EL_STEEL_CHAR_1;
5438 case 0x1634: // (blue steel)
5439 element = EL_STEEL_CHAR_2;
5442 case 0x1635: // (blue steel)
5443 element = EL_STEEL_CHAR_3;
5446 case 0x1636: // (blue steel)
5447 element = EL_STEEL_CHAR_4;
5450 case 0x1637: // (blue steel)
5451 element = EL_STEEL_CHAR_5;
5454 case 0x1638: // (blue steel)
5455 element = EL_STEEL_CHAR_6;
5458 case 0x1639: // (blue steel)
5459 element = EL_STEEL_CHAR_7;
5462 case 0x163a: // (blue steel)
5463 element = EL_STEEL_CHAR_8;
5466 case 0x163b: // (blue steel)
5467 element = EL_STEEL_CHAR_9;
5470 case 0x163c: // (blue steel)
5471 element = EL_STEEL_CHAR_PERIOD;
5474 case 0x163d: // (blue steel)
5475 element = EL_STEEL_CHAR_EXCLAM;
5478 case 0x163e: // (blue steel)
5479 element = EL_STEEL_CHAR_COLON;
5482 case 0x163f: // (blue steel)
5483 element = EL_STEEL_CHAR_LESS;
5486 case 0x1640: // (blue steel)
5487 element = EL_STEEL_CHAR_GREATER;
5490 case 0x1641: // (blue steel)
5491 element = EL_STEEL_CHAR_QUESTION;
5494 case 0x1642: // (blue steel)
5495 element = EL_STEEL_CHAR_COPYRIGHT;
5498 case 0x1643: // (blue steel)
5499 element = EL_STEEL_CHAR_UP;
5502 case 0x1644: // (blue steel)
5503 element = EL_STEEL_CHAR_DOWN;
5506 case 0x1645: // (blue steel)
5507 element = EL_STEEL_CHAR_BUTTON;
5510 case 0x1646: // (blue steel)
5511 element = EL_STEEL_CHAR_PLUS;
5514 case 0x1647: // (blue steel)
5515 element = EL_STEEL_CHAR_MINUS;
5518 case 0x1648: // (blue steel)
5519 element = EL_STEEL_CHAR_APOSTROPHE;
5522 case 0x1649: // (blue steel)
5523 element = EL_STEEL_CHAR_PARENLEFT;
5526 case 0x164a: // (blue steel)
5527 element = EL_STEEL_CHAR_PARENRIGHT;
5530 case 0x164b: // (green steel)
5531 element = EL_STEEL_CHAR_A;
5534 case 0x164c: // (green steel)
5535 element = EL_STEEL_CHAR_B;
5538 case 0x164d: // (green steel)
5539 element = EL_STEEL_CHAR_C;
5542 case 0x164e: // (green steel)
5543 element = EL_STEEL_CHAR_D;
5546 case 0x164f: // (green steel)
5547 element = EL_STEEL_CHAR_E;
5550 case 0x1650: // (green steel)
5551 element = EL_STEEL_CHAR_F;
5554 case 0x1651: // (green steel)
5555 element = EL_STEEL_CHAR_G;
5558 case 0x1652: // (green steel)
5559 element = EL_STEEL_CHAR_H;
5562 case 0x1653: // (green steel)
5563 element = EL_STEEL_CHAR_I;
5566 case 0x1654: // (green steel)
5567 element = EL_STEEL_CHAR_J;
5570 case 0x1655: // (green steel)
5571 element = EL_STEEL_CHAR_K;
5574 case 0x1656: // (green steel)
5575 element = EL_STEEL_CHAR_L;
5578 case 0x1657: // (green steel)
5579 element = EL_STEEL_CHAR_M;
5582 case 0x1658: // (green steel)
5583 element = EL_STEEL_CHAR_N;
5586 case 0x1659: // (green steel)
5587 element = EL_STEEL_CHAR_O;
5590 case 0x165a: // (green steel)
5591 element = EL_STEEL_CHAR_P;
5594 case 0x165b: // (green steel)
5595 element = EL_STEEL_CHAR_Q;
5598 case 0x165c: // (green steel)
5599 element = EL_STEEL_CHAR_R;
5602 case 0x165d: // (green steel)
5603 element = EL_STEEL_CHAR_S;
5606 case 0x165e: // (green steel)
5607 element = EL_STEEL_CHAR_T;
5610 case 0x165f: // (green steel)
5611 element = EL_STEEL_CHAR_U;
5614 case 0x1660: // (green steel)
5615 element = EL_STEEL_CHAR_V;
5618 case 0x1661: // (green steel)
5619 element = EL_STEEL_CHAR_W;
5622 case 0x1662: // (green steel)
5623 element = EL_STEEL_CHAR_X;
5626 case 0x1663: // (green steel)
5627 element = EL_STEEL_CHAR_Y;
5630 case 0x1664: // (green steel)
5631 element = EL_STEEL_CHAR_Z;
5634 case 0x1665: // (green steel)
5635 element = EL_STEEL_CHAR_AUMLAUT;
5638 case 0x1666: // (green steel)
5639 element = EL_STEEL_CHAR_OUMLAUT;
5642 case 0x1667: // (green steel)
5643 element = EL_STEEL_CHAR_UUMLAUT;
5646 case 0x1668: // (green steel)
5647 element = EL_STEEL_CHAR_0;
5650 case 0x1669: // (green steel)
5651 element = EL_STEEL_CHAR_1;
5654 case 0x166a: // (green steel)
5655 element = EL_STEEL_CHAR_2;
5658 case 0x166b: // (green steel)
5659 element = EL_STEEL_CHAR_3;
5662 case 0x166c: // (green steel)
5663 element = EL_STEEL_CHAR_4;
5666 case 0x166d: // (green steel)
5667 element = EL_STEEL_CHAR_5;
5670 case 0x166e: // (green steel)
5671 element = EL_STEEL_CHAR_6;
5674 case 0x166f: // (green steel)
5675 element = EL_STEEL_CHAR_7;
5678 case 0x1670: // (green steel)
5679 element = EL_STEEL_CHAR_8;
5682 case 0x1671: // (green steel)
5683 element = EL_STEEL_CHAR_9;
5686 case 0x1672: // (green steel)
5687 element = EL_STEEL_CHAR_PERIOD;
5690 case 0x1673: // (green steel)
5691 element = EL_STEEL_CHAR_EXCLAM;
5694 case 0x1674: // (green steel)
5695 element = EL_STEEL_CHAR_COLON;
5698 case 0x1675: // (green steel)
5699 element = EL_STEEL_CHAR_LESS;
5702 case 0x1676: // (green steel)
5703 element = EL_STEEL_CHAR_GREATER;
5706 case 0x1677: // (green steel)
5707 element = EL_STEEL_CHAR_QUESTION;
5710 case 0x1678: // (green steel)
5711 element = EL_STEEL_CHAR_COPYRIGHT;
5714 case 0x1679: // (green steel)
5715 element = EL_STEEL_CHAR_UP;
5718 case 0x167a: // (green steel)
5719 element = EL_STEEL_CHAR_DOWN;
5722 case 0x167b: // (green steel)
5723 element = EL_STEEL_CHAR_BUTTON;
5726 case 0x167c: // (green steel)
5727 element = EL_STEEL_CHAR_PLUS;
5730 case 0x167d: // (green steel)
5731 element = EL_STEEL_CHAR_MINUS;
5734 case 0x167e: // (green steel)
5735 element = EL_STEEL_CHAR_APOSTROPHE;
5738 case 0x167f: // (green steel)
5739 element = EL_STEEL_CHAR_PARENLEFT;
5742 case 0x1680: // (green steel)
5743 element = EL_STEEL_CHAR_PARENRIGHT;
5746 case 0x1681: // gate (red)
5747 element = EL_EM_GATE_1;
5750 case 0x1682: // secret gate (red)
5751 element = EL_EM_GATE_1_GRAY;
5754 case 0x1683: // gate (yellow)
5755 element = EL_EM_GATE_2;
5758 case 0x1684: // secret gate (yellow)
5759 element = EL_EM_GATE_2_GRAY;
5762 case 0x1685: // gate (blue)
5763 element = EL_EM_GATE_4;
5766 case 0x1686: // secret gate (blue)
5767 element = EL_EM_GATE_4_GRAY;
5770 case 0x1687: // gate (green)
5771 element = EL_EM_GATE_3;
5774 case 0x1688: // secret gate (green)
5775 element = EL_EM_GATE_3_GRAY;
5778 case 0x1689: // gate (white)
5779 element = EL_DC_GATE_WHITE;
5782 case 0x168a: // secret gate (white)
5783 element = EL_DC_GATE_WHITE_GRAY;
5786 case 0x168b: // secret gate (no key)
5787 element = EL_DC_GATE_FAKE_GRAY;
5791 element = EL_ROBOT_WHEEL;
5795 element = EL_DC_TIMEGATE_SWITCH;
5799 element = EL_ACID_POOL_BOTTOM;
5803 element = EL_ACID_POOL_TOPLEFT;
5807 element = EL_ACID_POOL_TOPRIGHT;
5811 element = EL_ACID_POOL_BOTTOMLEFT;
5815 element = EL_ACID_POOL_BOTTOMRIGHT;
5819 element = EL_STEELWALL;
5823 element = EL_STEELWALL_SLIPPERY;
5826 case 0x1695: // steel wall (not round)
5827 element = EL_STEELWALL;
5830 case 0x1696: // steel wall (left)
5831 element = EL_DC_STEELWALL_1_LEFT;
5834 case 0x1697: // steel wall (bottom)
5835 element = EL_DC_STEELWALL_1_BOTTOM;
5838 case 0x1698: // steel wall (right)
5839 element = EL_DC_STEELWALL_1_RIGHT;
5842 case 0x1699: // steel wall (top)
5843 element = EL_DC_STEELWALL_1_TOP;
5846 case 0x169a: // steel wall (left/bottom)
5847 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5850 case 0x169b: // steel wall (right/bottom)
5851 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5854 case 0x169c: // steel wall (right/top)
5855 element = EL_DC_STEELWALL_1_TOPRIGHT;
5858 case 0x169d: // steel wall (left/top)
5859 element = EL_DC_STEELWALL_1_TOPLEFT;
5862 case 0x169e: // steel wall (right/bottom small)
5863 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5866 case 0x169f: // steel wall (left/bottom small)
5867 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5870 case 0x16a0: // steel wall (right/top small)
5871 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5874 case 0x16a1: // steel wall (left/top small)
5875 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5878 case 0x16a2: // steel wall (left/right)
5879 element = EL_DC_STEELWALL_1_VERTICAL;
5882 case 0x16a3: // steel wall (top/bottom)
5883 element = EL_DC_STEELWALL_1_HORIZONTAL;
5886 case 0x16a4: // steel wall 2 (left end)
5887 element = EL_DC_STEELWALL_2_LEFT;
5890 case 0x16a5: // steel wall 2 (right end)
5891 element = EL_DC_STEELWALL_2_RIGHT;
5894 case 0x16a6: // steel wall 2 (top end)
5895 element = EL_DC_STEELWALL_2_TOP;
5898 case 0x16a7: // steel wall 2 (bottom end)
5899 element = EL_DC_STEELWALL_2_BOTTOM;
5902 case 0x16a8: // steel wall 2 (left/right)
5903 element = EL_DC_STEELWALL_2_HORIZONTAL;
5906 case 0x16a9: // steel wall 2 (up/down)
5907 element = EL_DC_STEELWALL_2_VERTICAL;
5910 case 0x16aa: // steel wall 2 (mid)
5911 element = EL_DC_STEELWALL_2_MIDDLE;
5915 element = EL_SIGN_EXCLAMATION;
5919 element = EL_SIGN_RADIOACTIVITY;
5923 element = EL_SIGN_STOP;
5927 element = EL_SIGN_WHEELCHAIR;
5931 element = EL_SIGN_PARKING;
5935 element = EL_SIGN_NO_ENTRY;
5939 element = EL_SIGN_HEART;
5943 element = EL_SIGN_GIVE_WAY;
5947 element = EL_SIGN_ENTRY_FORBIDDEN;
5951 element = EL_SIGN_EMERGENCY_EXIT;
5955 element = EL_SIGN_YIN_YANG;
5959 element = EL_WALL_EMERALD;
5963 element = EL_WALL_DIAMOND;
5967 element = EL_WALL_PEARL;
5971 element = EL_WALL_CRYSTAL;
5975 element = EL_INVISIBLE_WALL;
5979 element = EL_INVISIBLE_STEELWALL;
5983 // EL_INVISIBLE_SAND
5986 element = EL_LIGHT_SWITCH;
5990 element = EL_ENVELOPE_1;
5994 if (element >= 0x0117 && element <= 0x036e) // (?)
5995 element = EL_DIAMOND;
5996 else if (element >= 0x042d && element <= 0x0684) // (?)
5997 element = EL_EMERALD;
5998 else if (element >= 0x157c && element <= 0x158b)
6000 else if (element >= 0x1590 && element <= 0x159f)
6001 element = EL_DC_LANDMINE;
6002 else if (element >= 0x16bc && element <= 0x16cb)
6003 element = EL_INVISIBLE_SAND;
6006 Warn("unknown Diamond Caves element 0x%04x", element);
6008 element = EL_UNKNOWN;
6013 return getMappedElement(element);
6016 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6018 byte header[DC_LEVEL_HEADER_SIZE];
6020 int envelope_header_pos = 62;
6021 int envelope_content_pos = 94;
6022 int level_name_pos = 251;
6023 int level_author_pos = 292;
6024 int envelope_header_len;
6025 int envelope_content_len;
6027 int level_author_len;
6029 int num_yamyam_contents;
6032 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6034 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6036 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6038 header[i * 2 + 0] = header_word >> 8;
6039 header[i * 2 + 1] = header_word & 0xff;
6042 // read some values from level header to check level decoding integrity
6043 fieldx = header[6] | (header[7] << 8);
6044 fieldy = header[8] | (header[9] << 8);
6045 num_yamyam_contents = header[60] | (header[61] << 8);
6047 // do some simple sanity checks to ensure that level was correctly decoded
6048 if (fieldx < 1 || fieldx > 256 ||
6049 fieldy < 1 || fieldy > 256 ||
6050 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6052 level->no_valid_file = TRUE;
6054 Warn("cannot decode level from stream -- using empty level");
6059 // maximum envelope header size is 31 bytes
6060 envelope_header_len = header[envelope_header_pos];
6061 // maximum envelope content size is 110 (156?) bytes
6062 envelope_content_len = header[envelope_content_pos];
6064 // maximum level title size is 40 bytes
6065 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6066 // maximum level author size is 30 (51?) bytes
6067 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6071 for (i = 0; i < envelope_header_len; i++)
6072 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6073 level->envelope[0].text[envelope_size++] =
6074 header[envelope_header_pos + 1 + i];
6076 if (envelope_header_len > 0 && envelope_content_len > 0)
6078 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6079 level->envelope[0].text[envelope_size++] = '\n';
6080 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6081 level->envelope[0].text[envelope_size++] = '\n';
6084 for (i = 0; i < envelope_content_len; i++)
6085 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6086 level->envelope[0].text[envelope_size++] =
6087 header[envelope_content_pos + 1 + i];
6089 level->envelope[0].text[envelope_size] = '\0';
6091 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6092 level->envelope[0].ysize = 10;
6093 level->envelope[0].autowrap = TRUE;
6094 level->envelope[0].centered = TRUE;
6096 for (i = 0; i < level_name_len; i++)
6097 level->name[i] = header[level_name_pos + 1 + i];
6098 level->name[level_name_len] = '\0';
6100 for (i = 0; i < level_author_len; i++)
6101 level->author[i] = header[level_author_pos + 1 + i];
6102 level->author[level_author_len] = '\0';
6104 num_yamyam_contents = header[60] | (header[61] << 8);
6105 level->num_yamyam_contents =
6106 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6108 for (i = 0; i < num_yamyam_contents; i++)
6110 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6112 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6113 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6115 if (i < MAX_ELEMENT_CONTENTS)
6116 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6120 fieldx = header[6] | (header[7] << 8);
6121 fieldy = header[8] | (header[9] << 8);
6122 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6123 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6125 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6127 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6128 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6130 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6131 level->field[x][y] = getMappedElement_DC(element_dc);
6134 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6135 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6136 level->field[x][y] = EL_PLAYER_1;
6138 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6139 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6140 level->field[x][y] = EL_PLAYER_2;
6142 level->gems_needed = header[18] | (header[19] << 8);
6144 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6145 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6146 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6147 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6148 level->score[SC_NUT] = header[28] | (header[29] << 8);
6149 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6150 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6151 level->score[SC_BUG] = header[34] | (header[35] << 8);
6152 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6153 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6154 level->score[SC_KEY] = header[40] | (header[41] << 8);
6155 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6157 level->time = header[44] | (header[45] << 8);
6159 level->amoeba_speed = header[46] | (header[47] << 8);
6160 level->time_light = header[48] | (header[49] << 8);
6161 level->time_timegate = header[50] | (header[51] << 8);
6162 level->time_wheel = header[52] | (header[53] << 8);
6163 level->time_magic_wall = header[54] | (header[55] << 8);
6164 level->extra_time = header[56] | (header[57] << 8);
6165 level->shield_normal_time = header[58] | (header[59] << 8);
6167 // shield and extra time elements do not have a score
6168 level->score[SC_SHIELD] = 0;
6169 level->extra_time_score = 0;
6171 // set time for normal and deadly shields to the same value
6172 level->shield_deadly_time = level->shield_normal_time;
6174 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6175 // can slip down from flat walls, like normal walls and steel walls
6176 level->em_slippery_gems = TRUE;
6178 // time score is counted for each 10 seconds left in Diamond Caves levels
6179 level->time_score_base = 10;
6182 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6183 struct LevelFileInfo *level_file_info,
6184 boolean level_info_only)
6186 char *filename = level_file_info->filename;
6188 int num_magic_bytes = 8;
6189 char magic_bytes[num_magic_bytes + 1];
6190 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6192 if (!(file = openFile(filename, MODE_READ)))
6194 level->no_valid_file = TRUE;
6196 if (!level_info_only)
6197 Warn("cannot read level '%s' -- using empty level", filename);
6202 // fseek(file, 0x0000, SEEK_SET);
6204 if (level_file_info->packed)
6206 // read "magic bytes" from start of file
6207 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6208 magic_bytes[0] = '\0';
6210 // check "magic bytes" for correct file format
6211 if (!strPrefix(magic_bytes, "DC2"))
6213 level->no_valid_file = TRUE;
6215 Warn("unknown DC level file '%s' -- using empty level", filename);
6220 if (strPrefix(magic_bytes, "DC2Win95") ||
6221 strPrefix(magic_bytes, "DC2Win98"))
6223 int position_first_level = 0x00fa;
6224 int extra_bytes = 4;
6227 // advance file stream to first level inside the level package
6228 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6230 // each block of level data is followed by block of non-level data
6231 num_levels_to_skip *= 2;
6233 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6234 while (num_levels_to_skip >= 0)
6236 // advance file stream to next level inside the level package
6237 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6239 level->no_valid_file = TRUE;
6241 Warn("cannot fseek in file '%s' -- using empty level", filename);
6246 // skip apparently unused extra bytes following each level
6247 ReadUnusedBytesFromFile(file, extra_bytes);
6249 // read size of next level in level package
6250 skip_bytes = getFile32BitLE(file);
6252 num_levels_to_skip--;
6257 level->no_valid_file = TRUE;
6259 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6265 LoadLevelFromFileStream_DC(file, level);
6271 // ----------------------------------------------------------------------------
6272 // functions for loading SB level
6273 // ----------------------------------------------------------------------------
6275 int getMappedElement_SB(int element_ascii, boolean use_ces)
6283 sb_element_mapping[] =
6285 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6286 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6287 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6288 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6289 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6290 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6291 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6292 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6299 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6300 if (element_ascii == sb_element_mapping[i].ascii)
6301 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6303 return EL_UNDEFINED;
6306 static void SetLevelSettings_SB(struct LevelInfo *level)
6310 level->use_step_counter = TRUE;
6313 level->score[SC_TIME_BONUS] = 0;
6314 level->time_score_base = 1;
6315 level->rate_time_over_score = TRUE;
6318 level->auto_exit_sokoban = TRUE;
6321 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6322 struct LevelFileInfo *level_file_info,
6323 boolean level_info_only)
6325 char *filename = level_file_info->filename;
6326 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6327 char last_comment[MAX_LINE_LEN];
6328 char level_name[MAX_LINE_LEN];
6331 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6332 boolean read_continued_line = FALSE;
6333 boolean reading_playfield = FALSE;
6334 boolean got_valid_playfield_line = FALSE;
6335 boolean invalid_playfield_char = FALSE;
6336 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6337 int file_level_nr = 0;
6338 int x = 0, y = 0; // initialized to make compilers happy
6340 last_comment[0] = '\0';
6341 level_name[0] = '\0';
6343 if (!(file = openFile(filename, MODE_READ)))
6345 level->no_valid_file = TRUE;
6347 if (!level_info_only)
6348 Warn("cannot read level '%s' -- using empty level", filename);
6353 while (!checkEndOfFile(file))
6355 // level successfully read, but next level may follow here
6356 if (!got_valid_playfield_line && reading_playfield)
6358 // read playfield from single level file -- skip remaining file
6359 if (!level_file_info->packed)
6362 if (file_level_nr >= num_levels_to_skip)
6367 last_comment[0] = '\0';
6368 level_name[0] = '\0';
6370 reading_playfield = FALSE;
6373 got_valid_playfield_line = FALSE;
6375 // read next line of input file
6376 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6379 // cut trailing line break (this can be newline and/or carriage return)
6380 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6381 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6384 // copy raw input line for later use (mainly debugging output)
6385 strcpy(line_raw, line);
6387 if (read_continued_line)
6389 // append new line to existing line, if there is enough space
6390 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6391 strcat(previous_line, line_ptr);
6393 strcpy(line, previous_line); // copy storage buffer to line
6395 read_continued_line = FALSE;
6398 // if the last character is '\', continue at next line
6399 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6401 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6402 strcpy(previous_line, line); // copy line to storage buffer
6404 read_continued_line = TRUE;
6410 if (line[0] == '\0')
6413 // extract comment text from comment line
6416 for (line_ptr = line; *line_ptr; line_ptr++)
6417 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6420 strcpy(last_comment, line_ptr);
6425 // extract level title text from line containing level title
6426 if (line[0] == '\'')
6428 strcpy(level_name, &line[1]);
6430 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6431 level_name[strlen(level_name) - 1] = '\0';
6436 // skip lines containing only spaces (or empty lines)
6437 for (line_ptr = line; *line_ptr; line_ptr++)
6438 if (*line_ptr != ' ')
6440 if (*line_ptr == '\0')
6443 // at this point, we have found a line containing part of a playfield
6445 got_valid_playfield_line = TRUE;
6447 if (!reading_playfield)
6449 reading_playfield = TRUE;
6450 invalid_playfield_char = FALSE;
6452 for (x = 0; x < MAX_LEV_FIELDX; x++)
6453 for (y = 0; y < MAX_LEV_FIELDY; y++)
6454 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6459 // start with topmost tile row
6463 // skip playfield line if larger row than allowed
6464 if (y >= MAX_LEV_FIELDY)
6467 // start with leftmost tile column
6470 // read playfield elements from line
6471 for (line_ptr = line; *line_ptr; line_ptr++)
6473 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6475 // stop parsing playfield line if larger column than allowed
6476 if (x >= MAX_LEV_FIELDX)
6479 if (mapped_sb_element == EL_UNDEFINED)
6481 invalid_playfield_char = TRUE;
6486 level->field[x][y] = mapped_sb_element;
6488 // continue with next tile column
6491 level->fieldx = MAX(x, level->fieldx);
6494 if (invalid_playfield_char)
6496 // if first playfield line, treat invalid lines as comment lines
6498 reading_playfield = FALSE;
6503 // continue with next tile row
6511 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6512 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6514 if (!reading_playfield)
6516 level->no_valid_file = TRUE;
6518 Warn("cannot read level '%s' -- using empty level", filename);
6523 if (*level_name != '\0')
6525 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6526 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6528 else if (*last_comment != '\0')
6530 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6531 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6535 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6538 // set all empty fields beyond the border walls to invisible steel wall
6539 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6541 if ((x == 0 || x == level->fieldx - 1 ||
6542 y == 0 || y == level->fieldy - 1) &&
6543 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6544 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6545 level->field, level->fieldx, level->fieldy);
6548 // set special level settings for Sokoban levels
6549 SetLevelSettings_SB(level);
6551 if (load_xsb_to_ces)
6553 // special global settings can now be set in level template
6554 level->use_custom_template = TRUE;
6559 // -------------------------------------------------------------------------
6560 // functions for handling native levels
6561 // -------------------------------------------------------------------------
6563 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6564 struct LevelFileInfo *level_file_info,
6565 boolean level_info_only)
6569 // determine position of requested level inside level package
6570 if (level_file_info->packed)
6571 pos = level_file_info->nr - leveldir_current->first_level;
6573 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6574 level->no_valid_file = TRUE;
6577 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6578 struct LevelFileInfo *level_file_info,
6579 boolean level_info_only)
6581 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6582 level->no_valid_file = TRUE;
6585 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6586 struct LevelFileInfo *level_file_info,
6587 boolean level_info_only)
6591 // determine position of requested level inside level package
6592 if (level_file_info->packed)
6593 pos = level_file_info->nr - leveldir_current->first_level;
6595 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6596 level->no_valid_file = TRUE;
6599 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6600 struct LevelFileInfo *level_file_info,
6601 boolean level_info_only)
6603 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6604 level->no_valid_file = TRUE;
6607 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6609 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6610 CopyNativeLevel_RND_to_BD(level);
6611 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6612 CopyNativeLevel_RND_to_EM(level);
6613 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6614 CopyNativeLevel_RND_to_SP(level);
6615 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6616 CopyNativeLevel_RND_to_MM(level);
6619 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6621 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6622 CopyNativeLevel_BD_to_RND(level);
6623 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6624 CopyNativeLevel_EM_to_RND(level);
6625 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6626 CopyNativeLevel_SP_to_RND(level);
6627 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6628 CopyNativeLevel_MM_to_RND(level);
6631 void SaveNativeLevel(struct LevelInfo *level)
6633 // saving native level files only supported for some game engines
6634 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6635 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6638 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6639 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6640 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6641 char *filename = getLevelFilenameFromBasename(basename);
6643 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6646 boolean success = FALSE;
6648 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6650 CopyNativeLevel_RND_to_BD(level);
6651 // CopyNativeTape_RND_to_BD(level);
6653 success = SaveNativeLevel_BD(filename);
6655 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6657 CopyNativeLevel_RND_to_SP(level);
6658 CopyNativeTape_RND_to_SP(level);
6660 success = SaveNativeLevel_SP(filename);
6664 Request("Native level file saved!", REQ_CONFIRM);
6666 Request("Failed to save native level file!", REQ_CONFIRM);
6670 // ----------------------------------------------------------------------------
6671 // functions for loading generic level
6672 // ----------------------------------------------------------------------------
6674 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6675 struct LevelFileInfo *level_file_info,
6676 boolean level_info_only)
6678 // always start with reliable default values
6679 setLevelInfoToDefaults(level, level_info_only, TRUE);
6681 switch (level_file_info->type)
6683 case LEVEL_FILE_TYPE_RND:
6684 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6687 case LEVEL_FILE_TYPE_BD:
6688 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6689 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6692 case LEVEL_FILE_TYPE_EM:
6693 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6694 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6697 case LEVEL_FILE_TYPE_SP:
6698 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6699 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6702 case LEVEL_FILE_TYPE_MM:
6703 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6704 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6707 case LEVEL_FILE_TYPE_DC:
6708 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6711 case LEVEL_FILE_TYPE_SB:
6712 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6716 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6720 // if level file is invalid, restore level structure to default values
6721 if (level->no_valid_file)
6722 setLevelInfoToDefaults(level, level_info_only, FALSE);
6724 if (check_special_flags("use_native_bd_game_engine"))
6725 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6727 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6728 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6730 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6731 CopyNativeLevel_Native_to_RND(level);
6734 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6736 static struct LevelFileInfo level_file_info;
6738 // always start with reliable default values
6739 setFileInfoToDefaults(&level_file_info);
6741 level_file_info.nr = 0; // unknown level number
6742 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6744 setString(&level_file_info.filename, filename);
6746 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6749 static void LoadLevel_InitVersion(struct LevelInfo *level)
6753 if (leveldir_current == NULL) // only when dumping level
6756 // all engine modifications also valid for levels which use latest engine
6757 if (level->game_version < VERSION_IDENT(3,2,0,5))
6759 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6760 level->time_score_base = 10;
6763 if (leveldir_current->latest_engine)
6765 // ---------- use latest game engine --------------------------------------
6767 /* For all levels which are forced to use the latest game engine version
6768 (normally all but user contributed, private and undefined levels), set
6769 the game engine version to the actual version; this allows for actual
6770 corrections in the game engine to take effect for existing, converted
6771 levels (from "classic" or other existing games) to make the emulation
6772 of the corresponding game more accurate, while (hopefully) not breaking
6773 existing levels created from other players. */
6775 level->game_version = GAME_VERSION_ACTUAL;
6777 /* Set special EM style gems behaviour: EM style gems slip down from
6778 normal, steel and growing wall. As this is a more fundamental change,
6779 it seems better to set the default behaviour to "off" (as it is more
6780 natural) and make it configurable in the level editor (as a property
6781 of gem style elements). Already existing converted levels (neither
6782 private nor contributed levels) are changed to the new behaviour. */
6784 if (level->file_version < FILE_VERSION_2_0)
6785 level->em_slippery_gems = TRUE;
6790 // ---------- use game engine the level was created with --------------------
6792 /* For all levels which are not forced to use the latest game engine
6793 version (normally user contributed, private and undefined levels),
6794 use the version of the game engine the levels were created for.
6796 Since 2.0.1, the game engine version is now directly stored
6797 in the level file (chunk "VERS"), so there is no need anymore
6798 to set the game version from the file version (except for old,
6799 pre-2.0 levels, where the game version is still taken from the
6800 file format version used to store the level -- see above). */
6802 // player was faster than enemies in 1.0.0 and before
6803 if (level->file_version == FILE_VERSION_1_0)
6804 for (i = 0; i < MAX_PLAYERS; i++)
6805 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6807 // default behaviour for EM style gems was "slippery" only in 2.0.1
6808 if (level->game_version == VERSION_IDENT(2,0,1,0))
6809 level->em_slippery_gems = TRUE;
6811 // springs could be pushed over pits before (pre-release version) 2.2.0
6812 if (level->game_version < VERSION_IDENT(2,2,0,0))
6813 level->use_spring_bug = TRUE;
6815 if (level->game_version < VERSION_IDENT(3,2,0,5))
6817 // time orb caused limited time in endless time levels before 3.2.0-5
6818 level->use_time_orb_bug = TRUE;
6820 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6821 level->block_snap_field = FALSE;
6823 // extra time score was same value as time left score before 3.2.0-5
6824 level->extra_time_score = level->score[SC_TIME_BONUS];
6827 if (level->game_version < VERSION_IDENT(3,2,0,7))
6829 // default behaviour for snapping was "not continuous" before 3.2.0-7
6830 level->continuous_snapping = FALSE;
6833 // only few elements were able to actively move into acid before 3.1.0
6834 // trigger settings did not exist before 3.1.0; set to default "any"
6835 if (level->game_version < VERSION_IDENT(3,1,0,0))
6837 // correct "can move into acid" settings (all zero in old levels)
6839 level->can_move_into_acid_bits = 0; // nothing can move into acid
6840 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6842 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6843 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6844 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6845 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6847 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6848 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6850 // correct trigger settings (stored as zero == "none" in old levels)
6852 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6854 int element = EL_CUSTOM_START + i;
6855 struct ElementInfo *ei = &element_info[element];
6857 for (j = 0; j < ei->num_change_pages; j++)
6859 struct ElementChangeInfo *change = &ei->change_page[j];
6861 change->trigger_player = CH_PLAYER_ANY;
6862 change->trigger_page = CH_PAGE_ANY;
6867 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6869 int element = EL_CUSTOM_256;
6870 struct ElementInfo *ei = &element_info[element];
6871 struct ElementChangeInfo *change = &ei->change_page[0];
6873 /* This is needed to fix a problem that was caused by a bugfix in function
6874 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6875 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6876 not replace walkable elements, but instead just placed the player on it,
6877 without placing the Sokoban field under the player). Unfortunately, this
6878 breaks "Snake Bite" style levels when the snake is halfway through a door
6879 that just closes (the snake head is still alive and can be moved in this
6880 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6881 player (without Sokoban element) which then gets killed as designed). */
6883 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6884 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6885 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6886 change->target_element = EL_PLAYER_1;
6889 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6890 if (level->game_version < VERSION_IDENT(3,2,5,0))
6892 /* This is needed to fix a problem that was caused by a bugfix in function
6893 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6894 corrects the behaviour when a custom element changes to another custom
6895 element with a higher element number that has change actions defined.
6896 Normally, only one change per frame is allowed for custom elements.
6897 Therefore, it is checked if a custom element already changed in the
6898 current frame; if it did, subsequent changes are suppressed.
6899 Unfortunately, this is only checked for element changes, but not for
6900 change actions, which are still executed. As the function above loops
6901 through all custom elements from lower to higher, an element change
6902 resulting in a lower CE number won't be checked again, while a target
6903 element with a higher number will also be checked, and potential change
6904 actions will get executed for this CE, too (which is wrong), while
6905 further changes are ignored (which is correct). As this bugfix breaks
6906 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6907 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6908 behaviour for existing levels and tapes that make use of this bug */
6910 level->use_action_after_change_bug = TRUE;
6913 // not centering level after relocating player was default only in 3.2.3
6914 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6915 level->shifted_relocation = TRUE;
6917 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6918 if (level->game_version < VERSION_IDENT(3,2,6,0))
6919 level->em_explodes_by_fire = TRUE;
6921 // levels were solved by the first player entering an exit up to 4.1.0.0
6922 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6923 level->solved_by_one_player = TRUE;
6925 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6926 if (level->game_version < VERSION_IDENT(4,1,1,1))
6927 level->use_life_bugs = TRUE;
6929 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6930 if (level->game_version < VERSION_IDENT(4,1,1,1))
6931 level->sb_objects_needed = FALSE;
6933 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6934 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6935 level->finish_dig_collect = FALSE;
6937 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6938 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6939 level->keep_walkable_ce = TRUE;
6942 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6944 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6947 // check if this level is (not) a Sokoban level
6948 for (y = 0; y < level->fieldy; y++)
6949 for (x = 0; x < level->fieldx; x++)
6950 if (!IS_SB_ELEMENT(Tile[x][y]))
6951 is_sokoban_level = FALSE;
6953 if (is_sokoban_level)
6955 // set special level settings for Sokoban levels
6956 SetLevelSettings_SB(level);
6960 static void LoadLevel_InitSettings(struct LevelInfo *level)
6962 // adjust level settings for (non-native) Sokoban-style levels
6963 LoadLevel_InitSettings_SB(level);
6965 // rename levels with title "nameless level" or if renaming is forced
6966 if (leveldir_current->empty_level_name != NULL &&
6967 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6968 leveldir_current->force_level_name))
6969 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6970 leveldir_current->empty_level_name, level_nr);
6973 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6977 // map elements that have changed in newer versions
6978 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6979 level->game_version);
6980 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6981 for (x = 0; x < 3; x++)
6982 for (y = 0; y < 3; y++)
6983 level->yamyam_content[i].e[x][y] =
6984 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6985 level->game_version);
6989 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6993 // map custom element change events that have changed in newer versions
6994 // (these following values were accidentally changed in version 3.0.1)
6995 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6996 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6998 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7000 int element = EL_CUSTOM_START + i;
7002 // order of checking and copying events to be mapped is important
7003 // (do not change the start and end value -- they are constant)
7004 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7006 if (HAS_CHANGE_EVENT(element, j - 2))
7008 SET_CHANGE_EVENT(element, j - 2, FALSE);
7009 SET_CHANGE_EVENT(element, j, TRUE);
7013 // order of checking and copying events to be mapped is important
7014 // (do not change the start and end value -- they are constant)
7015 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7017 if (HAS_CHANGE_EVENT(element, j - 1))
7019 SET_CHANGE_EVENT(element, j - 1, FALSE);
7020 SET_CHANGE_EVENT(element, j, TRUE);
7026 // initialize "can_change" field for old levels with only one change page
7027 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7029 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7031 int element = EL_CUSTOM_START + i;
7033 if (CAN_CHANGE(element))
7034 element_info[element].change->can_change = TRUE;
7038 // correct custom element values (for old levels without these options)
7039 if (level->game_version < VERSION_IDENT(3,1,1,0))
7041 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7043 int element = EL_CUSTOM_START + i;
7044 struct ElementInfo *ei = &element_info[element];
7046 if (ei->access_direction == MV_NO_DIRECTION)
7047 ei->access_direction = MV_ALL_DIRECTIONS;
7051 // correct custom element values (fix invalid values for all versions)
7054 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7056 int element = EL_CUSTOM_START + i;
7057 struct ElementInfo *ei = &element_info[element];
7059 for (j = 0; j < ei->num_change_pages; j++)
7061 struct ElementChangeInfo *change = &ei->change_page[j];
7063 if (change->trigger_player == CH_PLAYER_NONE)
7064 change->trigger_player = CH_PLAYER_ANY;
7066 if (change->trigger_side == CH_SIDE_NONE)
7067 change->trigger_side = CH_SIDE_ANY;
7072 // initialize "can_explode" field for old levels which did not store this
7073 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7074 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7076 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7078 int element = EL_CUSTOM_START + i;
7080 if (EXPLODES_1X1_OLD(element))
7081 element_info[element].explosion_type = EXPLODES_1X1;
7083 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7084 EXPLODES_SMASHED(element) ||
7085 EXPLODES_IMPACT(element)));
7089 // correct previously hard-coded move delay values for maze runner style
7090 if (level->game_version < VERSION_IDENT(3,1,1,0))
7092 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7094 int element = EL_CUSTOM_START + i;
7096 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7098 // previously hard-coded and therefore ignored
7099 element_info[element].move_delay_fixed = 9;
7100 element_info[element].move_delay_random = 0;
7105 // set some other uninitialized values of custom elements in older levels
7106 if (level->game_version < VERSION_IDENT(3,1,0,0))
7108 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7110 int element = EL_CUSTOM_START + i;
7112 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7114 element_info[element].explosion_delay = 17;
7115 element_info[element].ignition_delay = 8;
7119 // set mouse click change events to work for left/middle/right mouse button
7120 if (level->game_version < VERSION_IDENT(4,2,3,0))
7122 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7124 int element = EL_CUSTOM_START + i;
7125 struct ElementInfo *ei = &element_info[element];
7127 for (j = 0; j < ei->num_change_pages; j++)
7129 struct ElementChangeInfo *change = &ei->change_page[j];
7131 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7132 change->has_event[CE_PRESSED_BY_MOUSE] ||
7133 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7134 change->has_event[CE_MOUSE_PRESSED_ON_X])
7135 change->trigger_side = CH_SIDE_ANY;
7141 static void LoadLevel_InitElements(struct LevelInfo *level)
7143 LoadLevel_InitStandardElements(level);
7145 if (level->file_has_custom_elements)
7146 LoadLevel_InitCustomElements(level);
7148 // initialize element properties for level editor etc.
7149 InitElementPropertiesEngine(level->game_version);
7150 InitElementPropertiesGfxElement();
7153 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7157 // map elements that have changed in newer versions
7158 for (y = 0; y < level->fieldy; y++)
7159 for (x = 0; x < level->fieldx; x++)
7160 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7161 level->game_version);
7163 // clear unused playfield data (nicer if level gets resized in editor)
7164 for (x = 0; x < MAX_LEV_FIELDX; x++)
7165 for (y = 0; y < MAX_LEV_FIELDY; y++)
7166 if (x >= level->fieldx || y >= level->fieldy)
7167 level->field[x][y] = EL_EMPTY;
7169 // copy elements to runtime playfield array
7170 for (x = 0; x < MAX_LEV_FIELDX; x++)
7171 for (y = 0; y < MAX_LEV_FIELDY; y++)
7172 Tile[x][y] = level->field[x][y];
7174 // initialize level size variables for faster access
7175 lev_fieldx = level->fieldx;
7176 lev_fieldy = level->fieldy;
7178 // determine border element for this level
7179 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7180 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7185 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7187 struct LevelFileInfo *level_file_info = &level->file_info;
7189 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7190 CopyNativeLevel_RND_to_Native(level);
7193 static void LoadLevelTemplate_LoadAndInit(void)
7195 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7197 LoadLevel_InitVersion(&level_template);
7198 LoadLevel_InitElements(&level_template);
7199 LoadLevel_InitSettings(&level_template);
7201 ActivateLevelTemplate();
7204 void LoadLevelTemplate(int nr)
7206 if (!fileExists(getGlobalLevelTemplateFilename()))
7208 Warn("no level template found for this level");
7213 setLevelFileInfo(&level_template.file_info, nr);
7215 LoadLevelTemplate_LoadAndInit();
7218 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7220 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7222 LoadLevelTemplate_LoadAndInit();
7225 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7227 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7229 if (level.use_custom_template)
7231 if (network_level != NULL)
7232 LoadNetworkLevelTemplate(network_level);
7234 LoadLevelTemplate(-1);
7237 LoadLevel_InitVersion(&level);
7238 LoadLevel_InitElements(&level);
7239 LoadLevel_InitPlayfield(&level);
7240 LoadLevel_InitSettings(&level);
7242 LoadLevel_InitNativeEngines(&level);
7245 void LoadLevel(int nr)
7247 SetLevelSetInfo(leveldir_current->identifier, nr);
7249 setLevelFileInfo(&level.file_info, nr);
7251 LoadLevel_LoadAndInit(NULL);
7254 void LoadLevelInfoOnly(int nr)
7256 setLevelFileInfo(&level.file_info, nr);
7258 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7261 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7263 SetLevelSetInfo(network_level->leveldir_identifier,
7264 network_level->file_info.nr);
7266 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7268 LoadLevel_LoadAndInit(network_level);
7271 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7275 chunk_size += putFileVersion(file, level->file_version);
7276 chunk_size += putFileVersion(file, level->game_version);
7281 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7285 chunk_size += putFile16BitBE(file, level->creation_date.year);
7286 chunk_size += putFile8Bit(file, level->creation_date.month);
7287 chunk_size += putFile8Bit(file, level->creation_date.day);
7292 #if ENABLE_HISTORIC_CHUNKS
7293 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7297 putFile8Bit(file, level->fieldx);
7298 putFile8Bit(file, level->fieldy);
7300 putFile16BitBE(file, level->time);
7301 putFile16BitBE(file, level->gems_needed);
7303 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7304 putFile8Bit(file, level->name[i]);
7306 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7307 putFile8Bit(file, level->score[i]);
7309 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7310 for (y = 0; y < 3; y++)
7311 for (x = 0; x < 3; x++)
7312 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7313 level->yamyam_content[i].e[x][y]));
7314 putFile8Bit(file, level->amoeba_speed);
7315 putFile8Bit(file, level->time_magic_wall);
7316 putFile8Bit(file, level->time_wheel);
7317 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7318 level->amoeba_content));
7319 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7320 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7321 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7322 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7324 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7326 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7327 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7328 putFile32BitBE(file, level->can_move_into_acid_bits);
7329 putFile8Bit(file, level->dont_collide_with_bits);
7331 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7332 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7334 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7335 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7336 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7338 putFile8Bit(file, level->game_engine_type);
7340 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7344 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7349 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7350 chunk_size += putFile8Bit(file, level->name[i]);
7355 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7360 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7361 chunk_size += putFile8Bit(file, level->author[i]);
7366 #if ENABLE_HISTORIC_CHUNKS
7367 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7372 for (y = 0; y < level->fieldy; y++)
7373 for (x = 0; x < level->fieldx; x++)
7374 if (level->encoding_16bit_field)
7375 chunk_size += putFile16BitBE(file, level->field[x][y]);
7377 chunk_size += putFile8Bit(file, level->field[x][y]);
7383 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7388 for (y = 0; y < level->fieldy; y++)
7389 for (x = 0; x < level->fieldx; x++)
7390 chunk_size += putFile16BitBE(file, level->field[x][y]);
7395 #if ENABLE_HISTORIC_CHUNKS
7396 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7400 putFile8Bit(file, EL_YAMYAM);
7401 putFile8Bit(file, level->num_yamyam_contents);
7402 putFile8Bit(file, 0);
7403 putFile8Bit(file, 0);
7405 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7406 for (y = 0; y < 3; y++)
7407 for (x = 0; x < 3; x++)
7408 if (level->encoding_16bit_field)
7409 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7411 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7415 #if ENABLE_HISTORIC_CHUNKS
7416 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7419 int num_contents, content_xsize, content_ysize;
7420 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7422 if (element == EL_YAMYAM)
7424 num_contents = level->num_yamyam_contents;
7428 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7429 for (y = 0; y < 3; y++)
7430 for (x = 0; x < 3; x++)
7431 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7433 else if (element == EL_BD_AMOEBA)
7439 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7440 for (y = 0; y < 3; y++)
7441 for (x = 0; x < 3; x++)
7442 content_array[i][x][y] = EL_EMPTY;
7443 content_array[0][0][0] = level->amoeba_content;
7447 // chunk header already written -- write empty chunk data
7448 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7450 Warn("cannot save content for element '%d'", element);
7455 putFile16BitBE(file, element);
7456 putFile8Bit(file, num_contents);
7457 putFile8Bit(file, content_xsize);
7458 putFile8Bit(file, content_ysize);
7460 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7462 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7463 for (y = 0; y < 3; y++)
7464 for (x = 0; x < 3; x++)
7465 putFile16BitBE(file, content_array[i][x][y]);
7469 #if ENABLE_HISTORIC_CHUNKS
7470 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7472 int envelope_nr = element - EL_ENVELOPE_1;
7473 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7477 chunk_size += putFile16BitBE(file, element);
7478 chunk_size += putFile16BitBE(file, envelope_len);
7479 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7480 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7482 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7483 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7485 for (i = 0; i < envelope_len; i++)
7486 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7492 #if ENABLE_HISTORIC_CHUNKS
7493 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7494 int num_changed_custom_elements)
7498 putFile16BitBE(file, num_changed_custom_elements);
7500 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7502 int element = EL_CUSTOM_START + i;
7504 struct ElementInfo *ei = &element_info[element];
7506 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7508 if (check < num_changed_custom_elements)
7510 putFile16BitBE(file, element);
7511 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7518 if (check != num_changed_custom_elements) // should not happen
7519 Warn("inconsistent number of custom element properties");
7523 #if ENABLE_HISTORIC_CHUNKS
7524 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7525 int num_changed_custom_elements)
7529 putFile16BitBE(file, num_changed_custom_elements);
7531 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7533 int element = EL_CUSTOM_START + i;
7535 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7537 if (check < num_changed_custom_elements)
7539 putFile16BitBE(file, element);
7540 putFile16BitBE(file, element_info[element].change->target_element);
7547 if (check != num_changed_custom_elements) // should not happen
7548 Warn("inconsistent number of custom target elements");
7552 #if ENABLE_HISTORIC_CHUNKS
7553 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7554 int num_changed_custom_elements)
7556 int i, j, x, y, check = 0;
7558 putFile16BitBE(file, num_changed_custom_elements);
7560 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7562 int element = EL_CUSTOM_START + i;
7563 struct ElementInfo *ei = &element_info[element];
7565 if (ei->modified_settings)
7567 if (check < num_changed_custom_elements)
7569 putFile16BitBE(file, element);
7571 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7572 putFile8Bit(file, ei->description[j]);
7574 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7576 // some free bytes for future properties and padding
7577 WriteUnusedBytesToFile(file, 7);
7579 putFile8Bit(file, ei->use_gfx_element);
7580 putFile16BitBE(file, ei->gfx_element_initial);
7582 putFile8Bit(file, ei->collect_score_initial);
7583 putFile8Bit(file, ei->collect_count_initial);
7585 putFile16BitBE(file, ei->push_delay_fixed);
7586 putFile16BitBE(file, ei->push_delay_random);
7587 putFile16BitBE(file, ei->move_delay_fixed);
7588 putFile16BitBE(file, ei->move_delay_random);
7590 putFile16BitBE(file, ei->move_pattern);
7591 putFile8Bit(file, ei->move_direction_initial);
7592 putFile8Bit(file, ei->move_stepsize);
7594 for (y = 0; y < 3; y++)
7595 for (x = 0; x < 3; x++)
7596 putFile16BitBE(file, ei->content.e[x][y]);
7598 putFile32BitBE(file, ei->change->events);
7600 putFile16BitBE(file, ei->change->target_element);
7602 putFile16BitBE(file, ei->change->delay_fixed);
7603 putFile16BitBE(file, ei->change->delay_random);
7604 putFile16BitBE(file, ei->change->delay_frames);
7606 putFile16BitBE(file, ei->change->initial_trigger_element);
7608 putFile8Bit(file, ei->change->explode);
7609 putFile8Bit(file, ei->change->use_target_content);
7610 putFile8Bit(file, ei->change->only_if_complete);
7611 putFile8Bit(file, ei->change->use_random_replace);
7613 putFile8Bit(file, ei->change->random_percentage);
7614 putFile8Bit(file, ei->change->replace_when);
7616 for (y = 0; y < 3; y++)
7617 for (x = 0; x < 3; x++)
7618 putFile16BitBE(file, ei->change->content.e[x][y]);
7620 putFile8Bit(file, ei->slippery_type);
7622 // some free bytes for future properties and padding
7623 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7630 if (check != num_changed_custom_elements) // should not happen
7631 Warn("inconsistent number of custom element properties");
7635 #if ENABLE_HISTORIC_CHUNKS
7636 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7638 struct ElementInfo *ei = &element_info[element];
7641 // ---------- custom element base property values (96 bytes) ----------------
7643 putFile16BitBE(file, element);
7645 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7646 putFile8Bit(file, ei->description[i]);
7648 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7650 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7652 putFile8Bit(file, ei->num_change_pages);
7654 putFile16BitBE(file, ei->ce_value_fixed_initial);
7655 putFile16BitBE(file, ei->ce_value_random_initial);
7656 putFile8Bit(file, ei->use_last_ce_value);
7658 putFile8Bit(file, ei->use_gfx_element);
7659 putFile16BitBE(file, ei->gfx_element_initial);
7661 putFile8Bit(file, ei->collect_score_initial);
7662 putFile8Bit(file, ei->collect_count_initial);
7664 putFile8Bit(file, ei->drop_delay_fixed);
7665 putFile8Bit(file, ei->push_delay_fixed);
7666 putFile8Bit(file, ei->drop_delay_random);
7667 putFile8Bit(file, ei->push_delay_random);
7668 putFile16BitBE(file, ei->move_delay_fixed);
7669 putFile16BitBE(file, ei->move_delay_random);
7671 // bits 0 - 15 of "move_pattern" ...
7672 putFile16BitBE(file, ei->move_pattern & 0xffff);
7673 putFile8Bit(file, ei->move_direction_initial);
7674 putFile8Bit(file, ei->move_stepsize);
7676 putFile8Bit(file, ei->slippery_type);
7678 for (y = 0; y < 3; y++)
7679 for (x = 0; x < 3; x++)
7680 putFile16BitBE(file, ei->content.e[x][y]);
7682 putFile16BitBE(file, ei->move_enter_element);
7683 putFile16BitBE(file, ei->move_leave_element);
7684 putFile8Bit(file, ei->move_leave_type);
7686 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7687 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7689 putFile8Bit(file, ei->access_direction);
7691 putFile8Bit(file, ei->explosion_delay);
7692 putFile8Bit(file, ei->ignition_delay);
7693 putFile8Bit(file, ei->explosion_type);
7695 // some free bytes for future custom property values and padding
7696 WriteUnusedBytesToFile(file, 1);
7698 // ---------- change page property values (48 bytes) ------------------------
7700 for (i = 0; i < ei->num_change_pages; i++)
7702 struct ElementChangeInfo *change = &ei->change_page[i];
7703 unsigned int event_bits;
7705 // bits 0 - 31 of "has_event[]" ...
7707 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7708 if (change->has_event[j])
7709 event_bits |= (1u << j);
7710 putFile32BitBE(file, event_bits);
7712 putFile16BitBE(file, change->target_element);
7714 putFile16BitBE(file, change->delay_fixed);
7715 putFile16BitBE(file, change->delay_random);
7716 putFile16BitBE(file, change->delay_frames);
7718 putFile16BitBE(file, change->initial_trigger_element);
7720 putFile8Bit(file, change->explode);
7721 putFile8Bit(file, change->use_target_content);
7722 putFile8Bit(file, change->only_if_complete);
7723 putFile8Bit(file, change->use_random_replace);
7725 putFile8Bit(file, change->random_percentage);
7726 putFile8Bit(file, change->replace_when);
7728 for (y = 0; y < 3; y++)
7729 for (x = 0; x < 3; x++)
7730 putFile16BitBE(file, change->target_content.e[x][y]);
7732 putFile8Bit(file, change->can_change);
7734 putFile8Bit(file, change->trigger_side);
7736 putFile8Bit(file, change->trigger_player);
7737 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7738 log_2(change->trigger_page)));
7740 putFile8Bit(file, change->has_action);
7741 putFile8Bit(file, change->action_type);
7742 putFile8Bit(file, change->action_mode);
7743 putFile16BitBE(file, change->action_arg);
7745 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7747 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7748 if (change->has_event[j])
7749 event_bits |= (1u << (j - 32));
7750 putFile8Bit(file, event_bits);
7755 #if ENABLE_HISTORIC_CHUNKS
7756 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7758 struct ElementInfo *ei = &element_info[element];
7759 struct ElementGroupInfo *group = ei->group;
7762 putFile16BitBE(file, element);
7764 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7765 putFile8Bit(file, ei->description[i]);
7767 putFile8Bit(file, group->num_elements);
7769 putFile8Bit(file, ei->use_gfx_element);
7770 putFile16BitBE(file, ei->gfx_element_initial);
7772 putFile8Bit(file, group->choice_mode);
7774 // some free bytes for future values and padding
7775 WriteUnusedBytesToFile(file, 3);
7777 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7778 putFile16BitBE(file, group->element[i]);
7782 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7783 boolean write_element)
7785 int save_type = entry->save_type;
7786 int data_type = entry->data_type;
7787 int conf_type = entry->conf_type;
7788 int byte_mask = conf_type & CONF_MASK_BYTES;
7789 int element = entry->element;
7790 int default_value = entry->default_value;
7792 boolean modified = FALSE;
7794 if (byte_mask != CONF_MASK_MULTI_BYTES)
7796 void *value_ptr = entry->value;
7797 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7800 // check if any settings have been modified before saving them
7801 if (value != default_value)
7804 // do not save if explicitly told or if unmodified default settings
7805 if ((save_type == SAVE_CONF_NEVER) ||
7806 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7810 num_bytes += putFile16BitBE(file, element);
7812 num_bytes += putFile8Bit(file, conf_type);
7813 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7814 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7815 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7818 else if (data_type == TYPE_STRING)
7820 char *default_string = entry->default_string;
7821 char *string = (char *)(entry->value);
7822 int string_length = strlen(string);
7825 // check if any settings have been modified before saving them
7826 if (!strEqual(string, default_string))
7829 // do not save if explicitly told or if unmodified default settings
7830 if ((save_type == SAVE_CONF_NEVER) ||
7831 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7835 num_bytes += putFile16BitBE(file, element);
7837 num_bytes += putFile8Bit(file, conf_type);
7838 num_bytes += putFile16BitBE(file, string_length);
7840 for (i = 0; i < string_length; i++)
7841 num_bytes += putFile8Bit(file, string[i]);
7843 else if (data_type == TYPE_ELEMENT_LIST)
7845 int *element_array = (int *)(entry->value);
7846 int num_elements = *(int *)(entry->num_entities);
7849 // check if any settings have been modified before saving them
7850 for (i = 0; i < num_elements; i++)
7851 if (element_array[i] != default_value)
7854 // do not save if explicitly told or if unmodified default settings
7855 if ((save_type == SAVE_CONF_NEVER) ||
7856 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7860 num_bytes += putFile16BitBE(file, element);
7862 num_bytes += putFile8Bit(file, conf_type);
7863 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7865 for (i = 0; i < num_elements; i++)
7866 num_bytes += putFile16BitBE(file, element_array[i]);
7868 else if (data_type == TYPE_CONTENT_LIST)
7870 struct Content *content = (struct Content *)(entry->value);
7871 int num_contents = *(int *)(entry->num_entities);
7874 // check if any settings have been modified before saving them
7875 for (i = 0; i < num_contents; i++)
7876 for (y = 0; y < 3; y++)
7877 for (x = 0; x < 3; x++)
7878 if (content[i].e[x][y] != default_value)
7881 // do not save if explicitly told or if unmodified default settings
7882 if ((save_type == SAVE_CONF_NEVER) ||
7883 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7887 num_bytes += putFile16BitBE(file, element);
7889 num_bytes += putFile8Bit(file, conf_type);
7890 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7892 for (i = 0; i < num_contents; i++)
7893 for (y = 0; y < 3; y++)
7894 for (x = 0; x < 3; x++)
7895 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7901 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7906 li = *level; // copy level data into temporary buffer
7908 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7909 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7914 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7919 li = *level; // copy level data into temporary buffer
7921 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7922 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7927 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7929 int envelope_nr = element - EL_ENVELOPE_1;
7933 chunk_size += putFile16BitBE(file, element);
7935 // copy envelope data into temporary buffer
7936 xx_envelope = level->envelope[envelope_nr];
7938 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7939 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7944 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7946 struct ElementInfo *ei = &element_info[element];
7950 chunk_size += putFile16BitBE(file, element);
7952 xx_ei = *ei; // copy element data into temporary buffer
7954 // set default description string for this specific element
7955 strcpy(xx_default_description, getDefaultElementDescription(ei));
7957 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7958 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7960 for (i = 0; i < ei->num_change_pages; i++)
7962 struct ElementChangeInfo *change = &ei->change_page[i];
7964 xx_current_change_page = i;
7966 xx_change = *change; // copy change data into temporary buffer
7969 setEventBitsFromEventFlags(change);
7971 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7972 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7979 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7981 struct ElementInfo *ei = &element_info[element];
7982 struct ElementGroupInfo *group = ei->group;
7986 chunk_size += putFile16BitBE(file, element);
7988 xx_ei = *ei; // copy element data into temporary buffer
7989 xx_group = *group; // copy group data into temporary buffer
7991 // set default description string for this specific element
7992 strcpy(xx_default_description, getDefaultElementDescription(ei));
7994 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7995 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8000 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8002 struct ElementInfo *ei = &element_info[element];
8006 chunk_size += putFile16BitBE(file, element);
8008 xx_ei = *ei; // copy element data into temporary buffer
8010 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8011 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8016 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8017 boolean save_as_template)
8023 if (!(file = fopen(filename, MODE_WRITE)))
8025 Warn("cannot save level file '%s'", filename);
8030 level->file_version = FILE_VERSION_ACTUAL;
8031 level->game_version = GAME_VERSION_ACTUAL;
8033 level->creation_date = getCurrentDate();
8035 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8036 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8038 chunk_size = SaveLevel_VERS(NULL, level);
8039 putFileChunkBE(file, "VERS", chunk_size);
8040 SaveLevel_VERS(file, level);
8042 chunk_size = SaveLevel_DATE(NULL, level);
8043 putFileChunkBE(file, "DATE", chunk_size);
8044 SaveLevel_DATE(file, level);
8046 chunk_size = SaveLevel_NAME(NULL, level);
8047 putFileChunkBE(file, "NAME", chunk_size);
8048 SaveLevel_NAME(file, level);
8050 chunk_size = SaveLevel_AUTH(NULL, level);
8051 putFileChunkBE(file, "AUTH", chunk_size);
8052 SaveLevel_AUTH(file, level);
8054 chunk_size = SaveLevel_INFO(NULL, level);
8055 putFileChunkBE(file, "INFO", chunk_size);
8056 SaveLevel_INFO(file, level);
8058 chunk_size = SaveLevel_BODY(NULL, level);
8059 putFileChunkBE(file, "BODY", chunk_size);
8060 SaveLevel_BODY(file, level);
8062 chunk_size = SaveLevel_ELEM(NULL, level);
8063 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8065 putFileChunkBE(file, "ELEM", chunk_size);
8066 SaveLevel_ELEM(file, level);
8069 for (i = 0; i < NUM_ENVELOPES; i++)
8071 int element = EL_ENVELOPE_1 + i;
8073 chunk_size = SaveLevel_NOTE(NULL, level, element);
8074 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8076 putFileChunkBE(file, "NOTE", chunk_size);
8077 SaveLevel_NOTE(file, level, element);
8081 // if not using template level, check for non-default custom/group elements
8082 if (!level->use_custom_template || save_as_template)
8084 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8086 int element = EL_CUSTOM_START + i;
8088 chunk_size = SaveLevel_CUSX(NULL, level, element);
8089 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8091 putFileChunkBE(file, "CUSX", chunk_size);
8092 SaveLevel_CUSX(file, level, element);
8096 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8098 int element = EL_GROUP_START + i;
8100 chunk_size = SaveLevel_GRPX(NULL, level, element);
8101 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8103 putFileChunkBE(file, "GRPX", chunk_size);
8104 SaveLevel_GRPX(file, level, element);
8108 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8110 int element = GET_EMPTY_ELEMENT(i);
8112 chunk_size = SaveLevel_EMPX(NULL, level, element);
8113 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8115 putFileChunkBE(file, "EMPX", chunk_size);
8116 SaveLevel_EMPX(file, level, element);
8123 SetFilePermissions(filename, PERMS_PRIVATE);
8126 void SaveLevel(int nr)
8128 char *filename = getDefaultLevelFilename(nr);
8130 SaveLevelFromFilename(&level, filename, FALSE);
8133 void SaveLevelTemplate(void)
8135 char *filename = getLocalLevelTemplateFilename();
8137 SaveLevelFromFilename(&level, filename, TRUE);
8140 boolean SaveLevelChecked(int nr)
8142 char *filename = getDefaultLevelFilename(nr);
8143 boolean new_level = !fileExists(filename);
8144 boolean level_saved = FALSE;
8146 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8151 Request("Level saved!", REQ_CONFIRM);
8159 void DumpLevel(struct LevelInfo *level)
8161 if (level->no_level_file || level->no_valid_file)
8163 Warn("cannot dump -- no valid level file found");
8169 Print("Level xxx (file version %08d, game version %08d)\n",
8170 level->file_version, level->game_version);
8173 Print("Level author: '%s'\n", level->author);
8174 Print("Level title: '%s'\n", level->name);
8176 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8178 Print("Level time: %d seconds\n", level->time);
8179 Print("Gems needed: %d\n", level->gems_needed);
8181 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8182 Print("Time for wheel: %d seconds\n", level->time_wheel);
8183 Print("Time for light: %d seconds\n", level->time_light);
8184 Print("Time for timegate: %d seconds\n", level->time_timegate);
8186 Print("Amoeba speed: %d\n", level->amoeba_speed);
8189 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8190 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8191 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8192 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8193 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8194 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8200 for (i = 0; i < NUM_ENVELOPES; i++)
8202 char *text = level->envelope[i].text;
8203 int text_len = strlen(text);
8204 boolean has_text = FALSE;
8206 for (j = 0; j < text_len; j++)
8207 if (text[j] != ' ' && text[j] != '\n')
8213 Print("Envelope %d:\n'%s'\n", i + 1, text);
8221 void DumpLevels(void)
8223 static LevelDirTree *dumplevel_leveldir = NULL;
8225 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8226 global.dumplevel_leveldir);
8228 if (dumplevel_leveldir == NULL)
8229 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8231 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8232 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8233 Fail("no such level number: %d", global.dumplevel_level_nr);
8235 leveldir_current = dumplevel_leveldir;
8237 LoadLevel(global.dumplevel_level_nr);
8244 // ============================================================================
8245 // tape file functions
8246 // ============================================================================
8248 static void setTapeInfoToDefaults(void)
8252 // always start with reliable default values (empty tape)
8255 // default values (also for pre-1.2 tapes) with only the first player
8256 tape.player_participates[0] = TRUE;
8257 for (i = 1; i < MAX_PLAYERS; i++)
8258 tape.player_participates[i] = FALSE;
8260 // at least one (default: the first) player participates in every tape
8261 tape.num_participating_players = 1;
8263 tape.property_bits = TAPE_PROPERTY_NONE;
8265 tape.level_nr = level_nr;
8267 tape.changed = FALSE;
8268 tape.solved = FALSE;
8270 tape.recording = FALSE;
8271 tape.playing = FALSE;
8272 tape.pausing = FALSE;
8274 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8275 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8277 tape.no_info_chunk = TRUE;
8278 tape.no_valid_file = FALSE;
8281 static int getTapePosSize(struct TapeInfo *tape)
8283 int tape_pos_size = 0;
8285 if (tape->use_key_actions)
8286 tape_pos_size += tape->num_participating_players;
8288 if (tape->use_mouse_actions)
8289 tape_pos_size += 3; // x and y position and mouse button mask
8291 tape_pos_size += 1; // tape action delay value
8293 return tape_pos_size;
8296 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8298 tape->use_key_actions = FALSE;
8299 tape->use_mouse_actions = FALSE;
8301 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8302 tape->use_key_actions = TRUE;
8304 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8305 tape->use_mouse_actions = TRUE;
8308 static int getTapeActionValue(struct TapeInfo *tape)
8310 return (tape->use_key_actions &&
8311 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8312 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8313 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8314 TAPE_ACTIONS_DEFAULT);
8317 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8319 tape->file_version = getFileVersion(file);
8320 tape->game_version = getFileVersion(file);
8325 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8329 tape->random_seed = getFile32BitBE(file);
8330 tape->date = getFile32BitBE(file);
8331 tape->length = getFile32BitBE(file);
8333 // read header fields that are new since version 1.2
8334 if (tape->file_version >= FILE_VERSION_1_2)
8336 byte store_participating_players = getFile8Bit(file);
8339 // since version 1.2, tapes store which players participate in the tape
8340 tape->num_participating_players = 0;
8341 for (i = 0; i < MAX_PLAYERS; i++)
8343 tape->player_participates[i] = FALSE;
8345 if (store_participating_players & (1 << i))
8347 tape->player_participates[i] = TRUE;
8348 tape->num_participating_players++;
8352 setTapeActionFlags(tape, getFile8Bit(file));
8354 tape->property_bits = getFile8Bit(file);
8355 tape->solved = getFile8Bit(file);
8357 engine_version = getFileVersion(file);
8358 if (engine_version > 0)
8359 tape->engine_version = engine_version;
8361 tape->engine_version = tape->game_version;
8367 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8369 tape->scr_fieldx = getFile8Bit(file);
8370 tape->scr_fieldy = getFile8Bit(file);
8375 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8377 char *level_identifier = NULL;
8378 int level_identifier_size;
8381 tape->no_info_chunk = FALSE;
8383 level_identifier_size = getFile16BitBE(file);
8385 level_identifier = checked_malloc(level_identifier_size);
8387 for (i = 0; i < level_identifier_size; i++)
8388 level_identifier[i] = getFile8Bit(file);
8390 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8391 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8393 checked_free(level_identifier);
8395 tape->level_nr = getFile16BitBE(file);
8397 chunk_size = 2 + level_identifier_size + 2;
8402 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8405 int tape_pos_size = getTapePosSize(tape);
8406 int chunk_size_expected = tape_pos_size * tape->length;
8408 if (chunk_size_expected != chunk_size)
8410 ReadUnusedBytesFromFile(file, chunk_size);
8411 return chunk_size_expected;
8414 for (i = 0; i < tape->length; i++)
8416 if (i >= MAX_TAPE_LEN)
8418 Warn("tape truncated -- size exceeds maximum tape size %d",
8421 // tape too large; read and ignore remaining tape data from this chunk
8422 for (;i < tape->length; i++)
8423 ReadUnusedBytesFromFile(file, tape_pos_size);
8428 if (tape->use_key_actions)
8430 for (j = 0; j < MAX_PLAYERS; j++)
8432 tape->pos[i].action[j] = MV_NONE;
8434 if (tape->player_participates[j])
8435 tape->pos[i].action[j] = getFile8Bit(file);
8439 if (tape->use_mouse_actions)
8441 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8442 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8443 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8446 tape->pos[i].delay = getFile8Bit(file);
8448 if (tape->file_version == FILE_VERSION_1_0)
8450 // eliminate possible diagonal moves in old tapes
8451 // this is only for backward compatibility
8453 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8454 byte action = tape->pos[i].action[0];
8455 int k, num_moves = 0;
8457 for (k = 0; k < 4; k++)
8459 if (action & joy_dir[k])
8461 tape->pos[i + num_moves].action[0] = joy_dir[k];
8463 tape->pos[i + num_moves].delay = 0;
8472 tape->length += num_moves;
8475 else if (tape->file_version < FILE_VERSION_2_0)
8477 // convert pre-2.0 tapes to new tape format
8479 if (tape->pos[i].delay > 1)
8482 tape->pos[i + 1] = tape->pos[i];
8483 tape->pos[i + 1].delay = 1;
8486 for (j = 0; j < MAX_PLAYERS; j++)
8487 tape->pos[i].action[j] = MV_NONE;
8488 tape->pos[i].delay--;
8495 if (checkEndOfFile(file))
8499 if (i != tape->length)
8500 chunk_size = tape_pos_size * i;
8505 static void LoadTape_SokobanSolution(char *filename)
8508 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8510 if (!(file = openFile(filename, MODE_READ)))
8512 tape.no_valid_file = TRUE;
8517 while (!checkEndOfFile(file))
8519 unsigned char c = getByteFromFile(file);
8521 if (checkEndOfFile(file))
8528 tape.pos[tape.length].action[0] = MV_UP;
8529 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8535 tape.pos[tape.length].action[0] = MV_DOWN;
8536 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8542 tape.pos[tape.length].action[0] = MV_LEFT;
8543 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8549 tape.pos[tape.length].action[0] = MV_RIGHT;
8550 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8558 // ignore white-space characters
8562 tape.no_valid_file = TRUE;
8564 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8572 if (tape.no_valid_file)
8575 tape.length_frames = GetTapeLengthFrames();
8576 tape.length_seconds = GetTapeLengthSeconds();
8579 void LoadTapeFromFilename(char *filename)
8581 char cookie[MAX_LINE_LEN];
8582 char chunk_name[CHUNK_ID_LEN + 1];
8586 // always start with reliable default values
8587 setTapeInfoToDefaults();
8589 if (strSuffix(filename, ".sln"))
8591 LoadTape_SokobanSolution(filename);
8596 if (!(file = openFile(filename, MODE_READ)))
8598 tape.no_valid_file = TRUE;
8603 getFileChunkBE(file, chunk_name, NULL);
8604 if (strEqual(chunk_name, "RND1"))
8606 getFile32BitBE(file); // not used
8608 getFileChunkBE(file, chunk_name, NULL);
8609 if (!strEqual(chunk_name, "TAPE"))
8611 tape.no_valid_file = TRUE;
8613 Warn("unknown format of tape file '%s'", filename);
8620 else // check for pre-2.0 file format with cookie string
8622 strcpy(cookie, chunk_name);
8623 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8625 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8626 cookie[strlen(cookie) - 1] = '\0';
8628 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8630 tape.no_valid_file = TRUE;
8632 Warn("unknown format of tape file '%s'", filename);
8639 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8641 tape.no_valid_file = TRUE;
8643 Warn("unsupported version of tape file '%s'", filename);
8650 // pre-2.0 tape files have no game version, so use file version here
8651 tape.game_version = tape.file_version;
8654 if (tape.file_version < FILE_VERSION_1_2)
8656 // tape files from versions before 1.2.0 without chunk structure
8657 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8658 LoadTape_BODY(file, 2 * tape.length, &tape);
8666 int (*loader)(File *, int, struct TapeInfo *);
8670 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8671 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8672 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8673 { "INFO", -1, LoadTape_INFO },
8674 { "BODY", -1, LoadTape_BODY },
8678 while (getFileChunkBE(file, chunk_name, &chunk_size))
8682 while (chunk_info[i].name != NULL &&
8683 !strEqual(chunk_name, chunk_info[i].name))
8686 if (chunk_info[i].name == NULL)
8688 Warn("unknown chunk '%s' in tape file '%s'",
8689 chunk_name, filename);
8691 ReadUnusedBytesFromFile(file, chunk_size);
8693 else if (chunk_info[i].size != -1 &&
8694 chunk_info[i].size != chunk_size)
8696 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8697 chunk_size, chunk_name, filename);
8699 ReadUnusedBytesFromFile(file, chunk_size);
8703 // call function to load this tape chunk
8704 int chunk_size_expected =
8705 (chunk_info[i].loader)(file, chunk_size, &tape);
8707 // the size of some chunks cannot be checked before reading other
8708 // chunks first (like "HEAD" and "BODY") that contain some header
8709 // information, so check them here
8710 if (chunk_size_expected != chunk_size)
8712 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8713 chunk_size, chunk_name, filename);
8721 tape.length_frames = GetTapeLengthFrames();
8722 tape.length_seconds = GetTapeLengthSeconds();
8725 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8727 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8729 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8730 tape.engine_version);
8734 void LoadTape(int nr)
8736 char *filename = getTapeFilename(nr);
8738 LoadTapeFromFilename(filename);
8741 void LoadSolutionTape(int nr)
8743 char *filename = getSolutionTapeFilename(nr);
8745 LoadTapeFromFilename(filename);
8747 if (TAPE_IS_EMPTY(tape))
8749 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8750 level.native_bd_level->replay != NULL)
8751 CopyNativeTape_BD_to_RND(&level);
8752 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8753 level.native_sp_level->demo.is_available)
8754 CopyNativeTape_SP_to_RND(&level);
8758 void LoadScoreTape(char *score_tape_basename, int nr)
8760 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8762 LoadTapeFromFilename(filename);
8765 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8767 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8769 LoadTapeFromFilename(filename);
8772 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8774 // chunk required for team mode tapes with non-default screen size
8775 return (tape->num_participating_players > 1 &&
8776 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8777 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8780 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8782 putFileVersion(file, tape->file_version);
8783 putFileVersion(file, tape->game_version);
8786 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8789 byte store_participating_players = 0;
8791 // set bits for participating players for compact storage
8792 for (i = 0; i < MAX_PLAYERS; i++)
8793 if (tape->player_participates[i])
8794 store_participating_players |= (1 << i);
8796 putFile32BitBE(file, tape->random_seed);
8797 putFile32BitBE(file, tape->date);
8798 putFile32BitBE(file, tape->length);
8800 putFile8Bit(file, store_participating_players);
8802 putFile8Bit(file, getTapeActionValue(tape));
8804 putFile8Bit(file, tape->property_bits);
8805 putFile8Bit(file, tape->solved);
8807 putFileVersion(file, tape->engine_version);
8810 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8812 putFile8Bit(file, tape->scr_fieldx);
8813 putFile8Bit(file, tape->scr_fieldy);
8816 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8818 int level_identifier_size = strlen(tape->level_identifier) + 1;
8821 putFile16BitBE(file, level_identifier_size);
8823 for (i = 0; i < level_identifier_size; i++)
8824 putFile8Bit(file, tape->level_identifier[i]);
8826 putFile16BitBE(file, tape->level_nr);
8829 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8833 for (i = 0; i < tape->length; i++)
8835 if (tape->use_key_actions)
8837 for (j = 0; j < MAX_PLAYERS; j++)
8838 if (tape->player_participates[j])
8839 putFile8Bit(file, tape->pos[i].action[j]);
8842 if (tape->use_mouse_actions)
8844 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8845 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8846 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8849 putFile8Bit(file, tape->pos[i].delay);
8853 void SaveTapeToFilename(char *filename)
8857 int info_chunk_size;
8858 int body_chunk_size;
8860 if (!(file = fopen(filename, MODE_WRITE)))
8862 Warn("cannot save level recording file '%s'", filename);
8867 tape_pos_size = getTapePosSize(&tape);
8869 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8870 body_chunk_size = tape_pos_size * tape.length;
8872 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8873 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8875 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8876 SaveTape_VERS(file, &tape);
8878 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8879 SaveTape_HEAD(file, &tape);
8881 if (checkSaveTape_SCRN(&tape))
8883 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8884 SaveTape_SCRN(file, &tape);
8887 putFileChunkBE(file, "INFO", info_chunk_size);
8888 SaveTape_INFO(file, &tape);
8890 putFileChunkBE(file, "BODY", body_chunk_size);
8891 SaveTape_BODY(file, &tape);
8895 SetFilePermissions(filename, PERMS_PRIVATE);
8898 static void SaveTapeExt(char *filename)
8902 tape.file_version = FILE_VERSION_ACTUAL;
8903 tape.game_version = GAME_VERSION_ACTUAL;
8905 tape.num_participating_players = 0;
8907 // count number of participating players
8908 for (i = 0; i < MAX_PLAYERS; i++)
8909 if (tape.player_participates[i])
8910 tape.num_participating_players++;
8912 SaveTapeToFilename(filename);
8914 tape.changed = FALSE;
8917 void SaveTape(int nr)
8919 char *filename = getTapeFilename(nr);
8921 InitTapeDirectory(leveldir_current->subdir);
8923 SaveTapeExt(filename);
8926 void SaveScoreTape(int nr)
8928 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8930 // used instead of "leveldir_current->subdir" (for network games)
8931 InitScoreTapeDirectory(levelset.identifier, nr);
8933 SaveTapeExt(filename);
8936 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8937 unsigned int req_state_added)
8939 char *filename = getTapeFilename(nr);
8940 boolean new_tape = !fileExists(filename);
8941 boolean tape_saved = FALSE;
8943 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8948 Request(msg_saved, REQ_CONFIRM | req_state_added);
8956 boolean SaveTapeChecked(int nr)
8958 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8961 boolean SaveTapeChecked_LevelSolved(int nr)
8963 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8964 "Level solved! Tape saved!", REQ_STAY_OPEN);
8967 void DumpTape(struct TapeInfo *tape)
8969 int tape_frame_counter;
8972 if (tape->no_valid_file)
8974 Warn("cannot dump -- no valid tape file found");
8981 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8982 tape->level_nr, tape->file_version, tape->game_version);
8983 Print(" (effective engine version %08d)\n",
8984 tape->engine_version);
8985 Print("Level series identifier: '%s'\n", tape->level_identifier);
8987 Print("Solution tape: %s\n",
8988 tape->solved ? "yes" :
8989 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8991 Print("Special tape properties: ");
8992 if (tape->property_bits == TAPE_PROPERTY_NONE)
8994 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8995 Print("[em_random_bug]");
8996 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8997 Print("[game_speed]");
8998 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9000 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9001 Print("[single_step]");
9002 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9003 Print("[snapshot]");
9004 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9005 Print("[replayed]");
9006 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9007 Print("[tas_keys]");
9008 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9009 Print("[small_graphics]");
9012 int year2 = tape->date / 10000;
9013 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9014 int month_index_raw = (tape->date / 100) % 100;
9015 int month_index = month_index_raw % 12; // prevent invalid index
9016 int month = month_index + 1;
9017 int day = tape->date % 100;
9019 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9023 tape_frame_counter = 0;
9025 for (i = 0; i < tape->length; i++)
9027 if (i >= MAX_TAPE_LEN)
9032 for (j = 0; j < MAX_PLAYERS; j++)
9034 if (tape->player_participates[j])
9036 int action = tape->pos[i].action[j];
9038 Print("%d:%02x ", j, action);
9039 Print("[%c%c%c%c|%c%c] - ",
9040 (action & JOY_LEFT ? '<' : ' '),
9041 (action & JOY_RIGHT ? '>' : ' '),
9042 (action & JOY_UP ? '^' : ' '),
9043 (action & JOY_DOWN ? 'v' : ' '),
9044 (action & JOY_BUTTON_1 ? '1' : ' '),
9045 (action & JOY_BUTTON_2 ? '2' : ' '));
9049 Print("(%03d) ", tape->pos[i].delay);
9050 Print("[%05d]\n", tape_frame_counter);
9052 tape_frame_counter += tape->pos[i].delay;
9058 void DumpTapes(void)
9060 static LevelDirTree *dumptape_leveldir = NULL;
9062 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9063 global.dumptape_leveldir);
9065 if (dumptape_leveldir == NULL)
9066 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9068 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9069 global.dumptape_level_nr > dumptape_leveldir->last_level)
9070 Fail("no such level number: %d", global.dumptape_level_nr);
9072 leveldir_current = dumptape_leveldir;
9074 if (options.mytapes)
9075 LoadTape(global.dumptape_level_nr);
9077 LoadSolutionTape(global.dumptape_level_nr);
9085 // ============================================================================
9086 // score file functions
9087 // ============================================================================
9089 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9093 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9095 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9096 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9097 scores->entry[i].score = 0;
9098 scores->entry[i].time = 0;
9100 scores->entry[i].id = -1;
9101 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9102 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9103 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9104 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9105 strcpy(scores->entry[i].country_code, "??");
9108 scores->num_entries = 0;
9109 scores->last_added = -1;
9110 scores->last_added_local = -1;
9112 scores->updated = FALSE;
9113 scores->uploaded = FALSE;
9114 scores->tape_downloaded = FALSE;
9115 scores->force_last_added = FALSE;
9117 // The following values are intentionally not reset here:
9121 // - continue_playing
9122 // - continue_on_return
9125 static void setScoreInfoToDefaults(void)
9127 setScoreInfoToDefaultsExt(&scores);
9130 static void setServerScoreInfoToDefaults(void)
9132 setScoreInfoToDefaultsExt(&server_scores);
9135 static void LoadScore_OLD(int nr)
9138 char *filename = getScoreFilename(nr);
9139 char cookie[MAX_LINE_LEN];
9140 char line[MAX_LINE_LEN];
9144 if (!(file = fopen(filename, MODE_READ)))
9147 // check file identifier
9148 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9150 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9151 cookie[strlen(cookie) - 1] = '\0';
9153 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9155 Warn("unknown format of score file '%s'", filename);
9162 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9164 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9165 Warn("fscanf() failed; %s", strerror(errno));
9167 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9170 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9171 line[strlen(line) - 1] = '\0';
9173 for (line_ptr = line; *line_ptr; line_ptr++)
9175 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9177 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9178 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9187 static void ConvertScore_OLD(void)
9189 // only convert score to time for levels that rate playing time over score
9190 if (!level.rate_time_over_score)
9193 // convert old score to playing time for score-less levels (like Supaplex)
9194 int time_final_max = 999;
9197 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9199 int score = scores.entry[i].score;
9201 if (score > 0 && score < time_final_max)
9202 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9206 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9208 scores->file_version = getFileVersion(file);
9209 scores->game_version = getFileVersion(file);
9214 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9216 char *level_identifier = NULL;
9217 int level_identifier_size;
9220 level_identifier_size = getFile16BitBE(file);
9222 level_identifier = checked_malloc(level_identifier_size);
9224 for (i = 0; i < level_identifier_size; i++)
9225 level_identifier[i] = getFile8Bit(file);
9227 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9228 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9230 checked_free(level_identifier);
9232 scores->level_nr = getFile16BitBE(file);
9233 scores->num_entries = getFile16BitBE(file);
9235 chunk_size = 2 + level_identifier_size + 2 + 2;
9240 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9244 for (i = 0; i < scores->num_entries; i++)
9246 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9247 scores->entry[i].name[j] = getFile8Bit(file);
9249 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9252 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9257 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9261 for (i = 0; i < scores->num_entries; i++)
9262 scores->entry[i].score = getFile16BitBE(file);
9264 chunk_size = scores->num_entries * 2;
9269 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9273 for (i = 0; i < scores->num_entries; i++)
9274 scores->entry[i].score = getFile32BitBE(file);
9276 chunk_size = scores->num_entries * 4;
9281 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9285 for (i = 0; i < scores->num_entries; i++)
9286 scores->entry[i].time = getFile32BitBE(file);
9288 chunk_size = scores->num_entries * 4;
9293 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9297 for (i = 0; i < scores->num_entries; i++)
9299 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9300 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9302 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9305 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9310 void LoadScore(int nr)
9312 char *filename = getScoreFilename(nr);
9313 char cookie[MAX_LINE_LEN];
9314 char chunk_name[CHUNK_ID_LEN + 1];
9316 boolean old_score_file_format = FALSE;
9319 // always start with reliable default values
9320 setScoreInfoToDefaults();
9322 if (!(file = openFile(filename, MODE_READ)))
9325 getFileChunkBE(file, chunk_name, NULL);
9326 if (strEqual(chunk_name, "RND1"))
9328 getFile32BitBE(file); // not used
9330 getFileChunkBE(file, chunk_name, NULL);
9331 if (!strEqual(chunk_name, "SCOR"))
9333 Warn("unknown format of score file '%s'", filename);
9340 else // check for old file format with cookie string
9342 strcpy(cookie, chunk_name);
9343 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9345 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9346 cookie[strlen(cookie) - 1] = '\0';
9348 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9350 Warn("unknown format of score file '%s'", filename);
9357 old_score_file_format = TRUE;
9360 if (old_score_file_format)
9362 // score files from versions before 4.2.4.0 without chunk structure
9365 // convert score to time, if possible (mainly for Supaplex levels)
9374 int (*loader)(File *, int, struct ScoreInfo *);
9378 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9379 { "INFO", -1, LoadScore_INFO },
9380 { "NAME", -1, LoadScore_NAME },
9381 { "SCOR", -1, LoadScore_SCOR },
9382 { "SC4R", -1, LoadScore_SC4R },
9383 { "TIME", -1, LoadScore_TIME },
9384 { "TAPE", -1, LoadScore_TAPE },
9389 while (getFileChunkBE(file, chunk_name, &chunk_size))
9393 while (chunk_info[i].name != NULL &&
9394 !strEqual(chunk_name, chunk_info[i].name))
9397 if (chunk_info[i].name == NULL)
9399 Warn("unknown chunk '%s' in score file '%s'",
9400 chunk_name, filename);
9402 ReadUnusedBytesFromFile(file, chunk_size);
9404 else if (chunk_info[i].size != -1 &&
9405 chunk_info[i].size != chunk_size)
9407 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9408 chunk_size, chunk_name, filename);
9410 ReadUnusedBytesFromFile(file, chunk_size);
9414 // call function to load this score chunk
9415 int chunk_size_expected =
9416 (chunk_info[i].loader)(file, chunk_size, &scores);
9418 // the size of some chunks cannot be checked before reading other
9419 // chunks first (like "HEAD" and "BODY") that contain some header
9420 // information, so check them here
9421 if (chunk_size_expected != chunk_size)
9423 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9424 chunk_size, chunk_name, filename);
9433 #if ENABLE_HISTORIC_CHUNKS
9434 void SaveScore_OLD(int nr)
9437 char *filename = getScoreFilename(nr);
9440 // used instead of "leveldir_current->subdir" (for network games)
9441 InitScoreDirectory(levelset.identifier);
9443 if (!(file = fopen(filename, MODE_WRITE)))
9445 Warn("cannot save score for level %d", nr);
9450 fprintf(file, "%s\n\n", SCORE_COOKIE);
9452 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9453 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9457 SetFilePermissions(filename, PERMS_PRIVATE);
9461 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9463 putFileVersion(file, scores->file_version);
9464 putFileVersion(file, scores->game_version);
9467 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9469 int level_identifier_size = strlen(scores->level_identifier) + 1;
9472 putFile16BitBE(file, level_identifier_size);
9474 for (i = 0; i < level_identifier_size; i++)
9475 putFile8Bit(file, scores->level_identifier[i]);
9477 putFile16BitBE(file, scores->level_nr);
9478 putFile16BitBE(file, scores->num_entries);
9481 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9485 for (i = 0; i < scores->num_entries; i++)
9487 int name_size = strlen(scores->entry[i].name);
9489 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9490 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9494 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9498 for (i = 0; i < scores->num_entries; i++)
9499 putFile16BitBE(file, scores->entry[i].score);
9502 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9506 for (i = 0; i < scores->num_entries; i++)
9507 putFile32BitBE(file, scores->entry[i].score);
9510 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9514 for (i = 0; i < scores->num_entries; i++)
9515 putFile32BitBE(file, scores->entry[i].time);
9518 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9522 for (i = 0; i < scores->num_entries; i++)
9524 int size = strlen(scores->entry[i].tape_basename);
9526 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9527 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9531 static void SaveScoreToFilename(char *filename)
9534 int info_chunk_size;
9535 int name_chunk_size;
9536 int scor_chunk_size;
9537 int sc4r_chunk_size;
9538 int time_chunk_size;
9539 int tape_chunk_size;
9540 boolean has_large_score_values;
9543 if (!(file = fopen(filename, MODE_WRITE)))
9545 Warn("cannot save score file '%s'", filename);
9550 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9551 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9552 scor_chunk_size = scores.num_entries * 2;
9553 sc4r_chunk_size = scores.num_entries * 4;
9554 time_chunk_size = scores.num_entries * 4;
9555 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9557 has_large_score_values = FALSE;
9558 for (i = 0; i < scores.num_entries; i++)
9559 if (scores.entry[i].score > 0xffff)
9560 has_large_score_values = TRUE;
9562 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9563 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9565 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9566 SaveScore_VERS(file, &scores);
9568 putFileChunkBE(file, "INFO", info_chunk_size);
9569 SaveScore_INFO(file, &scores);
9571 putFileChunkBE(file, "NAME", name_chunk_size);
9572 SaveScore_NAME(file, &scores);
9574 if (has_large_score_values)
9576 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9577 SaveScore_SC4R(file, &scores);
9581 putFileChunkBE(file, "SCOR", scor_chunk_size);
9582 SaveScore_SCOR(file, &scores);
9585 putFileChunkBE(file, "TIME", time_chunk_size);
9586 SaveScore_TIME(file, &scores);
9588 putFileChunkBE(file, "TAPE", tape_chunk_size);
9589 SaveScore_TAPE(file, &scores);
9593 SetFilePermissions(filename, PERMS_PRIVATE);
9596 void SaveScore(int nr)
9598 char *filename = getScoreFilename(nr);
9601 // used instead of "leveldir_current->subdir" (for network games)
9602 InitScoreDirectory(levelset.identifier);
9604 scores.file_version = FILE_VERSION_ACTUAL;
9605 scores.game_version = GAME_VERSION_ACTUAL;
9607 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9608 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9609 scores.level_nr = level_nr;
9611 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9612 if (scores.entry[i].score == 0 &&
9613 scores.entry[i].time == 0 &&
9614 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9617 scores.num_entries = i;
9619 if (scores.num_entries == 0)
9622 SaveScoreToFilename(filename);
9625 static void LoadServerScoreFromCache(int nr)
9627 struct ScoreEntry score_entry;
9636 { &score_entry.score, FALSE, 0 },
9637 { &score_entry.time, FALSE, 0 },
9638 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9639 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9640 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9641 { &score_entry.id, FALSE, 0 },
9642 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9643 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9644 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9645 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9649 char *filename = getScoreCacheFilename(nr);
9650 SetupFileHash *score_hash = loadSetupFileHash(filename);
9653 server_scores.num_entries = 0;
9655 if (score_hash == NULL)
9658 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9660 score_entry = server_scores.entry[i];
9662 for (j = 0; score_mapping[j].value != NULL; j++)
9666 sprintf(token, "%02d.%d", i, j);
9668 char *value = getHashEntry(score_hash, token);
9673 if (score_mapping[j].is_string)
9675 char *score_value = (char *)score_mapping[j].value;
9676 int value_size = score_mapping[j].string_size;
9678 strncpy(score_value, value, value_size);
9679 score_value[value_size] = '\0';
9683 int *score_value = (int *)score_mapping[j].value;
9685 *score_value = atoi(value);
9688 server_scores.num_entries = i + 1;
9691 server_scores.entry[i] = score_entry;
9694 freeSetupFileHash(score_hash);
9697 void LoadServerScore(int nr, boolean download_score)
9699 if (!setup.use_api_server)
9702 // always start with reliable default values
9703 setServerScoreInfoToDefaults();
9705 // 1st step: load server scores from cache file (which may not exist)
9706 // (this should prevent reading it while the thread is writing to it)
9707 LoadServerScoreFromCache(nr);
9709 if (download_score && runtime.use_api_server)
9711 // 2nd step: download server scores from score server to cache file
9712 // (as thread, as it might time out if the server is not reachable)
9713 ApiGetScoreAsThread(nr);
9717 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9719 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9721 // if score tape not uploaded, ask for uploading missing tapes later
9722 if (!setup.has_remaining_tapes)
9723 setup.ask_for_remaining_tapes = TRUE;
9725 setup.provide_uploading_tapes = TRUE;
9726 setup.has_remaining_tapes = TRUE;
9728 SaveSetup_ServerSetup();
9731 void SaveServerScore(int nr, boolean tape_saved)
9733 if (!runtime.use_api_server)
9735 PrepareScoreTapesForUpload(leveldir_current->subdir);
9740 ApiAddScoreAsThread(nr, tape_saved, NULL);
9743 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9744 char *score_tape_filename)
9746 if (!runtime.use_api_server)
9749 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9752 void LoadLocalAndServerScore(int nr, boolean download_score)
9754 int last_added_local = scores.last_added_local;
9755 boolean force_last_added = scores.force_last_added;
9757 // needed if only showing server scores
9758 setScoreInfoToDefaults();
9760 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9763 // restore last added local score entry (before merging server scores)
9764 scores.last_added = scores.last_added_local = last_added_local;
9766 if (setup.use_api_server &&
9767 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9769 // load server scores from cache file and trigger update from server
9770 LoadServerScore(nr, download_score);
9772 // merge local scores with scores from server
9776 if (force_last_added)
9777 scores.force_last_added = force_last_added;
9781 // ============================================================================
9782 // setup file functions
9783 // ============================================================================
9785 #define TOKEN_STR_PLAYER_PREFIX "player_"
9788 static struct TokenInfo global_setup_tokens[] =
9792 &setup.player_name, "player_name"
9796 &setup.multiple_users, "multiple_users"
9800 &setup.sound, "sound"
9804 &setup.sound_loops, "repeating_sound_loops"
9808 &setup.sound_music, "background_music"
9812 &setup.sound_simple, "simple_sound_effects"
9816 &setup.toons, "toons"
9820 &setup.global_animations, "global_animations"
9824 &setup.scroll_delay, "scroll_delay"
9828 &setup.forced_scroll_delay, "forced_scroll_delay"
9832 &setup.scroll_delay_value, "scroll_delay_value"
9836 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9840 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9844 &setup.fade_screens, "fade_screens"
9848 &setup.autorecord, "automatic_tape_recording"
9852 &setup.autorecord_after_replay, "autorecord_after_replay"
9856 &setup.auto_pause_on_start, "auto_pause_on_start"
9860 &setup.show_titlescreen, "show_titlescreen"
9864 &setup.quick_doors, "quick_doors"
9868 &setup.team_mode, "team_mode"
9872 &setup.handicap, "handicap"
9876 &setup.skip_levels, "skip_levels"
9880 &setup.increment_levels, "increment_levels"
9884 &setup.auto_play_next_level, "auto_play_next_level"
9888 &setup.count_score_after_game, "count_score_after_game"
9892 &setup.show_scores_after_game, "show_scores_after_game"
9896 &setup.time_limit, "time_limit"
9900 &setup.fullscreen, "fullscreen"
9904 &setup.window_scaling_percent, "window_scaling_percent"
9908 &setup.window_scaling_quality, "window_scaling_quality"
9912 &setup.screen_rendering_mode, "screen_rendering_mode"
9916 &setup.vsync_mode, "vsync_mode"
9920 &setup.ask_on_escape, "ask_on_escape"
9924 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9928 &setup.ask_on_game_over, "ask_on_game_over"
9932 &setup.ask_on_quit_game, "ask_on_quit_game"
9936 &setup.ask_on_quit_program, "ask_on_quit_program"
9940 &setup.quick_switch, "quick_player_switch"
9944 &setup.input_on_focus, "input_on_focus"
9948 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9952 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9956 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9960 &setup.game_speed_extended, "game_speed_extended"
9964 &setup.game_frame_delay, "game_frame_delay"
9968 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9972 &setup.bd_skip_hatching, "bd_skip_hatching"
9976 &setup.bd_scroll_delay, "bd_scroll_delay"
9980 &setup.bd_smooth_movements, "bd_smooth_movements"
9984 &setup.sp_show_border_elements, "sp_show_border_elements"
9988 &setup.small_game_graphics, "small_game_graphics"
9992 &setup.show_load_save_buttons, "show_load_save_buttons"
9996 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10000 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10004 &setup.graphics_set, "graphics_set"
10008 &setup.sounds_set, "sounds_set"
10012 &setup.music_set, "music_set"
10016 &setup.override_level_graphics, "override_level_graphics"
10020 &setup.override_level_sounds, "override_level_sounds"
10024 &setup.override_level_music, "override_level_music"
10028 &setup.volume_simple, "volume_simple"
10032 &setup.volume_loops, "volume_loops"
10036 &setup.volume_music, "volume_music"
10040 &setup.network_mode, "network_mode"
10044 &setup.network_player_nr, "network_player"
10048 &setup.network_server_hostname, "network_server_hostname"
10052 &setup.touch.control_type, "touch.control_type"
10056 &setup.touch.move_distance, "touch.move_distance"
10060 &setup.touch.drop_distance, "touch.drop_distance"
10064 &setup.touch.transparency, "touch.transparency"
10068 &setup.touch.draw_outlined, "touch.draw_outlined"
10072 &setup.touch.draw_pressed, "touch.draw_pressed"
10076 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10080 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10084 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10088 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10092 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10096 static struct TokenInfo auto_setup_tokens[] =
10100 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10104 static struct TokenInfo server_setup_tokens[] =
10108 &setup.player_uuid, "player_uuid"
10112 &setup.player_version, "player_version"
10116 &setup.use_api_server, TEST_PREFIX "use_api_server"
10120 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10124 &setup.api_server_password, TEST_PREFIX "api_server_password"
10128 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10132 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10136 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10140 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10144 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10148 static struct TokenInfo editor_setup_tokens[] =
10152 &setup.editor.el_classic, "editor.el_classic"
10156 &setup.editor.el_custom, "editor.el_custom"
10160 &setup.editor.el_user_defined, "editor.el_user_defined"
10164 &setup.editor.el_dynamic, "editor.el_dynamic"
10168 &setup.editor.el_headlines, "editor.el_headlines"
10172 &setup.editor.show_element_token, "editor.show_element_token"
10176 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10180 static struct TokenInfo editor_cascade_setup_tokens[] =
10184 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10188 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10192 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10196 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10200 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10204 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10208 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10212 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10216 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10220 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10224 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10228 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10232 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10236 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10240 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10244 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10248 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10252 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10256 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10260 static struct TokenInfo shortcut_setup_tokens[] =
10264 &setup.shortcut.save_game, "shortcut.save_game"
10268 &setup.shortcut.load_game, "shortcut.load_game"
10272 &setup.shortcut.restart_game, "shortcut.restart_game"
10276 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10280 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10284 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10288 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10292 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10296 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10300 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10304 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10308 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10312 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10316 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10320 &setup.shortcut.tape_record, "shortcut.tape_record"
10324 &setup.shortcut.tape_play, "shortcut.tape_play"
10328 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10332 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10336 &setup.shortcut.sound_music, "shortcut.sound_music"
10340 &setup.shortcut.snap_left, "shortcut.snap_left"
10344 &setup.shortcut.snap_right, "shortcut.snap_right"
10348 &setup.shortcut.snap_up, "shortcut.snap_up"
10352 &setup.shortcut.snap_down, "shortcut.snap_down"
10356 static struct SetupInputInfo setup_input;
10357 static struct TokenInfo player_setup_tokens[] =
10361 &setup_input.use_joystick, ".use_joystick"
10365 &setup_input.joy.device_name, ".joy.device_name"
10369 &setup_input.joy.xleft, ".joy.xleft"
10373 &setup_input.joy.xmiddle, ".joy.xmiddle"
10377 &setup_input.joy.xright, ".joy.xright"
10381 &setup_input.joy.yupper, ".joy.yupper"
10385 &setup_input.joy.ymiddle, ".joy.ymiddle"
10389 &setup_input.joy.ylower, ".joy.ylower"
10393 &setup_input.joy.snap, ".joy.snap_field"
10397 &setup_input.joy.drop, ".joy.place_bomb"
10401 &setup_input.key.left, ".key.move_left"
10405 &setup_input.key.right, ".key.move_right"
10409 &setup_input.key.up, ".key.move_up"
10413 &setup_input.key.down, ".key.move_down"
10417 &setup_input.key.snap, ".key.snap_field"
10421 &setup_input.key.drop, ".key.place_bomb"
10425 static struct TokenInfo system_setup_tokens[] =
10429 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10433 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10437 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10441 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10445 static struct TokenInfo internal_setup_tokens[] =
10449 &setup.internal.program_title, "program_title"
10453 &setup.internal.program_version, "program_version"
10457 &setup.internal.program_author, "program_author"
10461 &setup.internal.program_email, "program_email"
10465 &setup.internal.program_website, "program_website"
10469 &setup.internal.program_copyright, "program_copyright"
10473 &setup.internal.program_company, "program_company"
10477 &setup.internal.program_icon_file, "program_icon_file"
10481 &setup.internal.default_graphics_set, "default_graphics_set"
10485 &setup.internal.default_sounds_set, "default_sounds_set"
10489 &setup.internal.default_music_set, "default_music_set"
10493 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10497 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10501 &setup.internal.fallback_music_file, "fallback_music_file"
10505 &setup.internal.default_level_series, "default_level_series"
10509 &setup.internal.default_window_width, "default_window_width"
10513 &setup.internal.default_window_height, "default_window_height"
10517 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10521 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10525 &setup.internal.create_user_levelset, "create_user_levelset"
10529 &setup.internal.info_screens_from_main, "info_screens_from_main"
10533 &setup.internal.menu_game, "menu_game"
10537 &setup.internal.menu_engines, "menu_engines"
10541 &setup.internal.menu_editor, "menu_editor"
10545 &setup.internal.menu_graphics, "menu_graphics"
10549 &setup.internal.menu_sound, "menu_sound"
10553 &setup.internal.menu_artwork, "menu_artwork"
10557 &setup.internal.menu_input, "menu_input"
10561 &setup.internal.menu_touch, "menu_touch"
10565 &setup.internal.menu_shortcuts, "menu_shortcuts"
10569 &setup.internal.menu_exit, "menu_exit"
10573 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10577 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10581 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10585 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10589 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10593 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10597 &setup.internal.info_title, "info_title"
10601 &setup.internal.info_elements, "info_elements"
10605 &setup.internal.info_music, "info_music"
10609 &setup.internal.info_credits, "info_credits"
10613 &setup.internal.info_program, "info_program"
10617 &setup.internal.info_version, "info_version"
10621 &setup.internal.info_levelset, "info_levelset"
10625 &setup.internal.info_exit, "info_exit"
10629 static struct TokenInfo debug_setup_tokens[] =
10633 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10637 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10641 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10645 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10649 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10653 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10657 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10661 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10665 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10669 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10673 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10677 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10681 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10685 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10689 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10693 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10697 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10701 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10705 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10709 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10713 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10716 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10720 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10724 &setup.debug.xsn_mode, "debug.xsn_mode"
10728 &setup.debug.xsn_percent, "debug.xsn_percent"
10732 static struct TokenInfo options_setup_tokens[] =
10736 &setup.options.verbose, "options.verbose"
10740 &setup.options.debug, "options.debug"
10744 &setup.options.debug_mode, "options.debug_mode"
10748 static void setSetupInfoToDefaults(struct SetupInfo *si)
10752 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10754 si->multiple_users = TRUE;
10757 si->sound_loops = TRUE;
10758 si->sound_music = TRUE;
10759 si->sound_simple = TRUE;
10761 si->global_animations = TRUE;
10762 si->scroll_delay = TRUE;
10763 si->forced_scroll_delay = FALSE;
10764 si->scroll_delay_value = STD_SCROLL_DELAY;
10765 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10766 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10767 si->fade_screens = TRUE;
10768 si->autorecord = TRUE;
10769 si->autorecord_after_replay = TRUE;
10770 si->auto_pause_on_start = FALSE;
10771 si->show_titlescreen = TRUE;
10772 si->quick_doors = FALSE;
10773 si->team_mode = FALSE;
10774 si->handicap = TRUE;
10775 si->skip_levels = TRUE;
10776 si->increment_levels = TRUE;
10777 si->auto_play_next_level = TRUE;
10778 si->count_score_after_game = TRUE;
10779 si->show_scores_after_game = TRUE;
10780 si->time_limit = TRUE;
10781 si->fullscreen = FALSE;
10782 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10783 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10784 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10785 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10786 si->ask_on_escape = TRUE;
10787 si->ask_on_escape_editor = TRUE;
10788 si->ask_on_game_over = TRUE;
10789 si->ask_on_quit_game = TRUE;
10790 si->ask_on_quit_program = TRUE;
10791 si->quick_switch = FALSE;
10792 si->input_on_focus = FALSE;
10793 si->prefer_aga_graphics = TRUE;
10794 si->prefer_lowpass_sounds = FALSE;
10795 si->prefer_extra_panel_items = TRUE;
10796 si->game_speed_extended = FALSE;
10797 si->game_frame_delay = GAME_FRAME_DELAY;
10798 si->bd_skip_uncovering = FALSE;
10799 si->bd_skip_hatching = FALSE;
10800 si->bd_scroll_delay = TRUE;
10801 si->bd_smooth_movements = AUTO;
10802 si->sp_show_border_elements = FALSE;
10803 si->small_game_graphics = FALSE;
10804 si->show_load_save_buttons = FALSE;
10805 si->show_undo_redo_buttons = FALSE;
10806 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10808 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10809 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10810 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10812 si->override_level_graphics = FALSE;
10813 si->override_level_sounds = FALSE;
10814 si->override_level_music = FALSE;
10816 si->volume_simple = 100; // percent
10817 si->volume_loops = 100; // percent
10818 si->volume_music = 100; // percent
10820 si->network_mode = FALSE;
10821 si->network_player_nr = 0; // first player
10822 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10824 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10825 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10826 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10827 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10828 si->touch.draw_outlined = TRUE;
10829 si->touch.draw_pressed = TRUE;
10831 for (i = 0; i < 2; i++)
10833 char *default_grid_button[6][2] =
10839 { "111222", " vv " },
10840 { "111222", " vv " }
10842 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10843 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10844 int min_xsize = MIN(6, grid_xsize);
10845 int min_ysize = MIN(6, grid_ysize);
10846 int startx = grid_xsize - min_xsize;
10847 int starty = grid_ysize - min_ysize;
10850 // virtual buttons grid can only be set to defaults if video is initialized
10851 // (this will be repeated if virtual buttons are not loaded from setup file)
10852 if (video.initialized)
10854 si->touch.grid_xsize[i] = grid_xsize;
10855 si->touch.grid_ysize[i] = grid_ysize;
10859 si->touch.grid_xsize[i] = -1;
10860 si->touch.grid_ysize[i] = -1;
10863 for (x = 0; x < MAX_GRID_XSIZE; x++)
10864 for (y = 0; y < MAX_GRID_YSIZE; y++)
10865 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10867 for (x = 0; x < min_xsize; x++)
10868 for (y = 0; y < min_ysize; y++)
10869 si->touch.grid_button[i][x][starty + y] =
10870 default_grid_button[y][0][x];
10872 for (x = 0; x < min_xsize; x++)
10873 for (y = 0; y < min_ysize; y++)
10874 si->touch.grid_button[i][startx + x][starty + y] =
10875 default_grid_button[y][1][x];
10878 si->touch.grid_initialized = video.initialized;
10880 si->touch.overlay_buttons = FALSE;
10882 si->editor.el_boulderdash = TRUE;
10883 si->editor.el_boulderdash_native = TRUE;
10884 si->editor.el_emerald_mine = TRUE;
10885 si->editor.el_emerald_mine_club = TRUE;
10886 si->editor.el_more = TRUE;
10887 si->editor.el_sokoban = TRUE;
10888 si->editor.el_supaplex = TRUE;
10889 si->editor.el_diamond_caves = TRUE;
10890 si->editor.el_dx_boulderdash = TRUE;
10892 si->editor.el_mirror_magic = TRUE;
10893 si->editor.el_deflektor = TRUE;
10895 si->editor.el_chars = TRUE;
10896 si->editor.el_steel_chars = TRUE;
10898 si->editor.el_classic = TRUE;
10899 si->editor.el_custom = TRUE;
10901 si->editor.el_user_defined = FALSE;
10902 si->editor.el_dynamic = TRUE;
10904 si->editor.el_headlines = TRUE;
10906 si->editor.show_element_token = FALSE;
10908 si->editor.show_read_only_warning = TRUE;
10910 si->editor.use_template_for_new_levels = TRUE;
10912 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10913 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10914 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10915 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10916 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10918 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10919 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10920 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10921 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10922 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10924 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10925 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10926 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10927 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10928 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10929 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10931 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10932 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10933 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10935 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10936 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10937 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10938 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10940 for (i = 0; i < MAX_PLAYERS; i++)
10942 si->input[i].use_joystick = FALSE;
10943 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10944 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10945 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10946 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10947 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10948 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10949 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10950 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10951 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10952 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10953 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10954 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10955 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10956 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10957 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10960 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10961 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10962 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10963 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10965 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10966 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10967 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10968 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10969 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10970 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10971 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10973 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10975 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10976 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10977 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10979 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10980 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10981 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10983 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10984 si->internal.choose_from_top_leveldir = FALSE;
10985 si->internal.show_scaling_in_title = TRUE;
10986 si->internal.create_user_levelset = TRUE;
10987 si->internal.info_screens_from_main = FALSE;
10989 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10990 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10992 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10993 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10994 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10995 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10996 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10997 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10998 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10999 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11000 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11001 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11003 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11004 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11005 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11006 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11007 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11008 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11009 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11010 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11011 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11012 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11014 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11015 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11017 si->debug.show_frames_per_second = FALSE;
11019 si->debug.xsn_mode = AUTO;
11020 si->debug.xsn_percent = 0;
11022 si->options.verbose = FALSE;
11023 si->options.debug = FALSE;
11024 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11026 #if defined(PLATFORM_ANDROID)
11027 si->fullscreen = TRUE;
11028 si->touch.overlay_buttons = TRUE;
11031 setHideSetupEntry(&setup.debug.xsn_mode);
11034 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11036 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11039 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11041 si->player_uuid = NULL; // (will be set later)
11042 si->player_version = 1; // (will be set later)
11044 si->use_api_server = TRUE;
11045 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11046 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11047 si->ask_for_uploading_tapes = TRUE;
11048 si->ask_for_remaining_tapes = FALSE;
11049 si->provide_uploading_tapes = TRUE;
11050 si->ask_for_using_api_server = TRUE;
11051 si->has_remaining_tapes = FALSE;
11054 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11056 si->editor_cascade.el_bd = TRUE;
11057 si->editor_cascade.el_bd_native = TRUE;
11058 si->editor_cascade.el_em = TRUE;
11059 si->editor_cascade.el_emc = TRUE;
11060 si->editor_cascade.el_rnd = TRUE;
11061 si->editor_cascade.el_sb = TRUE;
11062 si->editor_cascade.el_sp = TRUE;
11063 si->editor_cascade.el_dc = TRUE;
11064 si->editor_cascade.el_dx = TRUE;
11066 si->editor_cascade.el_mm = TRUE;
11067 si->editor_cascade.el_df = TRUE;
11069 si->editor_cascade.el_chars = FALSE;
11070 si->editor_cascade.el_steel_chars = FALSE;
11071 si->editor_cascade.el_ce = FALSE;
11072 si->editor_cascade.el_ge = FALSE;
11073 si->editor_cascade.el_es = FALSE;
11074 si->editor_cascade.el_ref = FALSE;
11075 si->editor_cascade.el_user = FALSE;
11076 si->editor_cascade.el_dynamic = FALSE;
11079 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11081 static char *getHideSetupToken(void *setup_value)
11083 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11085 if (setup_value != NULL)
11086 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11088 return hide_setup_token;
11091 void setHideSetupEntry(void *setup_value)
11093 char *hide_setup_token = getHideSetupToken(setup_value);
11095 if (hide_setup_hash == NULL)
11096 hide_setup_hash = newSetupFileHash();
11098 if (setup_value != NULL)
11099 setHashEntry(hide_setup_hash, hide_setup_token, "");
11102 void removeHideSetupEntry(void *setup_value)
11104 char *hide_setup_token = getHideSetupToken(setup_value);
11106 if (setup_value != NULL)
11107 removeHashEntry(hide_setup_hash, hide_setup_token);
11110 boolean hideSetupEntry(void *setup_value)
11112 char *hide_setup_token = getHideSetupToken(setup_value);
11114 return (setup_value != NULL &&
11115 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11118 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11119 struct TokenInfo *token_info,
11120 int token_nr, char *token_text)
11122 char *token_hide_text = getStringCat2(token_text, ".hide");
11123 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11125 // set the value of this setup option in the setup option structure
11126 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11128 // check if this setup option should be hidden in the setup menu
11129 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11130 setHideSetupEntry(token_info[token_nr].value);
11132 free(token_hide_text);
11135 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11136 struct TokenInfo *token_info,
11139 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11140 token_info[token_nr].text);
11143 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11147 if (!setup_file_hash)
11150 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11151 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11153 setup.touch.grid_initialized = TRUE;
11154 for (i = 0; i < 2; i++)
11156 int grid_xsize = setup.touch.grid_xsize[i];
11157 int grid_ysize = setup.touch.grid_ysize[i];
11160 // if virtual buttons are not loaded from setup file, repeat initializing
11161 // virtual buttons grid with default values later when video is initialized
11162 if (grid_xsize == -1 ||
11165 setup.touch.grid_initialized = FALSE;
11170 for (y = 0; y < grid_ysize; y++)
11172 char token_string[MAX_LINE_LEN];
11174 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11176 char *value_string = getHashEntry(setup_file_hash, token_string);
11178 if (value_string == NULL)
11181 for (x = 0; x < grid_xsize; x++)
11183 char c = value_string[x];
11185 setup.touch.grid_button[i][x][y] =
11186 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11191 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11192 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11194 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11195 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11197 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11201 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11203 setup_input = setup.input[pnr];
11204 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11206 char full_token[100];
11208 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11209 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11212 setup.input[pnr] = setup_input;
11215 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11216 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11218 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11219 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11221 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11222 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11224 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11225 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11227 setHideRelatedSetupEntries();
11230 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11234 if (!setup_file_hash)
11237 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11238 setSetupInfo(auto_setup_tokens, i,
11239 getHashEntry(setup_file_hash,
11240 auto_setup_tokens[i].text));
11243 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11247 if (!setup_file_hash)
11250 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11251 setSetupInfo(server_setup_tokens, i,
11252 getHashEntry(setup_file_hash,
11253 server_setup_tokens[i].text));
11256 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11260 if (!setup_file_hash)
11263 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11264 setSetupInfo(editor_cascade_setup_tokens, i,
11265 getHashEntry(setup_file_hash,
11266 editor_cascade_setup_tokens[i].text));
11269 void LoadUserNames(void)
11271 int last_user_nr = user.nr;
11274 if (global.user_names != NULL)
11276 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11277 checked_free(global.user_names[i]);
11279 checked_free(global.user_names);
11282 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11284 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11288 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11290 if (setup_file_hash)
11292 char *player_name = getHashEntry(setup_file_hash, "player_name");
11294 global.user_names[i] = getFixedUserName(player_name);
11296 freeSetupFileHash(setup_file_hash);
11299 if (global.user_names[i] == NULL)
11300 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11303 user.nr = last_user_nr;
11306 void LoadSetupFromFilename(char *filename)
11308 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11310 if (setup_file_hash)
11312 decodeSetupFileHash_Default(setup_file_hash);
11314 freeSetupFileHash(setup_file_hash);
11318 Debug("setup", "using default setup values");
11322 static void LoadSetup_SpecialPostProcessing(void)
11324 char *player_name_new;
11326 // needed to work around problems with fixed length strings
11327 player_name_new = getFixedUserName(setup.player_name);
11328 free(setup.player_name);
11329 setup.player_name = player_name_new;
11331 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11332 if (setup.scroll_delay == FALSE)
11334 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11335 setup.scroll_delay = TRUE; // now always "on"
11338 // make sure that scroll delay value stays inside valid range
11339 setup.scroll_delay_value =
11340 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11343 void LoadSetup_Default(void)
11347 // always start with reliable default values
11348 setSetupInfoToDefaults(&setup);
11350 // try to load setup values from default setup file
11351 filename = getDefaultSetupFilename();
11353 if (fileExists(filename))
11354 LoadSetupFromFilename(filename);
11356 // try to load setup values from platform setup file
11357 filename = getPlatformSetupFilename();
11359 if (fileExists(filename))
11360 LoadSetupFromFilename(filename);
11362 // try to load setup values from user setup file
11363 filename = getSetupFilename();
11365 LoadSetupFromFilename(filename);
11367 LoadSetup_SpecialPostProcessing();
11370 void LoadSetup_AutoSetup(void)
11372 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11373 SetupFileHash *setup_file_hash = NULL;
11375 // always start with reliable default values
11376 setSetupInfoToDefaults_AutoSetup(&setup);
11378 setup_file_hash = loadSetupFileHash(filename);
11380 if (setup_file_hash)
11382 decodeSetupFileHash_AutoSetup(setup_file_hash);
11384 freeSetupFileHash(setup_file_hash);
11390 void LoadSetup_ServerSetup(void)
11392 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11393 SetupFileHash *setup_file_hash = NULL;
11395 // always start with reliable default values
11396 setSetupInfoToDefaults_ServerSetup(&setup);
11398 setup_file_hash = loadSetupFileHash(filename);
11400 if (setup_file_hash)
11402 decodeSetupFileHash_ServerSetup(setup_file_hash);
11404 freeSetupFileHash(setup_file_hash);
11409 if (setup.player_uuid == NULL)
11411 // player UUID does not yet exist in setup file
11412 setup.player_uuid = getStringCopy(getUUID());
11413 setup.player_version = 2;
11415 SaveSetup_ServerSetup();
11419 void LoadSetup_EditorCascade(void)
11421 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11422 SetupFileHash *setup_file_hash = NULL;
11424 // always start with reliable default values
11425 setSetupInfoToDefaults_EditorCascade(&setup);
11427 setup_file_hash = loadSetupFileHash(filename);
11429 if (setup_file_hash)
11431 decodeSetupFileHash_EditorCascade(setup_file_hash);
11433 freeSetupFileHash(setup_file_hash);
11439 void LoadSetup(void)
11441 LoadSetup_Default();
11442 LoadSetup_AutoSetup();
11443 LoadSetup_ServerSetup();
11444 LoadSetup_EditorCascade();
11447 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11448 char *mapping_line)
11450 char mapping_guid[MAX_LINE_LEN];
11451 char *mapping_start, *mapping_end;
11453 // get GUID from game controller mapping line: copy complete line
11454 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11455 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11457 // get GUID from game controller mapping line: cut after GUID part
11458 mapping_start = strchr(mapping_guid, ',');
11459 if (mapping_start != NULL)
11460 *mapping_start = '\0';
11462 // cut newline from game controller mapping line
11463 mapping_end = strchr(mapping_line, '\n');
11464 if (mapping_end != NULL)
11465 *mapping_end = '\0';
11467 // add mapping entry to game controller mappings hash
11468 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11471 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11476 if (!(file = fopen(filename, MODE_READ)))
11478 Warn("cannot read game controller mappings file '%s'", filename);
11483 while (!feof(file))
11485 char line[MAX_LINE_LEN];
11487 if (!fgets(line, MAX_LINE_LEN, file))
11490 addGameControllerMappingToHash(mappings_hash, line);
11496 void SaveSetup_Default(void)
11498 char *filename = getSetupFilename();
11502 InitUserDataDirectory();
11504 if (!(file = fopen(filename, MODE_WRITE)))
11506 Warn("cannot write setup file '%s'", filename);
11511 fprintFileHeader(file, SETUP_FILENAME);
11513 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11515 // just to make things nicer :)
11516 if (global_setup_tokens[i].value == &setup.multiple_users ||
11517 global_setup_tokens[i].value == &setup.sound ||
11518 global_setup_tokens[i].value == &setup.graphics_set ||
11519 global_setup_tokens[i].value == &setup.volume_simple ||
11520 global_setup_tokens[i].value == &setup.network_mode ||
11521 global_setup_tokens[i].value == &setup.touch.control_type ||
11522 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11523 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11524 fprintf(file, "\n");
11526 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11529 for (i = 0; i < 2; i++)
11531 int grid_xsize = setup.touch.grid_xsize[i];
11532 int grid_ysize = setup.touch.grid_ysize[i];
11535 fprintf(file, "\n");
11537 for (y = 0; y < grid_ysize; y++)
11539 char token_string[MAX_LINE_LEN];
11540 char value_string[MAX_LINE_LEN];
11542 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11544 for (x = 0; x < grid_xsize; x++)
11546 char c = setup.touch.grid_button[i][x][y];
11548 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11551 value_string[grid_xsize] = '\0';
11553 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11557 fprintf(file, "\n");
11558 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11559 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11561 fprintf(file, "\n");
11562 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11563 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11565 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11569 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11570 fprintf(file, "\n");
11572 setup_input = setup.input[pnr];
11573 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11574 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11577 fprintf(file, "\n");
11578 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11579 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11581 // (internal setup values not saved to user setup file)
11583 fprintf(file, "\n");
11584 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11585 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11586 setup.debug.xsn_mode != AUTO)
11587 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11589 fprintf(file, "\n");
11590 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11591 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11595 SetFilePermissions(filename, PERMS_PRIVATE);
11598 void SaveSetup_AutoSetup(void)
11600 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11604 InitUserDataDirectory();
11606 if (!(file = fopen(filename, MODE_WRITE)))
11608 Warn("cannot write auto setup file '%s'", filename);
11615 fprintFileHeader(file, AUTOSETUP_FILENAME);
11617 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11618 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11622 SetFilePermissions(filename, PERMS_PRIVATE);
11627 void SaveSetup_ServerSetup(void)
11629 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11633 InitUserDataDirectory();
11635 if (!(file = fopen(filename, MODE_WRITE)))
11637 Warn("cannot write server setup file '%s'", filename);
11644 fprintFileHeader(file, SERVERSETUP_FILENAME);
11646 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11648 // just to make things nicer :)
11649 if (server_setup_tokens[i].value == &setup.use_api_server)
11650 fprintf(file, "\n");
11652 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11657 SetFilePermissions(filename, PERMS_PRIVATE);
11662 void SaveSetup_EditorCascade(void)
11664 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11668 InitUserDataDirectory();
11670 if (!(file = fopen(filename, MODE_WRITE)))
11672 Warn("cannot write editor cascade state file '%s'", filename);
11679 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11681 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11682 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11686 SetFilePermissions(filename, PERMS_PRIVATE);
11691 void SaveSetup(void)
11693 SaveSetup_Default();
11694 SaveSetup_AutoSetup();
11695 SaveSetup_ServerSetup();
11696 SaveSetup_EditorCascade();
11699 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11704 if (!(file = fopen(filename, MODE_WRITE)))
11706 Warn("cannot write game controller mappings file '%s'", filename);
11711 BEGIN_HASH_ITERATION(mappings_hash, itr)
11713 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11715 END_HASH_ITERATION(mappings_hash, itr)
11720 void SaveSetup_AddGameControllerMapping(char *mapping)
11722 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11723 SetupFileHash *mappings_hash = newSetupFileHash();
11725 InitUserDataDirectory();
11727 // load existing personal game controller mappings
11728 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11730 // add new mapping to personal game controller mappings
11731 addGameControllerMappingToHash(mappings_hash, mapping);
11733 // save updated personal game controller mappings
11734 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11736 freeSetupFileHash(mappings_hash);
11740 void LoadCustomElementDescriptions(void)
11742 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11743 SetupFileHash *setup_file_hash;
11746 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11748 if (element_info[i].custom_description != NULL)
11750 free(element_info[i].custom_description);
11751 element_info[i].custom_description = NULL;
11755 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11758 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11760 char *token = getStringCat2(element_info[i].token_name, ".name");
11761 char *value = getHashEntry(setup_file_hash, token);
11764 element_info[i].custom_description = getStringCopy(value);
11769 freeSetupFileHash(setup_file_hash);
11772 static int getElementFromToken(char *token)
11774 char *value = getHashEntry(element_token_hash, token);
11777 return atoi(value);
11779 Warn("unknown element token '%s'", token);
11781 return EL_UNDEFINED;
11784 void FreeGlobalAnimEventInfo(void)
11786 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11788 if (gaei->event_list == NULL)
11793 for (i = 0; i < gaei->num_event_lists; i++)
11795 checked_free(gaei->event_list[i]->event_value);
11796 checked_free(gaei->event_list[i]);
11799 checked_free(gaei->event_list);
11801 gaei->event_list = NULL;
11802 gaei->num_event_lists = 0;
11805 static int AddGlobalAnimEventList(void)
11807 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11808 int list_pos = gaei->num_event_lists++;
11810 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11811 sizeof(struct GlobalAnimEventListInfo *));
11813 gaei->event_list[list_pos] =
11814 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11816 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11818 gaeli->event_value = NULL;
11819 gaeli->num_event_values = 0;
11824 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11826 // do not add empty global animation events
11827 if (event_value == ANIM_EVENT_NONE)
11830 // if list position is undefined, create new list
11831 if (list_pos == ANIM_EVENT_UNDEFINED)
11832 list_pos = AddGlobalAnimEventList();
11834 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11835 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11836 int value_pos = gaeli->num_event_values++;
11838 gaeli->event_value = checked_realloc(gaeli->event_value,
11839 gaeli->num_event_values * sizeof(int *));
11841 gaeli->event_value[value_pos] = event_value;
11846 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11848 if (list_pos == ANIM_EVENT_UNDEFINED)
11849 return ANIM_EVENT_NONE;
11851 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11852 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11854 return gaeli->event_value[value_pos];
11857 int GetGlobalAnimEventValueCount(int list_pos)
11859 if (list_pos == ANIM_EVENT_UNDEFINED)
11862 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11863 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11865 return gaeli->num_event_values;
11868 // This function checks if a string <s> of the format "string1, string2, ..."
11869 // exactly contains a string <s_contained>.
11871 static boolean string_has_parameter(char *s, char *s_contained)
11875 if (s == NULL || s_contained == NULL)
11878 if (strlen(s_contained) > strlen(s))
11881 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11883 char next_char = s[strlen(s_contained)];
11885 // check if next character is delimiter or whitespace
11886 if (next_char == ',' || next_char == '\0' ||
11887 next_char == ' ' || next_char == '\t')
11891 // check if string contains another parameter string after a comma
11892 substring = strchr(s, ',');
11893 if (substring == NULL) // string does not contain a comma
11896 // advance string pointer to next character after the comma
11899 // skip potential whitespaces after the comma
11900 while (*substring == ' ' || *substring == '\t')
11903 return string_has_parameter(substring, s_contained);
11906 static int get_anim_parameter_value_ce(char *s)
11909 char *pattern_1 = "ce_change:custom_";
11910 char *pattern_2 = ".page_";
11911 int pattern_1_len = strlen(pattern_1);
11912 char *matching_char = strstr(s_ptr, pattern_1);
11913 int result = ANIM_EVENT_NONE;
11915 if (matching_char == NULL)
11916 return ANIM_EVENT_NONE;
11918 result = ANIM_EVENT_CE_CHANGE;
11920 s_ptr = matching_char + pattern_1_len;
11922 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11923 if (*s_ptr >= '0' && *s_ptr <= '9')
11925 int gic_ce_nr = (*s_ptr++ - '0');
11927 if (*s_ptr >= '0' && *s_ptr <= '9')
11929 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11931 if (*s_ptr >= '0' && *s_ptr <= '9')
11932 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11935 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11936 return ANIM_EVENT_NONE;
11938 // custom element stored as 0 to 255
11941 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11945 // invalid custom element number specified
11947 return ANIM_EVENT_NONE;
11950 // check for change page number ("page_X" or "page_XX") (optional)
11951 if (strPrefix(s_ptr, pattern_2))
11953 s_ptr += strlen(pattern_2);
11955 if (*s_ptr >= '0' && *s_ptr <= '9')
11957 int gic_page_nr = (*s_ptr++ - '0');
11959 if (*s_ptr >= '0' && *s_ptr <= '9')
11960 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11962 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11963 return ANIM_EVENT_NONE;
11965 // change page stored as 1 to 32 (0 means "all change pages")
11967 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11971 // invalid animation part number specified
11973 return ANIM_EVENT_NONE;
11977 // discard result if next character is neither delimiter nor whitespace
11978 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11979 *s_ptr == ' ' || *s_ptr == '\t'))
11980 return ANIM_EVENT_NONE;
11985 static int get_anim_parameter_value(char *s)
11987 int event_value[] =
11995 char *pattern_1[] =
12003 char *pattern_2 = ".part_";
12004 char *matching_char = NULL;
12006 int pattern_1_len = 0;
12007 int result = ANIM_EVENT_NONE;
12010 result = get_anim_parameter_value_ce(s);
12012 if (result != ANIM_EVENT_NONE)
12015 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12017 matching_char = strstr(s_ptr, pattern_1[i]);
12018 pattern_1_len = strlen(pattern_1[i]);
12019 result = event_value[i];
12021 if (matching_char != NULL)
12025 if (matching_char == NULL)
12026 return ANIM_EVENT_NONE;
12028 s_ptr = matching_char + pattern_1_len;
12030 // check for main animation number ("anim_X" or "anim_XX")
12031 if (*s_ptr >= '0' && *s_ptr <= '9')
12033 int gic_anim_nr = (*s_ptr++ - '0');
12035 if (*s_ptr >= '0' && *s_ptr <= '9')
12036 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12038 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12039 return ANIM_EVENT_NONE;
12041 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12045 // invalid main animation number specified
12047 return ANIM_EVENT_NONE;
12050 // check for animation part number ("part_X" or "part_XX") (optional)
12051 if (strPrefix(s_ptr, pattern_2))
12053 s_ptr += strlen(pattern_2);
12055 if (*s_ptr >= '0' && *s_ptr <= '9')
12057 int gic_part_nr = (*s_ptr++ - '0');
12059 if (*s_ptr >= '0' && *s_ptr <= '9')
12060 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12062 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12063 return ANIM_EVENT_NONE;
12065 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12069 // invalid animation part number specified
12071 return ANIM_EVENT_NONE;
12075 // discard result if next character is neither delimiter nor whitespace
12076 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12077 *s_ptr == ' ' || *s_ptr == '\t'))
12078 return ANIM_EVENT_NONE;
12083 static int get_anim_parameter_values(char *s)
12085 int list_pos = ANIM_EVENT_UNDEFINED;
12086 int event_value = ANIM_EVENT_DEFAULT;
12088 if (string_has_parameter(s, "any"))
12089 event_value |= ANIM_EVENT_ANY;
12091 if (string_has_parameter(s, "click:self") ||
12092 string_has_parameter(s, "click") ||
12093 string_has_parameter(s, "self"))
12094 event_value |= ANIM_EVENT_SELF;
12096 if (string_has_parameter(s, "unclick:any"))
12097 event_value |= ANIM_EVENT_UNCLICK_ANY;
12099 // if animation event found, add it to global animation event list
12100 if (event_value != ANIM_EVENT_NONE)
12101 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12105 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12106 event_value = get_anim_parameter_value(s);
12108 // if animation event found, add it to global animation event list
12109 if (event_value != ANIM_EVENT_NONE)
12110 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12112 // continue with next part of the string, starting with next comma
12113 s = strchr(s + 1, ',');
12119 static int get_anim_action_parameter_value(char *token)
12121 // check most common default case first to massively speed things up
12122 if (strEqual(token, ARG_UNDEFINED))
12123 return ANIM_EVENT_ACTION_NONE;
12125 int result = getImageIDFromToken(token);
12129 char *gfx_token = getStringCat2("gfx.", token);
12131 result = getImageIDFromToken(gfx_token);
12133 checked_free(gfx_token);
12138 Key key = getKeyFromX11KeyName(token);
12140 if (key != KSYM_UNDEFINED)
12141 result = -(int)key;
12148 result = get_hash_from_string(token); // unsigned int => int
12149 result = ABS(result); // may be negative now
12150 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12152 setHashEntry(anim_url_hash, int2str(result, 0), token);
12157 result = ANIM_EVENT_ACTION_NONE;
12162 int get_parameter_value(char *value_raw, char *suffix, int type)
12164 char *value = getStringToLower(value_raw);
12165 int result = 0; // probably a save default value
12167 if (strEqual(suffix, ".direction"))
12169 result = (strEqual(value, "left") ? MV_LEFT :
12170 strEqual(value, "right") ? MV_RIGHT :
12171 strEqual(value, "up") ? MV_UP :
12172 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12174 else if (strEqual(suffix, ".position"))
12176 result = (strEqual(value, "left") ? POS_LEFT :
12177 strEqual(value, "right") ? POS_RIGHT :
12178 strEqual(value, "top") ? POS_TOP :
12179 strEqual(value, "upper") ? POS_UPPER :
12180 strEqual(value, "middle") ? POS_MIDDLE :
12181 strEqual(value, "lower") ? POS_LOWER :
12182 strEqual(value, "bottom") ? POS_BOTTOM :
12183 strEqual(value, "any") ? POS_ANY :
12184 strEqual(value, "ce") ? POS_CE :
12185 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12186 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12188 else if (strEqual(suffix, ".align"))
12190 result = (strEqual(value, "left") ? ALIGN_LEFT :
12191 strEqual(value, "right") ? ALIGN_RIGHT :
12192 strEqual(value, "center") ? ALIGN_CENTER :
12193 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12195 else if (strEqual(suffix, ".valign"))
12197 result = (strEqual(value, "top") ? VALIGN_TOP :
12198 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12199 strEqual(value, "middle") ? VALIGN_MIDDLE :
12200 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12202 else if (strEqual(suffix, ".anim_mode"))
12204 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12205 string_has_parameter(value, "loop") ? ANIM_LOOP :
12206 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12207 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12208 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12209 string_has_parameter(value, "random") ? ANIM_RANDOM :
12210 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12211 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12212 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12213 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12214 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12215 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12216 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12217 string_has_parameter(value, "all") ? ANIM_ALL :
12218 string_has_parameter(value, "tiled") ? ANIM_TILED :
12219 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12222 if (string_has_parameter(value, "once"))
12223 result |= ANIM_ONCE;
12225 if (string_has_parameter(value, "reverse"))
12226 result |= ANIM_REVERSE;
12228 if (string_has_parameter(value, "opaque_player"))
12229 result |= ANIM_OPAQUE_PLAYER;
12231 if (string_has_parameter(value, "static_panel"))
12232 result |= ANIM_STATIC_PANEL;
12234 else if (strEqual(suffix, ".init_event") ||
12235 strEqual(suffix, ".anim_event"))
12237 result = get_anim_parameter_values(value);
12239 else if (strEqual(suffix, ".init_delay_action") ||
12240 strEqual(suffix, ".anim_delay_action") ||
12241 strEqual(suffix, ".post_delay_action") ||
12242 strEqual(suffix, ".init_event_action") ||
12243 strEqual(suffix, ".anim_event_action"))
12245 result = get_anim_action_parameter_value(value_raw);
12247 else if (strEqual(suffix, ".class"))
12249 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12250 get_hash_from_string(value));
12252 else if (strEqual(suffix, ".style"))
12254 result = STYLE_DEFAULT;
12256 if (string_has_parameter(value, "accurate_borders"))
12257 result |= STYLE_ACCURATE_BORDERS;
12259 if (string_has_parameter(value, "inner_corners"))
12260 result |= STYLE_INNER_CORNERS;
12262 if (string_has_parameter(value, "reverse"))
12263 result |= STYLE_REVERSE;
12265 if (string_has_parameter(value, "leftmost_position"))
12266 result |= STYLE_LEFTMOST_POSITION;
12268 if (string_has_parameter(value, "block_clicks"))
12269 result |= STYLE_BLOCK;
12271 if (string_has_parameter(value, "passthrough_clicks"))
12272 result |= STYLE_PASSTHROUGH;
12274 if (string_has_parameter(value, "multiple_actions"))
12275 result |= STYLE_MULTIPLE_ACTIONS;
12277 if (string_has_parameter(value, "consume_ce_event"))
12278 result |= STYLE_CONSUME_CE_EVENT;
12280 else if (strEqual(suffix, ".fade_mode"))
12282 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12283 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12284 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12285 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12286 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12287 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12288 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12289 FADE_MODE_DEFAULT);
12291 else if (strEqual(suffix, ".auto_delay_unit"))
12293 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12294 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12295 AUTO_DELAY_UNIT_DEFAULT);
12297 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12299 result = gfx.get_font_from_token_function(value);
12301 else // generic parameter of type integer or boolean
12303 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12304 type == TYPE_INTEGER ? get_integer_from_string(value) :
12305 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12306 ARG_UNDEFINED_VALUE);
12314 static int get_token_parameter_value(char *token, char *value_raw)
12318 if (token == NULL || value_raw == NULL)
12319 return ARG_UNDEFINED_VALUE;
12321 suffix = strrchr(token, '.');
12322 if (suffix == NULL)
12325 if (strEqual(suffix, ".element"))
12326 return getElementFromToken(value_raw);
12328 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12329 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12332 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12333 boolean ignore_defaults)
12337 for (i = 0; image_config_vars[i].token != NULL; i++)
12339 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12341 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12342 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12346 *image_config_vars[i].value =
12347 get_token_parameter_value(image_config_vars[i].token, value);
12351 void InitMenuDesignSettings_Static(void)
12353 // always start with reliable default values from static default config
12354 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12357 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12361 // the following initializes hierarchical values from static configuration
12363 // special case: initialize "ARG_DEFAULT" values in static default config
12364 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12365 titlescreen_initial_first_default.fade_mode =
12366 title_initial_first_default.fade_mode;
12367 titlescreen_initial_first_default.fade_delay =
12368 title_initial_first_default.fade_delay;
12369 titlescreen_initial_first_default.post_delay =
12370 title_initial_first_default.post_delay;
12371 titlescreen_initial_first_default.auto_delay =
12372 title_initial_first_default.auto_delay;
12373 titlescreen_initial_first_default.auto_delay_unit =
12374 title_initial_first_default.auto_delay_unit;
12375 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12376 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12377 titlescreen_first_default.post_delay = title_first_default.post_delay;
12378 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12379 titlescreen_first_default.auto_delay_unit =
12380 title_first_default.auto_delay_unit;
12381 titlemessage_initial_first_default.fade_mode =
12382 title_initial_first_default.fade_mode;
12383 titlemessage_initial_first_default.fade_delay =
12384 title_initial_first_default.fade_delay;
12385 titlemessage_initial_first_default.post_delay =
12386 title_initial_first_default.post_delay;
12387 titlemessage_initial_first_default.auto_delay =
12388 title_initial_first_default.auto_delay;
12389 titlemessage_initial_first_default.auto_delay_unit =
12390 title_initial_first_default.auto_delay_unit;
12391 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12392 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12393 titlemessage_first_default.post_delay = title_first_default.post_delay;
12394 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12395 titlemessage_first_default.auto_delay_unit =
12396 title_first_default.auto_delay_unit;
12398 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12399 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12400 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12401 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12402 titlescreen_initial_default.auto_delay_unit =
12403 title_initial_default.auto_delay_unit;
12404 titlescreen_default.fade_mode = title_default.fade_mode;
12405 titlescreen_default.fade_delay = title_default.fade_delay;
12406 titlescreen_default.post_delay = title_default.post_delay;
12407 titlescreen_default.auto_delay = title_default.auto_delay;
12408 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12409 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12410 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12411 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12412 titlemessage_initial_default.auto_delay_unit =
12413 title_initial_default.auto_delay_unit;
12414 titlemessage_default.fade_mode = title_default.fade_mode;
12415 titlemessage_default.fade_delay = title_default.fade_delay;
12416 titlemessage_default.post_delay = title_default.post_delay;
12417 titlemessage_default.auto_delay = title_default.auto_delay;
12418 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12420 // special case: initialize "ARG_DEFAULT" values in static default config
12421 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12422 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12424 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12425 titlescreen_first[i] = titlescreen_first_default;
12426 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12427 titlemessage_first[i] = titlemessage_first_default;
12429 titlescreen_initial[i] = titlescreen_initial_default;
12430 titlescreen[i] = titlescreen_default;
12431 titlemessage_initial[i] = titlemessage_initial_default;
12432 titlemessage[i] = titlemessage_default;
12435 // special case: initialize "ARG_DEFAULT" values in static default config
12436 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12437 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12439 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12442 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12443 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12444 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12447 // special case: initialize "ARG_DEFAULT" values in static default config
12448 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12449 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12451 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12452 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12453 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12455 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12458 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12462 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12466 struct XY *dst, *src;
12468 game_buttons_xy[] =
12470 { &game.button.save, &game.button.stop },
12471 { &game.button.pause2, &game.button.pause },
12472 { &game.button.load, &game.button.play },
12473 { &game.button.undo, &game.button.stop },
12474 { &game.button.redo, &game.button.play },
12480 // special case: initialize later added SETUP list size from LEVELS value
12481 if (menu.list_size[GAME_MODE_SETUP] == -1)
12482 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12484 // set default position for snapshot buttons to stop/pause/play buttons
12485 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12486 if ((*game_buttons_xy[i].dst).x == -1 &&
12487 (*game_buttons_xy[i].dst).y == -1)
12488 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12490 // --------------------------------------------------------------------------
12491 // dynamic viewports (including playfield margins, borders and alignments)
12492 // --------------------------------------------------------------------------
12494 // dynamic viewports currently only supported for landscape mode
12495 int display_width = MAX(video.display_width, video.display_height);
12496 int display_height = MIN(video.display_width, video.display_height);
12498 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12500 struct RectWithBorder *vp_window = &viewport.window[i];
12501 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12502 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12503 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12504 boolean dynamic_window_width = (vp_window->min_width != -1);
12505 boolean dynamic_window_height = (vp_window->min_height != -1);
12506 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12507 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12509 // adjust window size if min/max width/height is specified
12511 if (vp_window->min_width != -1)
12513 int window_width = display_width;
12515 // when using static window height, use aspect ratio of display
12516 if (vp_window->min_height == -1)
12517 window_width = vp_window->height * display_width / display_height;
12519 vp_window->width = MAX(vp_window->min_width, window_width);
12522 if (vp_window->min_height != -1)
12524 int window_height = display_height;
12526 // when using static window width, use aspect ratio of display
12527 if (vp_window->min_width == -1)
12528 window_height = vp_window->width * display_height / display_width;
12530 vp_window->height = MAX(vp_window->min_height, window_height);
12533 if (vp_window->max_width != -1)
12534 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12536 if (vp_window->max_height != -1)
12537 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12539 int playfield_width = vp_window->width;
12540 int playfield_height = vp_window->height;
12542 // adjust playfield size and position according to specified margins
12544 playfield_width -= vp_playfield->margin_left;
12545 playfield_width -= vp_playfield->margin_right;
12547 playfield_height -= vp_playfield->margin_top;
12548 playfield_height -= vp_playfield->margin_bottom;
12550 // adjust playfield size if min/max width/height is specified
12552 if (vp_playfield->min_width != -1)
12553 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12555 if (vp_playfield->min_height != -1)
12556 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12558 if (vp_playfield->max_width != -1)
12559 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12561 if (vp_playfield->max_height != -1)
12562 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12564 // adjust playfield position according to specified alignment
12566 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12567 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12568 else if (vp_playfield->align == ALIGN_CENTER)
12569 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12570 else if (vp_playfield->align == ALIGN_RIGHT)
12571 vp_playfield->x += playfield_width - vp_playfield->width;
12573 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12574 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12575 else if (vp_playfield->valign == VALIGN_MIDDLE)
12576 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12577 else if (vp_playfield->valign == VALIGN_BOTTOM)
12578 vp_playfield->y += playfield_height - vp_playfield->height;
12580 vp_playfield->x += vp_playfield->margin_left;
12581 vp_playfield->y += vp_playfield->margin_top;
12583 // adjust individual playfield borders if only default border is specified
12585 if (vp_playfield->border_left == -1)
12586 vp_playfield->border_left = vp_playfield->border_size;
12587 if (vp_playfield->border_right == -1)
12588 vp_playfield->border_right = vp_playfield->border_size;
12589 if (vp_playfield->border_top == -1)
12590 vp_playfield->border_top = vp_playfield->border_size;
12591 if (vp_playfield->border_bottom == -1)
12592 vp_playfield->border_bottom = vp_playfield->border_size;
12594 // set dynamic playfield borders if borders are specified as undefined
12595 // (but only if window size was dynamic and playfield size was static)
12597 if (dynamic_window_width && !dynamic_playfield_width)
12599 if (vp_playfield->border_left == -1)
12601 vp_playfield->border_left = (vp_playfield->x -
12602 vp_playfield->margin_left);
12603 vp_playfield->x -= vp_playfield->border_left;
12604 vp_playfield->width += vp_playfield->border_left;
12607 if (vp_playfield->border_right == -1)
12609 vp_playfield->border_right = (vp_window->width -
12611 vp_playfield->width -
12612 vp_playfield->margin_right);
12613 vp_playfield->width += vp_playfield->border_right;
12617 if (dynamic_window_height && !dynamic_playfield_height)
12619 if (vp_playfield->border_top == -1)
12621 vp_playfield->border_top = (vp_playfield->y -
12622 vp_playfield->margin_top);
12623 vp_playfield->y -= vp_playfield->border_top;
12624 vp_playfield->height += vp_playfield->border_top;
12627 if (vp_playfield->border_bottom == -1)
12629 vp_playfield->border_bottom = (vp_window->height -
12631 vp_playfield->height -
12632 vp_playfield->margin_bottom);
12633 vp_playfield->height += vp_playfield->border_bottom;
12637 // adjust playfield size to be a multiple of a defined alignment tile size
12639 int align_size = vp_playfield->align_size;
12640 int playfield_xtiles = vp_playfield->width / align_size;
12641 int playfield_ytiles = vp_playfield->height / align_size;
12642 int playfield_width_corrected = playfield_xtiles * align_size;
12643 int playfield_height_corrected = playfield_ytiles * align_size;
12644 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12645 i == GFX_SPECIAL_ARG_EDITOR);
12647 if (is_playfield_mode &&
12648 dynamic_playfield_width &&
12649 vp_playfield->width != playfield_width_corrected)
12651 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12653 vp_playfield->width = playfield_width_corrected;
12655 if (vp_playfield->align == ALIGN_LEFT)
12657 vp_playfield->border_left += playfield_xdiff;
12659 else if (vp_playfield->align == ALIGN_RIGHT)
12661 vp_playfield->border_right += playfield_xdiff;
12663 else if (vp_playfield->align == ALIGN_CENTER)
12665 int border_left_diff = playfield_xdiff / 2;
12666 int border_right_diff = playfield_xdiff - border_left_diff;
12668 vp_playfield->border_left += border_left_diff;
12669 vp_playfield->border_right += border_right_diff;
12673 if (is_playfield_mode &&
12674 dynamic_playfield_height &&
12675 vp_playfield->height != playfield_height_corrected)
12677 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12679 vp_playfield->height = playfield_height_corrected;
12681 if (vp_playfield->valign == VALIGN_TOP)
12683 vp_playfield->border_top += playfield_ydiff;
12685 else if (vp_playfield->align == VALIGN_BOTTOM)
12687 vp_playfield->border_right += playfield_ydiff;
12689 else if (vp_playfield->align == VALIGN_MIDDLE)
12691 int border_top_diff = playfield_ydiff / 2;
12692 int border_bottom_diff = playfield_ydiff - border_top_diff;
12694 vp_playfield->border_top += border_top_diff;
12695 vp_playfield->border_bottom += border_bottom_diff;
12699 // adjust door positions according to specified alignment
12701 for (j = 0; j < 2; j++)
12703 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12705 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12706 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12707 else if (vp_door->align == ALIGN_CENTER)
12708 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12709 else if (vp_door->align == ALIGN_RIGHT)
12710 vp_door->x += vp_window->width - vp_door->width;
12712 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12713 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12714 else if (vp_door->valign == VALIGN_MIDDLE)
12715 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12716 else if (vp_door->valign == VALIGN_BOTTOM)
12717 vp_door->y += vp_window->height - vp_door->height;
12722 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12726 struct XYTileSize *dst, *src;
12729 editor_buttons_xy[] =
12732 &editor.button.element_left, &editor.palette.element_left,
12733 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12736 &editor.button.element_middle, &editor.palette.element_middle,
12737 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12740 &editor.button.element_right, &editor.palette.element_right,
12741 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12748 // set default position for element buttons to element graphics
12749 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12751 if ((*editor_buttons_xy[i].dst).x == -1 &&
12752 (*editor_buttons_xy[i].dst).y == -1)
12754 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12756 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12758 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12762 // adjust editor palette rows and columns if specified to be dynamic
12764 if (editor.palette.cols == -1)
12766 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12767 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12768 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12770 editor.palette.cols = (vp_width - sc_width) / bt_width;
12772 if (editor.palette.x == -1)
12774 int palette_width = editor.palette.cols * bt_width + sc_width;
12776 editor.palette.x = (vp_width - palette_width) / 2;
12780 if (editor.palette.rows == -1)
12782 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12783 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12784 int tx_height = getFontHeight(FONT_TEXT_2);
12786 editor.palette.rows = (vp_height - tx_height) / bt_height;
12788 if (editor.palette.y == -1)
12790 int palette_height = editor.palette.rows * bt_height + tx_height;
12792 editor.palette.y = (vp_height - palette_height) / 2;
12797 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12798 boolean initialize)
12800 // special case: check if network and preview player positions are redefined,
12801 // to compare this later against the main menu level preview being redefined
12802 struct TokenIntPtrInfo menu_config_players[] =
12804 { "main.network_players.x", &menu.main.network_players.redefined },
12805 { "main.network_players.y", &menu.main.network_players.redefined },
12806 { "main.preview_players.x", &menu.main.preview_players.redefined },
12807 { "main.preview_players.y", &menu.main.preview_players.redefined },
12808 { "preview.x", &preview.redefined },
12809 { "preview.y", &preview.redefined }
12815 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12816 *menu_config_players[i].value = FALSE;
12820 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12821 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12822 *menu_config_players[i].value = TRUE;
12826 static void InitMenuDesignSettings_PreviewPlayers(void)
12828 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12831 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12833 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12836 static void LoadMenuDesignSettingsFromFilename(char *filename)
12838 static struct TitleFadingInfo tfi;
12839 static struct TitleMessageInfo tmi;
12840 static struct TokenInfo title_tokens[] =
12842 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12843 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12844 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12845 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12846 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12850 static struct TokenInfo titlemessage_tokens[] =
12852 { TYPE_INTEGER, &tmi.x, ".x" },
12853 { TYPE_INTEGER, &tmi.y, ".y" },
12854 { TYPE_INTEGER, &tmi.width, ".width" },
12855 { TYPE_INTEGER, &tmi.height, ".height" },
12856 { TYPE_INTEGER, &tmi.chars, ".chars" },
12857 { TYPE_INTEGER, &tmi.lines, ".lines" },
12858 { TYPE_INTEGER, &tmi.align, ".align" },
12859 { TYPE_INTEGER, &tmi.valign, ".valign" },
12860 { TYPE_INTEGER, &tmi.font, ".font" },
12861 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12862 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12863 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12864 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12865 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12866 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12867 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12868 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12869 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12875 struct TitleFadingInfo *info;
12880 // initialize first titles from "enter screen" definitions, if defined
12881 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12882 { &title_first_default, "menu.enter_screen.TITLE" },
12884 // initialize title screens from "next screen" definitions, if defined
12885 { &title_initial_default, "menu.next_screen.TITLE" },
12886 { &title_default, "menu.next_screen.TITLE" },
12892 struct TitleMessageInfo *array;
12895 titlemessage_arrays[] =
12897 // initialize first titles from "enter screen" definitions, if defined
12898 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12899 { titlescreen_first, "menu.enter_screen.TITLE" },
12900 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12901 { titlemessage_first, "menu.enter_screen.TITLE" },
12903 // initialize titles from "next screen" definitions, if defined
12904 { titlescreen_initial, "menu.next_screen.TITLE" },
12905 { titlescreen, "menu.next_screen.TITLE" },
12906 { titlemessage_initial, "menu.next_screen.TITLE" },
12907 { titlemessage, "menu.next_screen.TITLE" },
12909 // overwrite titles with title definitions, if defined
12910 { titlescreen_initial_first, "[title_initial]" },
12911 { titlescreen_first, "[title]" },
12912 { titlemessage_initial_first, "[title_initial]" },
12913 { titlemessage_first, "[title]" },
12915 { titlescreen_initial, "[title_initial]" },
12916 { titlescreen, "[title]" },
12917 { titlemessage_initial, "[title_initial]" },
12918 { titlemessage, "[title]" },
12920 // overwrite titles with title screen/message definitions, if defined
12921 { titlescreen_initial_first, "[titlescreen_initial]" },
12922 { titlescreen_first, "[titlescreen]" },
12923 { titlemessage_initial_first, "[titlemessage_initial]" },
12924 { titlemessage_first, "[titlemessage]" },
12926 { titlescreen_initial, "[titlescreen_initial]" },
12927 { titlescreen, "[titlescreen]" },
12928 { titlemessage_initial, "[titlemessage_initial]" },
12929 { titlemessage, "[titlemessage]" },
12933 SetupFileHash *setup_file_hash;
12936 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12939 // the following initializes hierarchical values from dynamic configuration
12941 // special case: initialize with default values that may be overwritten
12942 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12943 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12945 struct TokenIntPtrInfo menu_config[] =
12947 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12948 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12949 { "menu.list_size", &menu.list_size[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.INFO[XXX]" from "menu.draw_xoffset.INFO")
12964 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12966 struct TokenIntPtrInfo menu_config[] =
12968 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12969 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12970 { "menu.list_size.INFO", &menu.list_size_info[i] },
12971 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12972 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12975 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12977 char *token = menu_config[j].token;
12978 char *value = getHashEntry(setup_file_hash, token);
12981 *menu_config[j].value = get_integer_from_string(value);
12985 // special case: initialize with default values that may be overwritten
12986 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12987 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12989 struct TokenIntPtrInfo menu_config[] =
12991 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12992 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12995 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12997 char *token = menu_config[j].token;
12998 char *value = getHashEntry(setup_file_hash, token);
13001 *menu_config[j].value = get_integer_from_string(value);
13005 // special case: initialize with default values that may be overwritten
13006 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13007 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13009 struct TokenIntPtrInfo menu_config[] =
13011 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13012 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13013 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13014 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13015 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13016 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13017 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13018 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13019 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13020 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13023 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13025 char *token = menu_config[j].token;
13026 char *value = getHashEntry(setup_file_hash, token);
13029 *menu_config[j].value = get_integer_from_string(value);
13033 // special case: initialize with default values that may be overwritten
13034 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13035 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13037 struct TokenIntPtrInfo menu_config[] =
13039 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13040 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13041 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13042 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13043 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13044 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13045 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13046 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13047 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13050 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13052 char *token = menu_config[j].token;
13053 char *value = getHashEntry(setup_file_hash, token);
13056 *menu_config[j].value = get_token_parameter_value(token, value);
13060 // special case: initialize with default values that may be overwritten
13061 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13062 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13066 char *token_prefix;
13067 struct RectWithBorder *struct_ptr;
13071 { "viewport.window", &viewport.window[i] },
13072 { "viewport.playfield", &viewport.playfield[i] },
13073 { "viewport.door_1", &viewport.door_1[i] },
13074 { "viewport.door_2", &viewport.door_2[i] }
13077 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13079 struct TokenIntPtrInfo vp_config[] =
13081 { ".x", &vp_struct[j].struct_ptr->x },
13082 { ".y", &vp_struct[j].struct_ptr->y },
13083 { ".width", &vp_struct[j].struct_ptr->width },
13084 { ".height", &vp_struct[j].struct_ptr->height },
13085 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13086 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13087 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13088 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13089 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13090 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13091 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13092 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13093 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13094 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13095 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13096 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13097 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13098 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13099 { ".align", &vp_struct[j].struct_ptr->align },
13100 { ".valign", &vp_struct[j].struct_ptr->valign }
13103 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13105 char *token = getStringCat2(vp_struct[j].token_prefix,
13106 vp_config[k].token);
13107 char *value = getHashEntry(setup_file_hash, token);
13110 *vp_config[k].value = get_token_parameter_value(token, value);
13117 // special case: initialize with default values that may be overwritten
13118 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13119 for (i = 0; title_info[i].info != NULL; i++)
13121 struct TitleFadingInfo *info = title_info[i].info;
13122 char *base_token = title_info[i].text;
13124 for (j = 0; title_tokens[j].type != -1; j++)
13126 char *token = getStringCat2(base_token, title_tokens[j].text);
13127 char *value = getHashEntry(setup_file_hash, token);
13131 int parameter_value = get_token_parameter_value(token, value);
13135 *(int *)title_tokens[j].value = (int)parameter_value;
13144 // special case: initialize with default values that may be overwritten
13145 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13146 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13148 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13149 char *base_token = titlemessage_arrays[i].text;
13151 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13153 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13154 char *value = getHashEntry(setup_file_hash, token);
13158 int parameter_value = get_token_parameter_value(token, value);
13160 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13164 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13165 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13167 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13177 // read (and overwrite with) values that may be specified in config file
13178 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13180 // special case: check if network and preview player positions are redefined
13181 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13183 freeSetupFileHash(setup_file_hash);
13186 void LoadMenuDesignSettings(void)
13188 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13190 InitMenuDesignSettings_Static();
13191 InitMenuDesignSettings_SpecialPreProcessing();
13192 InitMenuDesignSettings_PreviewPlayers();
13194 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13196 // first look for special settings configured in level series config
13197 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13199 if (fileExists(filename_base))
13200 LoadMenuDesignSettingsFromFilename(filename_base);
13203 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13205 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13206 LoadMenuDesignSettingsFromFilename(filename_local);
13208 InitMenuDesignSettings_SpecialPostProcessing();
13211 void LoadMenuDesignSettings_AfterGraphics(void)
13213 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13216 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13217 boolean ignore_defaults)
13221 for (i = 0; sound_config_vars[i].token != NULL; i++)
13223 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13225 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13226 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13230 *sound_config_vars[i].value =
13231 get_token_parameter_value(sound_config_vars[i].token, value);
13235 void InitSoundSettings_Static(void)
13237 // always start with reliable default values from static default config
13238 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13241 static void LoadSoundSettingsFromFilename(char *filename)
13243 SetupFileHash *setup_file_hash;
13245 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13248 // read (and overwrite with) values that may be specified in config file
13249 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13251 freeSetupFileHash(setup_file_hash);
13254 void LoadSoundSettings(void)
13256 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13258 InitSoundSettings_Static();
13260 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13262 // first look for special settings configured in level series config
13263 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13265 if (fileExists(filename_base))
13266 LoadSoundSettingsFromFilename(filename_base);
13269 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13271 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13272 LoadSoundSettingsFromFilename(filename_local);
13275 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13277 char *filename = getEditorSetupFilename();
13278 SetupFileList *setup_file_list, *list;
13279 SetupFileHash *element_hash;
13280 int num_unknown_tokens = 0;
13283 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13286 element_hash = newSetupFileHash();
13288 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13289 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13291 // determined size may be larger than needed (due to unknown elements)
13293 for (list = setup_file_list; list != NULL; list = list->next)
13296 // add space for up to 3 more elements for padding that may be needed
13297 *num_elements += 3;
13299 // free memory for old list of elements, if needed
13300 checked_free(*elements);
13302 // allocate memory for new list of elements
13303 *elements = checked_malloc(*num_elements * sizeof(int));
13306 for (list = setup_file_list; list != NULL; list = list->next)
13308 char *value = getHashEntry(element_hash, list->token);
13310 if (value == NULL) // try to find obsolete token mapping
13312 char *mapped_token = get_mapped_token(list->token);
13314 if (mapped_token != NULL)
13316 value = getHashEntry(element_hash, mapped_token);
13318 free(mapped_token);
13324 (*elements)[(*num_elements)++] = atoi(value);
13328 if (num_unknown_tokens == 0)
13331 Warn("unknown token(s) found in config file:");
13332 Warn("- config file: '%s'", filename);
13334 num_unknown_tokens++;
13337 Warn("- token: '%s'", list->token);
13341 if (num_unknown_tokens > 0)
13344 while (*num_elements % 4) // pad with empty elements, if needed
13345 (*elements)[(*num_elements)++] = EL_EMPTY;
13347 freeSetupFileList(setup_file_list);
13348 freeSetupFileHash(element_hash);
13351 for (i = 0; i < *num_elements; i++)
13352 Debug("editor", "element '%s' [%d]\n",
13353 element_info[(*elements)[i]].token_name, (*elements)[i]);
13357 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13360 SetupFileHash *setup_file_hash = NULL;
13361 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13362 char *filename_music, *filename_prefix, *filename_info;
13368 token_to_value_ptr[] =
13370 { "title_header", &tmp_music_file_info.title_header },
13371 { "artist_header", &tmp_music_file_info.artist_header },
13372 { "album_header", &tmp_music_file_info.album_header },
13373 { "year_header", &tmp_music_file_info.year_header },
13374 { "played_header", &tmp_music_file_info.played_header },
13376 { "title", &tmp_music_file_info.title },
13377 { "artist", &tmp_music_file_info.artist },
13378 { "album", &tmp_music_file_info.album },
13379 { "year", &tmp_music_file_info.year },
13380 { "played", &tmp_music_file_info.played },
13386 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13387 getCustomMusicFilename(basename));
13389 if (filename_music == NULL)
13392 // ---------- try to replace file extension ----------
13394 filename_prefix = getStringCopy(filename_music);
13395 if (strrchr(filename_prefix, '.') != NULL)
13396 *strrchr(filename_prefix, '.') = '\0';
13397 filename_info = getStringCat2(filename_prefix, ".txt");
13399 if (fileExists(filename_info))
13400 setup_file_hash = loadSetupFileHash(filename_info);
13402 free(filename_prefix);
13403 free(filename_info);
13405 if (setup_file_hash == NULL)
13407 // ---------- try to add file extension ----------
13409 filename_prefix = getStringCopy(filename_music);
13410 filename_info = getStringCat2(filename_prefix, ".txt");
13412 if (fileExists(filename_info))
13413 setup_file_hash = loadSetupFileHash(filename_info);
13415 free(filename_prefix);
13416 free(filename_info);
13419 if (setup_file_hash == NULL)
13422 // ---------- music file info found ----------
13424 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13426 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13428 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13430 *token_to_value_ptr[i].value_ptr =
13431 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13434 tmp_music_file_info.basename = getStringCopy(basename);
13435 tmp_music_file_info.music = music;
13436 tmp_music_file_info.is_sound = is_sound;
13438 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13439 *new_music_file_info = tmp_music_file_info;
13441 return new_music_file_info;
13444 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13446 return get_music_file_info_ext(basename, music, FALSE);
13449 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13451 return get_music_file_info_ext(basename, sound, TRUE);
13454 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13455 char *basename, boolean is_sound)
13457 for (; list != NULL; list = list->next)
13458 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13464 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13466 return music_info_listed_ext(list, basename, FALSE);
13469 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13471 return music_info_listed_ext(list, basename, TRUE);
13474 void LoadMusicInfo(void)
13476 int num_music_noconf = getMusicListSize_NoConf();
13477 int num_music = getMusicListSize();
13478 int num_sounds = getSoundListSize();
13479 struct FileInfo *music, *sound;
13480 struct MusicFileInfo *next, **new;
13484 while (music_file_info != NULL)
13486 next = music_file_info->next;
13488 checked_free(music_file_info->basename);
13490 checked_free(music_file_info->title_header);
13491 checked_free(music_file_info->artist_header);
13492 checked_free(music_file_info->album_header);
13493 checked_free(music_file_info->year_header);
13494 checked_free(music_file_info->played_header);
13496 checked_free(music_file_info->title);
13497 checked_free(music_file_info->artist);
13498 checked_free(music_file_info->album);
13499 checked_free(music_file_info->year);
13500 checked_free(music_file_info->played);
13502 free(music_file_info);
13504 music_file_info = next;
13507 new = &music_file_info;
13509 // get (configured or unconfigured) music file info for all levels
13510 for (i = leveldir_current->first_level;
13511 i <= leveldir_current->last_level; i++)
13515 if (levelset.music[i] != MUS_UNDEFINED)
13517 // get music file info for configured level music
13518 music_nr = levelset.music[i];
13520 else if (num_music_noconf > 0)
13522 // get music file info for unconfigured level music
13523 int level_pos = i - leveldir_current->first_level;
13525 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13532 char *basename = getMusicInfoEntryFilename(music_nr);
13534 if (basename == NULL)
13537 if (!music_info_listed(music_file_info, basename))
13539 *new = get_music_file_info(basename, music_nr);
13542 new = &(*new)->next;
13546 // get music file info for all remaining configured music files
13547 for (i = 0; i < num_music; i++)
13549 music = getMusicListEntry(i);
13551 if (music->filename == NULL)
13554 if (strEqual(music->filename, UNDEFINED_FILENAME))
13557 // a configured file may be not recognized as music
13558 if (!FileIsMusic(music->filename))
13561 if (!music_info_listed(music_file_info, music->filename))
13563 *new = get_music_file_info(music->filename, i);
13566 new = &(*new)->next;
13570 // get sound file info for all configured sound files
13571 for (i = 0; i < num_sounds; i++)
13573 sound = getSoundListEntry(i);
13575 if (sound->filename == NULL)
13578 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13581 // a configured file may be not recognized as sound
13582 if (!FileIsSound(sound->filename))
13585 if (!sound_info_listed(music_file_info, sound->filename))
13587 *new = get_sound_file_info(sound->filename, i);
13589 new = &(*new)->next;
13593 // add pointers to previous list nodes
13595 struct MusicFileInfo *node = music_file_info;
13597 while (node != NULL)
13600 node->next->prev = node;
13606 static void add_helpanim_entry(int element, int action, int direction,
13607 int delay, int *num_list_entries)
13609 struct HelpAnimInfo *new_list_entry;
13610 (*num_list_entries)++;
13613 checked_realloc(helpanim_info,
13614 *num_list_entries * sizeof(struct HelpAnimInfo));
13615 new_list_entry = &helpanim_info[*num_list_entries - 1];
13617 new_list_entry->element = element;
13618 new_list_entry->action = action;
13619 new_list_entry->direction = direction;
13620 new_list_entry->delay = delay;
13623 static void print_unknown_token(char *filename, char *token, int token_nr)
13628 Warn("unknown token(s) found in config file:");
13629 Warn("- config file: '%s'", filename);
13632 Warn("- token: '%s'", token);
13635 static void print_unknown_token_end(int token_nr)
13641 void LoadHelpAnimInfo(void)
13643 char *filename = getHelpAnimFilename();
13644 SetupFileList *setup_file_list = NULL, *list;
13645 SetupFileHash *element_hash, *action_hash, *direction_hash;
13646 int num_list_entries = 0;
13647 int num_unknown_tokens = 0;
13650 if (fileExists(filename))
13651 setup_file_list = loadSetupFileList(filename);
13653 if (setup_file_list == NULL)
13655 // use reliable default values from static configuration
13656 SetupFileList *insert_ptr;
13658 insert_ptr = setup_file_list =
13659 newSetupFileList(helpanim_config[0].token,
13660 helpanim_config[0].value);
13662 for (i = 1; helpanim_config[i].token; i++)
13663 insert_ptr = addListEntry(insert_ptr,
13664 helpanim_config[i].token,
13665 helpanim_config[i].value);
13668 element_hash = newSetupFileHash();
13669 action_hash = newSetupFileHash();
13670 direction_hash = newSetupFileHash();
13672 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13673 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13675 for (i = 0; i < NUM_ACTIONS; i++)
13676 setHashEntry(action_hash, element_action_info[i].suffix,
13677 i_to_a(element_action_info[i].value));
13679 // do not store direction index (bit) here, but direction value!
13680 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13681 setHashEntry(direction_hash, element_direction_info[i].suffix,
13682 i_to_a(1 << element_direction_info[i].value));
13684 for (list = setup_file_list; list != NULL; list = list->next)
13686 char *element_token, *action_token, *direction_token;
13687 char *element_value, *action_value, *direction_value;
13688 int delay = atoi(list->value);
13690 if (strEqual(list->token, "end"))
13692 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13697 /* first try to break element into element/action/direction parts;
13698 if this does not work, also accept combined "element[.act][.dir]"
13699 elements (like "dynamite.active"), which are unique elements */
13701 if (strchr(list->token, '.') == NULL) // token contains no '.'
13703 element_value = getHashEntry(element_hash, list->token);
13704 if (element_value != NULL) // element found
13705 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13706 &num_list_entries);
13709 // no further suffixes found -- this is not an element
13710 print_unknown_token(filename, list->token, num_unknown_tokens++);
13716 // token has format "<prefix>.<something>"
13718 action_token = strchr(list->token, '.'); // suffix may be action ...
13719 direction_token = action_token; // ... or direction
13721 element_token = getStringCopy(list->token);
13722 *strchr(element_token, '.') = '\0';
13724 element_value = getHashEntry(element_hash, element_token);
13726 if (element_value == NULL) // this is no element
13728 element_value = getHashEntry(element_hash, list->token);
13729 if (element_value != NULL) // combined element found
13730 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13731 &num_list_entries);
13733 print_unknown_token(filename, list->token, num_unknown_tokens++);
13735 free(element_token);
13740 action_value = getHashEntry(action_hash, action_token);
13742 if (action_value != NULL) // action found
13744 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13745 &num_list_entries);
13747 free(element_token);
13752 direction_value = getHashEntry(direction_hash, direction_token);
13754 if (direction_value != NULL) // direction found
13756 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13757 &num_list_entries);
13759 free(element_token);
13764 if (strchr(action_token + 1, '.') == NULL)
13766 // no further suffixes found -- this is not an action nor direction
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);
13780 // token has format "<prefix>.<suffix>.<something>"
13782 direction_token = strchr(action_token + 1, '.');
13784 action_token = getStringCopy(action_token);
13785 *strchr(action_token + 1, '.') = '\0';
13787 action_value = getHashEntry(action_hash, action_token);
13789 if (action_value == NULL) // this is no action
13791 element_value = getHashEntry(element_hash, list->token);
13792 if (element_value != NULL) // combined element found
13793 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13794 &num_list_entries);
13796 print_unknown_token(filename, list->token, num_unknown_tokens++);
13798 free(element_token);
13799 free(action_token);
13804 direction_value = getHashEntry(direction_hash, direction_token);
13806 if (direction_value != NULL) // direction found
13808 add_helpanim_entry(atoi(element_value), atoi(action_value),
13809 atoi(direction_value), delay, &num_list_entries);
13811 free(element_token);
13812 free(action_token);
13817 // this is no direction
13819 element_value = getHashEntry(element_hash, list->token);
13820 if (element_value != NULL) // combined element found
13821 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13822 &num_list_entries);
13824 print_unknown_token(filename, list->token, num_unknown_tokens++);
13826 free(element_token);
13827 free(action_token);
13830 print_unknown_token_end(num_unknown_tokens);
13832 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13833 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13835 freeSetupFileList(setup_file_list);
13836 freeSetupFileHash(element_hash);
13837 freeSetupFileHash(action_hash);
13838 freeSetupFileHash(direction_hash);
13841 for (i = 0; i < num_list_entries; i++)
13842 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13843 EL_NAME(helpanim_info[i].element),
13844 helpanim_info[i].element,
13845 helpanim_info[i].action,
13846 helpanim_info[i].direction,
13847 helpanim_info[i].delay);
13851 void LoadHelpTextInfo(void)
13853 char *filename = getHelpTextFilename();
13856 if (helptext_info != NULL)
13858 freeSetupFileHash(helptext_info);
13859 helptext_info = NULL;
13862 if (fileExists(filename))
13863 helptext_info = loadSetupFileHash(filename);
13865 if (helptext_info == NULL)
13867 // use reliable default values from static configuration
13868 helptext_info = newSetupFileHash();
13870 for (i = 0; helptext_config[i].token; i++)
13871 setHashEntry(helptext_info,
13872 helptext_config[i].token,
13873 helptext_config[i].value);
13877 BEGIN_HASH_ITERATION(helptext_info, itr)
13879 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13880 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13882 END_HASH_ITERATION(hash, itr)
13887 // ----------------------------------------------------------------------------
13889 // ----------------------------------------------------------------------------
13891 #define MAX_NUM_CONVERT_LEVELS 1000
13893 void ConvertLevels(void)
13895 static LevelDirTree *convert_leveldir = NULL;
13896 static int convert_level_nr = -1;
13897 static int num_levels_handled = 0;
13898 static int num_levels_converted = 0;
13899 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13902 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13903 global.convert_leveldir);
13905 if (convert_leveldir == NULL)
13906 Fail("no such level identifier: '%s'", global.convert_leveldir);
13908 leveldir_current = convert_leveldir;
13910 if (global.convert_level_nr != -1)
13912 convert_leveldir->first_level = global.convert_level_nr;
13913 convert_leveldir->last_level = global.convert_level_nr;
13916 convert_level_nr = convert_leveldir->first_level;
13918 PrintLine("=", 79);
13919 Print("Converting levels\n");
13920 PrintLine("-", 79);
13921 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13922 Print("Level series name: '%s'\n", convert_leveldir->name);
13923 Print("Level series author: '%s'\n", convert_leveldir->author);
13924 Print("Number of levels: %d\n", convert_leveldir->levels);
13925 PrintLine("=", 79);
13928 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13929 levels_failed[i] = FALSE;
13931 while (convert_level_nr <= convert_leveldir->last_level)
13933 char *level_filename;
13936 level_nr = convert_level_nr++;
13938 Print("Level %03d: ", level_nr);
13940 LoadLevel(level_nr);
13941 if (level.no_level_file || level.no_valid_file)
13943 Print("(no level)\n");
13947 Print("converting level ... ");
13950 // special case: conversion of some EMC levels as requested by ACME
13951 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13954 level_filename = getDefaultLevelFilename(level_nr);
13955 new_level = !fileExists(level_filename);
13959 SaveLevel(level_nr);
13961 num_levels_converted++;
13963 Print("converted.\n");
13967 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13968 levels_failed[level_nr] = TRUE;
13970 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13973 num_levels_handled++;
13977 PrintLine("=", 79);
13978 Print("Number of levels handled: %d\n", num_levels_handled);
13979 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13980 (num_levels_handled ?
13981 num_levels_converted * 100 / num_levels_handled : 0));
13982 PrintLine("-", 79);
13983 Print("Summary (for automatic parsing by scripts):\n");
13984 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13985 convert_leveldir->identifier, num_levels_converted,
13986 num_levels_handled,
13987 (num_levels_handled ?
13988 num_levels_converted * 100 / num_levels_handled : 0));
13990 if (num_levels_handled != num_levels_converted)
13992 Print(", FAILED:");
13993 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13994 if (levels_failed[i])
13999 PrintLine("=", 79);
14001 CloseAllAndExit(0);
14005 // ----------------------------------------------------------------------------
14006 // create and save images for use in level sketches (raw BMP format)
14007 // ----------------------------------------------------------------------------
14009 void CreateLevelSketchImages(void)
14015 InitElementPropertiesGfxElement();
14017 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14018 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14020 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14022 int element = getMappedElement(i);
14023 char basename1[16];
14024 char basename2[16];
14028 sprintf(basename1, "%04d.bmp", i);
14029 sprintf(basename2, "%04ds.bmp", i);
14031 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14032 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14034 DrawSizedElement(0, 0, element, TILESIZE);
14035 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14037 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14038 Fail("cannot save level sketch image file '%s'", filename1);
14040 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14041 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14043 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14044 Fail("cannot save level sketch image file '%s'", filename2);
14049 // create corresponding SQL statements (for normal and small images)
14052 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14053 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14056 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14057 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14059 // optional: create content for forum level sketch demonstration post
14061 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14064 FreeBitmap(bitmap1);
14065 FreeBitmap(bitmap2);
14068 fprintf(stderr, "\n");
14070 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14072 CloseAllAndExit(0);
14076 // ----------------------------------------------------------------------------
14077 // create and save images for element collecting animations (raw BMP format)
14078 // ----------------------------------------------------------------------------
14080 static boolean createCollectImage(int element)
14082 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14085 void CreateCollectElementImages(void)
14089 int anim_frames = num_steps - 1;
14090 int tile_size = TILESIZE;
14091 int anim_width = tile_size * anim_frames;
14092 int anim_height = tile_size;
14093 int num_collect_images = 0;
14094 int pos_collect_images = 0;
14096 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14097 if (createCollectImage(i))
14098 num_collect_images++;
14100 Info("Creating %d element collecting animation images ...",
14101 num_collect_images);
14103 int dst_width = anim_width * 2;
14104 int dst_height = anim_height * num_collect_images / 2;
14105 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14106 char *basename_bmp = "RocksCollect.bmp";
14107 char *basename_png = "RocksCollect.png";
14108 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14109 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14110 int len_filename_bmp = strlen(filename_bmp);
14111 int len_filename_png = strlen(filename_png);
14112 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14113 char cmd_convert[max_command_len];
14115 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14119 // force using RGBA surface for destination bitmap
14120 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14121 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14123 dst_bitmap->surface =
14124 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14126 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14128 if (!createCollectImage(i))
14131 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14132 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14133 int graphic = el2img(i);
14134 char *token_name = element_info[i].token_name;
14135 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14136 Bitmap *src_bitmap;
14139 Info("- creating collecting image for '%s' ...", token_name);
14141 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14143 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14144 tile_size, tile_size, 0, 0);
14146 // force using RGBA surface for temporary bitmap (using transparent black)
14147 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14148 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14150 tmp_bitmap->surface =
14151 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14153 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14155 for (j = 0; j < anim_frames; j++)
14157 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14158 int frame_size = frame_size_final * num_steps;
14159 int offset = (tile_size - frame_size_final) / 2;
14160 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14162 while (frame_size > frame_size_final)
14166 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14168 FreeBitmap(frame_bitmap);
14170 frame_bitmap = half_bitmap;
14173 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14174 frame_size_final, frame_size_final,
14175 dst_x + j * tile_size + offset, dst_y + offset);
14177 FreeBitmap(frame_bitmap);
14180 tmp_bitmap->surface_masked = NULL;
14182 FreeBitmap(tmp_bitmap);
14184 pos_collect_images++;
14187 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14188 Fail("cannot save element collecting image file '%s'", filename_bmp);
14190 FreeBitmap(dst_bitmap);
14192 Info("Converting image file from BMP to PNG ...");
14194 if (system(cmd_convert) != 0)
14195 Fail("converting image file failed");
14197 unlink(filename_bmp);
14201 CloseAllAndExit(0);
14205 // ----------------------------------------------------------------------------
14206 // create and save images for custom and group elements (raw BMP format)
14207 // ----------------------------------------------------------------------------
14209 void CreateCustomElementImages(char *directory)
14211 char *src_basename = "RocksCE-template.ilbm";
14212 char *dst_basename = "RocksCE.bmp";
14213 char *src_filename = getPath2(directory, src_basename);
14214 char *dst_filename = getPath2(directory, dst_basename);
14215 Bitmap *src_bitmap;
14217 int yoffset_ce = 0;
14218 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14221 InitVideoDefaults();
14223 ReCreateBitmap(&backbuffer, video.width, video.height);
14225 src_bitmap = LoadImage(src_filename);
14227 bitmap = CreateBitmap(TILEX * 16 * 2,
14228 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14231 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14238 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14239 TILEX * x, TILEY * y + yoffset_ce);
14241 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14243 TILEX * x + TILEX * 16,
14244 TILEY * y + yoffset_ce);
14246 for (j = 2; j >= 0; j--)
14250 BlitBitmap(src_bitmap, bitmap,
14251 TILEX + c * 7, 0, 6, 10,
14252 TILEX * x + 6 + j * 7,
14253 TILEY * y + 11 + yoffset_ce);
14255 BlitBitmap(src_bitmap, bitmap,
14256 TILEX + c * 8, TILEY, 6, 10,
14257 TILEX * 16 + TILEX * x + 6 + j * 8,
14258 TILEY * y + 10 + yoffset_ce);
14264 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14271 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14272 TILEX * x, TILEY * y + yoffset_ge);
14274 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14276 TILEX * x + TILEX * 16,
14277 TILEY * y + yoffset_ge);
14279 for (j = 1; j >= 0; j--)
14283 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14284 TILEX * x + 6 + j * 10,
14285 TILEY * y + 11 + yoffset_ge);
14287 BlitBitmap(src_bitmap, bitmap,
14288 TILEX + c * 8, TILEY + 12, 6, 10,
14289 TILEX * 16 + TILEX * x + 10 + j * 8,
14290 TILEY * y + 10 + yoffset_ge);
14296 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14297 Fail("cannot save CE graphics file '%s'", dst_filename);
14299 FreeBitmap(bitmap);
14301 CloseAllAndExit(0);