1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
323 // (these values are the same for each player)
326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
327 &li.block_last_field, FALSE // default case for EM levels
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
332 &li.sp_block_last_field, TRUE // default case for SP levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
337 &li.instant_relocation, FALSE
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
342 &li.can_pass_to_walkable, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
347 &li.block_snap_field, TRUE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
352 &li.continuous_snapping, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
357 &li.shifted_relocation, FALSE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
362 &li.lazy_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
367 &li.finish_dig_collect, TRUE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
372 &li.keep_walkable_ce, FALSE
375 // (these values are different for each player)
378 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
379 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
383 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
384 &li.initial_player_gravity[0], FALSE
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
389 &li.use_start_element[0], FALSE
393 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
394 &li.start_element[0], EL_PLAYER_1
398 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
399 &li.use_artwork_element[0], FALSE
403 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
404 &li.artwork_element[0], EL_PLAYER_1
408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
409 &li.use_explosion_element[0], FALSE
413 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
414 &li.explosion_element[0], EL_PLAYER_1
418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
419 &li.use_initial_inventory[0], FALSE
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
424 &li.initial_inventory_size[0], 1
428 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
429 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
435 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
436 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
440 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
441 &li.initial_player_gravity[1], FALSE
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
446 &li.use_start_element[1], FALSE
450 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
451 &li.start_element[1], EL_PLAYER_2
455 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
456 &li.use_artwork_element[1], FALSE
460 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
461 &li.artwork_element[1], EL_PLAYER_2
465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
466 &li.use_explosion_element[1], FALSE
470 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
471 &li.explosion_element[1], EL_PLAYER_2
475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
476 &li.use_initial_inventory[1], FALSE
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
481 &li.initial_inventory_size[1], 1
485 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
486 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
492 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
493 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
497 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
498 &li.initial_player_gravity[2], FALSE
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
503 &li.use_start_element[2], FALSE
507 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
508 &li.start_element[2], EL_PLAYER_3
512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
513 &li.use_artwork_element[2], FALSE
517 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
518 &li.artwork_element[2], EL_PLAYER_3
522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
523 &li.use_explosion_element[2], FALSE
527 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
528 &li.explosion_element[2], EL_PLAYER_3
532 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
533 &li.use_initial_inventory[2], FALSE
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
538 &li.initial_inventory_size[2], 1
542 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
543 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
549 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
550 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
554 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
555 &li.initial_player_gravity[3], FALSE
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
560 &li.use_start_element[3], FALSE
564 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
565 &li.start_element[3], EL_PLAYER_4
569 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
570 &li.use_artwork_element[3], FALSE
574 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
575 &li.artwork_element[3], EL_PLAYER_4
579 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
580 &li.use_explosion_element[3], FALSE
584 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
585 &li.explosion_element[3], EL_PLAYER_4
589 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
590 &li.use_initial_inventory[3], FALSE
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
595 &li.initial_inventory_size[3], 1
599 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
600 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
604 // (these values are only valid for BD style levels)
607 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
608 &li.bd_diagonal_movements, FALSE
612 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
613 &li.bd_topmost_player_active, TRUE
617 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
618 &li.bd_pushing_prob, 25
622 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
623 &li.bd_pushing_prob_with_sweet, 100
627 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
628 &li.bd_push_mega_rock_with_sweet, FALSE
633 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
634 &li.score[SC_DIAMOND_EXTRA], 20
637 // (the following values are related to various game elements)
641 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
642 &li.score[SC_EMERALD], 10
647 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
648 &li.score[SC_DIAMOND], 10
653 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
654 &li.score[SC_BUG], 10
659 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
660 &li.score[SC_SPACESHIP], 10
665 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
666 &li.score[SC_PACMAN], 10
671 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
672 &li.score[SC_NUT], 10
677 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
678 &li.score[SC_DYNAMITE], 10
683 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
684 &li.score[SC_KEY], 10
689 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
690 &li.score[SC_PEARL], 10
695 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
696 &li.score[SC_CRYSTAL], 10
701 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
702 &li.amoeba_content, EL_DIAMOND
706 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
711 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
712 &li.grow_into_diggable, TRUE
717 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
718 &li.yamyam_content, EL_ROCK, NULL,
719 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
723 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
724 &li.score[SC_YAMYAM], 10
729 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
730 &li.score[SC_ROBOT], 10
734 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
740 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
746 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
747 &li.time_magic_wall, 10
752 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
753 &li.game_of_life[0], 2
757 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
758 &li.game_of_life[1], 3
762 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
763 &li.game_of_life[2], 3
767 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
768 &li.game_of_life[3], 3
772 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
773 &li.use_life_bugs, FALSE
778 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
783 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
788 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
793 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
798 EL_TIMEGATE_SWITCH, -1,
799 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
800 &li.time_timegate, 10
804 EL_LIGHT_SWITCH_ACTIVE, -1,
805 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
810 EL_SHIELD_NORMAL, -1,
811 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
812 &li.shield_normal_time, 10
815 EL_SHIELD_NORMAL, -1,
816 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
817 &li.score[SC_SHIELD], 10
821 EL_SHIELD_DEADLY, -1,
822 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
823 &li.shield_deadly_time, 10
826 EL_SHIELD_DEADLY, -1,
827 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
828 &li.score[SC_SHIELD], 10
833 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
838 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
839 &li.extra_time_score, 10
843 EL_TIME_ORB_FULL, -1,
844 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
845 &li.time_orb_time, 10
848 EL_TIME_ORB_FULL, -1,
849 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
850 &li.use_time_orb_bug, FALSE
855 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
856 &li.use_spring_bug, FALSE
861 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
862 &li.android_move_time, 10
866 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
867 &li.android_clone_time, 10
870 EL_EMC_ANDROID, SAVE_CONF_NEVER,
871 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
872 &li.android_clone_element[0], EL_EMPTY, NULL,
873 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
877 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
878 &li.android_clone_element[0], EL_EMPTY, NULL,
879 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
884 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
889 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
894 EL_EMC_MAGNIFIER, -1,
895 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
896 &li.magnify_score, 10
899 EL_EMC_MAGNIFIER, -1,
900 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
905 EL_EMC_MAGIC_BALL, -1,
906 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
910 EL_EMC_MAGIC_BALL, -1,
911 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
912 &li.ball_random, FALSE
915 EL_EMC_MAGIC_BALL, -1,
916 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
917 &li.ball_active_initial, FALSE
920 EL_EMC_MAGIC_BALL, -1,
921 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
922 &li.ball_content, EL_EMPTY, NULL,
923 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
927 EL_SOKOBAN_FIELD_EMPTY, -1,
928 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
929 &li.sb_fields_needed, TRUE
933 EL_SOKOBAN_OBJECT, -1,
934 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
935 &li.sb_objects_needed, TRUE
940 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
941 &li.mm_laser_red, FALSE
945 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
946 &li.mm_laser_green, FALSE
950 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
951 &li.mm_laser_blue, TRUE
956 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
957 &li.df_laser_red, TRUE
961 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
962 &li.df_laser_green, TRUE
966 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
967 &li.df_laser_blue, FALSE
971 EL_MM_FUSE_ACTIVE, -1,
972 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
977 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
983 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
988 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
989 &li.mm_ball_choice_mode, ANIM_RANDOM
993 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
994 &li.mm_ball_content, EL_EMPTY, NULL,
995 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
999 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1000 &li.rotate_mm_ball_content, TRUE
1003 EL_MM_GRAY_BALL, -1,
1004 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1005 &li.explode_mm_ball, FALSE
1009 EL_MM_STEEL_BLOCK, -1,
1010 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1011 &li.mm_time_block, 75
1014 EL_MM_LIGHTBALL, -1,
1015 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1016 &li.score[SC_ELEM_BONUS], 10
1026 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1030 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1031 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1035 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1036 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1041 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1042 &xx_envelope.autowrap, FALSE
1046 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1047 &xx_envelope.centered, FALSE
1052 TYPE_STRING, CONF_VALUE_BYTES(1),
1053 &xx_envelope.text, -1, NULL,
1054 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1055 &xx_default_string_empty[0]
1065 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1069 TYPE_STRING, CONF_VALUE_BYTES(1),
1070 &xx_ei.description[0], -1,
1071 &yy_ei.description[0],
1072 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1073 &xx_default_description[0]
1078 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1079 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1080 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1082 #if ENABLE_RESERVED_CODE
1083 // (reserved for later use)
1086 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1087 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1088 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1094 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1095 &xx_ei.use_gfx_element, FALSE,
1096 &yy_ei.use_gfx_element
1100 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1101 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1102 &yy_ei.gfx_element_initial
1107 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1108 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1109 &yy_ei.access_direction
1114 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1115 &xx_ei.collect_score_initial, 10,
1116 &yy_ei.collect_score_initial
1120 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1121 &xx_ei.collect_count_initial, 1,
1122 &yy_ei.collect_count_initial
1127 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1128 &xx_ei.ce_value_fixed_initial, 0,
1129 &yy_ei.ce_value_fixed_initial
1133 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1134 &xx_ei.ce_value_random_initial, 0,
1135 &yy_ei.ce_value_random_initial
1139 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1140 &xx_ei.use_last_ce_value, FALSE,
1141 &yy_ei.use_last_ce_value
1146 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1147 &xx_ei.push_delay_fixed, 8,
1148 &yy_ei.push_delay_fixed
1152 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1153 &xx_ei.push_delay_random, 8,
1154 &yy_ei.push_delay_random
1158 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1159 &xx_ei.drop_delay_fixed, 0,
1160 &yy_ei.drop_delay_fixed
1164 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1165 &xx_ei.drop_delay_random, 0,
1166 &yy_ei.drop_delay_random
1170 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1171 &xx_ei.move_delay_fixed, 0,
1172 &yy_ei.move_delay_fixed
1176 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1177 &xx_ei.move_delay_random, 0,
1178 &yy_ei.move_delay_random
1182 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1183 &xx_ei.step_delay_fixed, 0,
1184 &yy_ei.step_delay_fixed
1188 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1189 &xx_ei.step_delay_random, 0,
1190 &yy_ei.step_delay_random
1195 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1196 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1201 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1202 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1203 &yy_ei.move_direction_initial
1207 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1208 &xx_ei.move_stepsize, TILEX / 8,
1209 &yy_ei.move_stepsize
1214 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1215 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1216 &yy_ei.move_enter_element
1220 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1221 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1222 &yy_ei.move_leave_element
1226 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1227 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1228 &yy_ei.move_leave_type
1233 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1234 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1235 &yy_ei.slippery_type
1240 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1241 &xx_ei.explosion_type, EXPLODES_3X3,
1242 &yy_ei.explosion_type
1246 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1247 &xx_ei.explosion_delay, 16,
1248 &yy_ei.explosion_delay
1252 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1253 &xx_ei.ignition_delay, 8,
1254 &yy_ei.ignition_delay
1259 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1260 &xx_ei.content, EL_EMPTY_SPACE,
1262 &xx_num_contents, 1, 1
1265 // ---------- "num_change_pages" must be the last entry ---------------------
1268 -1, SAVE_CONF_ALWAYS,
1269 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1270 &xx_ei.num_change_pages, 1,
1271 &yy_ei.num_change_pages
1282 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1284 // ---------- "current_change_page" must be the first entry -----------------
1287 -1, SAVE_CONF_ALWAYS,
1288 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1289 &xx_current_change_page, -1
1292 // ---------- (the remaining entries can be in any order) -------------------
1296 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1297 &xx_change.can_change, FALSE
1302 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1303 &xx_event_bits[0], 0
1307 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1308 &xx_event_bits[1], 0
1313 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1314 &xx_change.trigger_player, CH_PLAYER_ANY
1318 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1319 &xx_change.trigger_side, CH_SIDE_ANY
1323 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1324 &xx_change.trigger_page, CH_PAGE_ANY
1329 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1330 &xx_change.target_element, EL_EMPTY_SPACE
1335 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1336 &xx_change.delay_fixed, 0
1340 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1341 &xx_change.delay_random, 0
1345 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1346 &xx_change.delay_frames, FRAMES_PER_SECOND
1351 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1352 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1357 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1358 &xx_change.explode, FALSE
1362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1363 &xx_change.use_target_content, FALSE
1367 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1368 &xx_change.only_if_complete, FALSE
1372 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1373 &xx_change.use_random_replace, FALSE
1377 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1378 &xx_change.random_percentage, 100
1382 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1383 &xx_change.replace_when, CP_WHEN_EMPTY
1388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1389 &xx_change.has_action, FALSE
1393 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1394 &xx_change.action_type, CA_NO_ACTION
1398 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1399 &xx_change.action_mode, CA_MODE_UNDEFINED
1403 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1404 &xx_change.action_arg, CA_ARG_UNDEFINED
1409 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1410 &xx_change.action_element, EL_EMPTY_SPACE
1415 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1416 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1417 &xx_num_contents, 1, 1
1427 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1431 TYPE_STRING, CONF_VALUE_BYTES(1),
1432 &xx_ei.description[0], -1, NULL,
1433 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1434 &xx_default_description[0]
1439 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1440 &xx_ei.use_gfx_element, FALSE
1444 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1445 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1450 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1451 &xx_group.choice_mode, ANIM_RANDOM
1456 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1457 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1458 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1468 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1472 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1473 &xx_ei.use_gfx_element, FALSE
1477 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1478 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1488 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1492 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1493 &li.block_snap_field, TRUE
1497 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1498 &li.continuous_snapping, TRUE
1502 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1503 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1508 &li.use_start_element[0], FALSE
1512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1513 &li.start_element[0], EL_PLAYER_1
1517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1518 &li.use_artwork_element[0], FALSE
1522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1523 &li.artwork_element[0], EL_PLAYER_1
1527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1528 &li.use_explosion_element[0], FALSE
1532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1533 &li.explosion_element[0], EL_PLAYER_1
1548 filetype_id_list[] =
1550 { LEVEL_FILE_TYPE_RND, "RND" },
1551 { LEVEL_FILE_TYPE_BD, "BD" },
1552 { LEVEL_FILE_TYPE_EM, "EM" },
1553 { LEVEL_FILE_TYPE_SP, "SP" },
1554 { LEVEL_FILE_TYPE_DX, "DX" },
1555 { LEVEL_FILE_TYPE_SB, "SB" },
1556 { LEVEL_FILE_TYPE_DC, "DC" },
1557 { LEVEL_FILE_TYPE_MM, "MM" },
1558 { LEVEL_FILE_TYPE_MM, "DF" },
1563 // ============================================================================
1564 // level file functions
1565 // ============================================================================
1567 static boolean check_special_flags(char *flag)
1569 if (strEqual(options.special_flags, flag) ||
1570 strEqual(leveldir_current->special_flags, flag))
1576 static struct DateInfo getCurrentDate(void)
1578 time_t epoch_seconds = time(NULL);
1579 struct tm *now = localtime(&epoch_seconds);
1580 struct DateInfo date;
1582 date.year = now->tm_year + 1900;
1583 date.month = now->tm_mon + 1;
1584 date.day = now->tm_mday;
1586 date.src = DATE_SRC_CLOCK;
1591 static void resetEventFlags(struct ElementChangeInfo *change)
1595 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1596 change->has_event[i] = FALSE;
1599 static void resetEventBits(void)
1603 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1604 xx_event_bits[i] = 0;
1607 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1611 /* important: only change event flag if corresponding event bit is set
1612 (this is because all xx_event_bits[] values are loaded separately,
1613 and all xx_event_bits[] values are set back to zero before loading
1614 another value xx_event_bits[x] (each value representing 32 flags)) */
1616 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1617 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1618 change->has_event[i] = TRUE;
1621 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1625 /* in contrast to the above function setEventFlagsFromEventBits(), it
1626 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1627 depending on the corresponding change->has_event[i] values here, as
1628 all xx_event_bits[] values are reset in resetEventBits() before */
1630 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1631 if (change->has_event[i])
1632 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1635 static char *getDefaultElementDescription(struct ElementInfo *ei)
1637 static char description[MAX_ELEMENT_NAME_LEN + 1];
1638 char *default_description = (ei->custom_description != NULL ?
1639 ei->custom_description :
1640 ei->editor_description);
1643 // always start with reliable default values
1644 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1645 description[i] = '\0';
1647 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1648 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1650 return &description[0];
1653 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1655 char *default_description = getDefaultElementDescription(ei);
1658 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1659 ei->description[i] = default_description[i];
1662 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1666 for (i = 0; conf[i].data_type != -1; i++)
1668 int default_value = conf[i].default_value;
1669 int data_type = conf[i].data_type;
1670 int conf_type = conf[i].conf_type;
1671 int byte_mask = conf_type & CONF_MASK_BYTES;
1673 if (byte_mask == CONF_MASK_MULTI_BYTES)
1675 int default_num_entities = conf[i].default_num_entities;
1676 int max_num_entities = conf[i].max_num_entities;
1678 *(int *)(conf[i].num_entities) = default_num_entities;
1680 if (data_type == TYPE_STRING)
1682 char *default_string = conf[i].default_string;
1683 char *string = (char *)(conf[i].value);
1685 strncpy(string, default_string, max_num_entities);
1687 else if (data_type == TYPE_ELEMENT_LIST)
1689 int *element_array = (int *)(conf[i].value);
1692 for (j = 0; j < max_num_entities; j++)
1693 element_array[j] = default_value;
1695 else if (data_type == TYPE_CONTENT_LIST)
1697 struct Content *content = (struct Content *)(conf[i].value);
1700 for (c = 0; c < max_num_entities; c++)
1701 for (y = 0; y < 3; y++)
1702 for (x = 0; x < 3; x++)
1703 content[c].e[x][y] = default_value;
1706 else // constant size configuration data (1, 2 or 4 bytes)
1708 if (data_type == TYPE_BOOLEAN)
1709 *(boolean *)(conf[i].value) = default_value;
1711 *(int *) (conf[i].value) = default_value;
1716 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1720 for (i = 0; conf[i].data_type != -1; i++)
1722 int data_type = conf[i].data_type;
1723 int conf_type = conf[i].conf_type;
1724 int byte_mask = conf_type & CONF_MASK_BYTES;
1726 if (byte_mask == CONF_MASK_MULTI_BYTES)
1728 int max_num_entities = conf[i].max_num_entities;
1730 if (data_type == TYPE_STRING)
1732 char *string = (char *)(conf[i].value);
1733 char *string_copy = (char *)(conf[i].value_copy);
1735 strncpy(string_copy, string, max_num_entities);
1737 else if (data_type == TYPE_ELEMENT_LIST)
1739 int *element_array = (int *)(conf[i].value);
1740 int *element_array_copy = (int *)(conf[i].value_copy);
1743 for (j = 0; j < max_num_entities; j++)
1744 element_array_copy[j] = element_array[j];
1746 else if (data_type == TYPE_CONTENT_LIST)
1748 struct Content *content = (struct Content *)(conf[i].value);
1749 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1752 for (c = 0; c < max_num_entities; c++)
1753 for (y = 0; y < 3; y++)
1754 for (x = 0; x < 3; x++)
1755 content_copy[c].e[x][y] = content[c].e[x][y];
1758 else // constant size configuration data (1, 2 or 4 bytes)
1760 if (data_type == TYPE_BOOLEAN)
1761 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1763 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1768 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1772 xx_ei = *ei_from; // copy element data into temporary buffer
1773 yy_ei = *ei_to; // copy element data into temporary buffer
1775 copyConfigFromConfigList(chunk_config_CUSX_base);
1780 // ---------- reinitialize and copy change pages ----------
1782 ei_to->num_change_pages = ei_from->num_change_pages;
1783 ei_to->current_change_page = ei_from->current_change_page;
1785 setElementChangePages(ei_to, ei_to->num_change_pages);
1787 for (i = 0; i < ei_to->num_change_pages; i++)
1788 ei_to->change_page[i] = ei_from->change_page[i];
1790 // ---------- copy group element info ----------
1791 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1792 *ei_to->group = *ei_from->group;
1794 // mark this custom element as modified
1795 ei_to->modified_settings = TRUE;
1798 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1800 int change_page_size = sizeof(struct ElementChangeInfo);
1802 ei->num_change_pages = MAX(1, change_pages);
1805 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1807 if (ei->current_change_page >= ei->num_change_pages)
1808 ei->current_change_page = ei->num_change_pages - 1;
1810 ei->change = &ei->change_page[ei->current_change_page];
1813 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1815 xx_change = *change; // copy change data into temporary buffer
1817 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1819 *change = xx_change;
1821 resetEventFlags(change);
1823 change->direct_action = 0;
1824 change->other_action = 0;
1826 change->pre_change_function = NULL;
1827 change->change_function = NULL;
1828 change->post_change_function = NULL;
1831 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1835 li = *level; // copy level data into temporary buffer
1836 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1837 *level = li; // copy temporary buffer back to level data
1839 setLevelInfoToDefaults_BD();
1840 setLevelInfoToDefaults_EM();
1841 setLevelInfoToDefaults_SP();
1842 setLevelInfoToDefaults_MM();
1844 level->native_bd_level = &native_bd_level;
1845 level->native_em_level = &native_em_level;
1846 level->native_sp_level = &native_sp_level;
1847 level->native_mm_level = &native_mm_level;
1849 level->file_version = FILE_VERSION_ACTUAL;
1850 level->game_version = GAME_VERSION_ACTUAL;
1852 level->creation_date = getCurrentDate();
1854 level->encoding_16bit_field = TRUE;
1855 level->encoding_16bit_yamyam = TRUE;
1856 level->encoding_16bit_amoeba = TRUE;
1858 // clear level name and level author string buffers
1859 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1860 level->name[i] = '\0';
1861 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1862 level->author[i] = '\0';
1864 // set level name and level author to default values
1865 strcpy(level->name, NAMELESS_LEVEL_NAME);
1866 strcpy(level->author, ANONYMOUS_NAME);
1868 // set level playfield to playable default level with player and exit
1869 for (x = 0; x < MAX_LEV_FIELDX; x++)
1870 for (y = 0; y < MAX_LEV_FIELDY; y++)
1871 level->field[x][y] = EL_SAND;
1873 level->field[0][0] = EL_PLAYER_1;
1874 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1876 BorderElement = EL_STEELWALL;
1878 // detect custom elements when loading them
1879 level->file_has_custom_elements = FALSE;
1881 // set all bug compatibility flags to "false" => do not emulate this bug
1882 level->use_action_after_change_bug = FALSE;
1884 if (leveldir_current)
1886 // try to determine better author name than 'anonymous'
1887 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1889 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1890 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1894 switch (LEVELCLASS(leveldir_current))
1896 case LEVELCLASS_TUTORIAL:
1897 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1900 case LEVELCLASS_CONTRIB:
1901 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1902 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1905 case LEVELCLASS_PRIVATE:
1906 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1907 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1911 // keep default value
1918 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1920 static boolean clipboard_elements_initialized = FALSE;
1923 InitElementPropertiesStatic();
1925 li = *level; // copy level data into temporary buffer
1926 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1927 *level = li; // copy temporary buffer back to level data
1929 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1932 struct ElementInfo *ei = &element_info[element];
1934 if (element == EL_MM_GRAY_BALL)
1936 struct LevelInfo_MM *level_mm = level->native_mm_level;
1939 for (j = 0; j < level->num_mm_ball_contents; j++)
1940 level->mm_ball_content[j] =
1941 map_element_MM_to_RND(level_mm->ball_content[j]);
1944 // never initialize clipboard elements after the very first time
1945 // (to be able to use clipboard elements between several levels)
1946 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1949 if (IS_ENVELOPE(element))
1951 int envelope_nr = element - EL_ENVELOPE_1;
1953 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1955 level->envelope[envelope_nr] = xx_envelope;
1958 if (IS_CUSTOM_ELEMENT(element) ||
1959 IS_GROUP_ELEMENT(element) ||
1960 IS_INTERNAL_ELEMENT(element))
1962 xx_ei = *ei; // copy element data into temporary buffer
1964 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1969 setElementChangePages(ei, 1);
1970 setElementChangeInfoToDefaults(ei->change);
1972 if (IS_CUSTOM_ELEMENT(element) ||
1973 IS_GROUP_ELEMENT(element))
1975 setElementDescriptionToDefault(ei);
1977 ei->modified_settings = FALSE;
1980 if (IS_CUSTOM_ELEMENT(element) ||
1981 IS_INTERNAL_ELEMENT(element))
1983 // internal values used in level editor
1985 ei->access_type = 0;
1986 ei->access_layer = 0;
1987 ei->access_protected = 0;
1988 ei->walk_to_action = 0;
1989 ei->smash_targets = 0;
1992 ei->can_explode_by_fire = FALSE;
1993 ei->can_explode_smashed = FALSE;
1994 ei->can_explode_impact = FALSE;
1996 ei->current_change_page = 0;
1999 if (IS_GROUP_ELEMENT(element) ||
2000 IS_INTERNAL_ELEMENT(element))
2002 struct ElementGroupInfo *group;
2004 // initialize memory for list of elements in group
2005 if (ei->group == NULL)
2006 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2010 xx_group = *group; // copy group data into temporary buffer
2012 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2017 if (IS_EMPTY_ELEMENT(element) ||
2018 IS_INTERNAL_ELEMENT(element))
2020 xx_ei = *ei; // copy element data into temporary buffer
2022 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2028 clipboard_elements_initialized = TRUE;
2031 static void setLevelInfoToDefaults(struct LevelInfo *level,
2032 boolean level_info_only,
2033 boolean reset_file_status)
2035 setLevelInfoToDefaults_Level(level);
2037 if (!level_info_only)
2038 setLevelInfoToDefaults_Elements(level);
2040 if (reset_file_status)
2042 level->no_valid_file = FALSE;
2043 level->no_level_file = FALSE;
2046 level->changed = FALSE;
2049 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2051 level_file_info->nr = 0;
2052 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2053 level_file_info->packed = FALSE;
2055 setString(&level_file_info->basename, NULL);
2056 setString(&level_file_info->filename, NULL);
2059 int getMappedElement_SB(int, boolean);
2061 static void ActivateLevelTemplate(void)
2065 if (check_special_flags("load_xsb_to_ces"))
2067 // fill smaller playfields with padding "beyond border wall" elements
2068 if (level.fieldx < level_template.fieldx ||
2069 level.fieldy < level_template.fieldy)
2071 short field[level.fieldx][level.fieldy];
2072 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2073 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2074 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2075 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2077 // copy old playfield (which is smaller than the visible area)
2078 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2079 field[x][y] = level.field[x][y];
2081 // fill new, larger playfield with "beyond border wall" elements
2082 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2083 level.field[x][y] = getMappedElement_SB('_', TRUE);
2085 // copy the old playfield to the middle of the new playfield
2086 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2087 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2089 level.fieldx = new_fieldx;
2090 level.fieldy = new_fieldy;
2094 // Currently there is no special action needed to activate the template
2095 // data, because 'element_info' property settings overwrite the original
2096 // level data, while all other variables do not change.
2098 // Exception: 'from_level_template' elements in the original level playfield
2099 // are overwritten with the corresponding elements at the same position in
2100 // playfield from the level template.
2102 for (x = 0; x < level.fieldx; x++)
2103 for (y = 0; y < level.fieldy; y++)
2104 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2105 level.field[x][y] = level_template.field[x][y];
2107 if (check_special_flags("load_xsb_to_ces"))
2109 struct LevelInfo level_backup = level;
2111 // overwrite all individual level settings from template level settings
2112 level = level_template;
2114 // restore level file info
2115 level.file_info = level_backup.file_info;
2117 // restore playfield size
2118 level.fieldx = level_backup.fieldx;
2119 level.fieldy = level_backup.fieldy;
2121 // restore playfield content
2122 for (x = 0; x < level.fieldx; x++)
2123 for (y = 0; y < level.fieldy; y++)
2124 level.field[x][y] = level_backup.field[x][y];
2126 // restore name and author from individual level
2127 strcpy(level.name, level_backup.name);
2128 strcpy(level.author, level_backup.author);
2130 // restore flag "use_custom_template"
2131 level.use_custom_template = level_backup.use_custom_template;
2135 static boolean checkForPackageFromBasename_BD(char *basename)
2137 // check for native BD level file extensions
2138 if (!strSuffixLower(basename, ".bd") &&
2139 !strSuffixLower(basename, ".bdr") &&
2140 !strSuffixLower(basename, ".brc") &&
2141 !strSuffixLower(basename, ".gds"))
2144 // check for standard single-level BD files (like "001.bd")
2145 if (strSuffixLower(basename, ".bd") &&
2146 strlen(basename) == 6 &&
2147 basename[0] >= '0' && basename[0] <= '9' &&
2148 basename[1] >= '0' && basename[1] <= '9' &&
2149 basename[2] >= '0' && basename[2] <= '9')
2152 // this is a level package in native BD file format
2156 static char *getLevelFilenameFromBasename(char *basename)
2158 static char *filename = NULL;
2160 checked_free(filename);
2162 filename = getPath2(getCurrentLevelDir(), basename);
2167 static int getFileTypeFromBasename(char *basename)
2169 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2171 static char *filename = NULL;
2172 struct stat file_status;
2174 // ---------- try to determine file type from filename ----------
2176 // check for typical filename of a Supaplex level package file
2177 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2178 return LEVEL_FILE_TYPE_SP;
2180 // check for typical filename of a Diamond Caves II level package file
2181 if (strSuffixLower(basename, ".dc") ||
2182 strSuffixLower(basename, ".dc2"))
2183 return LEVEL_FILE_TYPE_DC;
2185 // check for typical filename of a Sokoban level package file
2186 if (strSuffixLower(basename, ".xsb") &&
2187 strchr(basename, '%') == NULL)
2188 return LEVEL_FILE_TYPE_SB;
2190 // check for typical filename of a Boulder Dash (GDash) level package file
2191 if (checkForPackageFromBasename_BD(basename))
2192 return LEVEL_FILE_TYPE_BD;
2194 // ---------- try to determine file type from filesize ----------
2196 checked_free(filename);
2197 filename = getPath2(getCurrentLevelDir(), basename);
2199 if (stat(filename, &file_status) == 0)
2201 // check for typical filesize of a Supaplex level package file
2202 if (file_status.st_size == 170496)
2203 return LEVEL_FILE_TYPE_SP;
2206 return LEVEL_FILE_TYPE_UNKNOWN;
2209 static int getFileTypeFromMagicBytes(char *filename, int type)
2213 if ((file = openFile(filename, MODE_READ)))
2215 char chunk_name[CHUNK_ID_LEN + 1];
2217 getFileChunkBE(file, chunk_name, NULL);
2219 if (strEqual(chunk_name, "MMII") ||
2220 strEqual(chunk_name, "MIRR"))
2221 type = LEVEL_FILE_TYPE_MM;
2229 static boolean checkForPackageFromBasename(char *basename)
2231 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2232 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2234 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2237 static char *getSingleLevelBasenameExt(int nr, char *extension)
2239 static char basename[MAX_FILENAME_LEN];
2242 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2244 sprintf(basename, "%03d.%s", nr, extension);
2249 static char *getSingleLevelBasename(int nr)
2251 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2254 static char *getPackedLevelBasename(int type)
2256 static char basename[MAX_FILENAME_LEN];
2257 char *directory = getCurrentLevelDir();
2259 DirectoryEntry *dir_entry;
2261 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2263 if ((dir = openDirectory(directory)) == NULL)
2265 Warn("cannot read current level directory '%s'", directory);
2270 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2272 char *entry_basename = dir_entry->basename;
2273 int entry_type = getFileTypeFromBasename(entry_basename);
2275 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2277 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2280 strcpy(basename, entry_basename);
2287 closeDirectory(dir);
2292 static char *getSingleLevelFilename(int nr)
2294 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2297 #if ENABLE_UNUSED_CODE
2298 static char *getPackedLevelFilename(int type)
2300 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2304 char *getDefaultLevelFilename(int nr)
2306 return getSingleLevelFilename(nr);
2309 #if ENABLE_UNUSED_CODE
2310 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2314 lfi->packed = FALSE;
2316 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2317 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2321 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2322 int type, char *format, ...)
2324 static char basename[MAX_FILENAME_LEN];
2327 va_start(ap, format);
2328 vsprintf(basename, format, ap);
2332 lfi->packed = FALSE;
2334 setString(&lfi->basename, basename);
2335 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2338 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2344 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2345 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2348 static int getFiletypeFromID(char *filetype_id)
2350 char *filetype_id_lower;
2351 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2354 if (filetype_id == NULL)
2355 return LEVEL_FILE_TYPE_UNKNOWN;
2357 filetype_id_lower = getStringToLower(filetype_id);
2359 for (i = 0; filetype_id_list[i].id != NULL; i++)
2361 char *id_lower = getStringToLower(filetype_id_list[i].id);
2363 if (strEqual(filetype_id_lower, id_lower))
2364 filetype = filetype_id_list[i].filetype;
2368 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2372 free(filetype_id_lower);
2377 char *getLocalLevelTemplateFilename(void)
2379 return getDefaultLevelFilename(-1);
2382 char *getGlobalLevelTemplateFilename(void)
2384 // global variable "leveldir_current" must be modified in the loop below
2385 LevelDirTree *leveldir_current_last = leveldir_current;
2386 char *filename = NULL;
2388 // check for template level in path from current to topmost tree node
2390 while (leveldir_current != NULL)
2392 filename = getDefaultLevelFilename(-1);
2394 if (fileExists(filename))
2397 leveldir_current = leveldir_current->node_parent;
2400 // restore global variable "leveldir_current" modified in above loop
2401 leveldir_current = leveldir_current_last;
2406 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2410 // special case: level number is negative => check for level template file
2413 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2414 getSingleLevelBasename(-1));
2416 // replace local level template filename with global template filename
2417 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2419 // no fallback if template file not existing
2423 // special case: check for file name/pattern specified in "levelinfo.conf"
2424 if (leveldir_current->level_filename != NULL)
2426 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2428 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2429 leveldir_current->level_filename, nr);
2431 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2433 if (fileExists(lfi->filename))
2436 else if (leveldir_current->level_filetype != NULL)
2438 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2440 // check for specified native level file with standard file name
2441 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2442 "%03d.%s", nr, LEVELFILE_EXTENSION);
2443 if (fileExists(lfi->filename))
2447 // check for native Rocks'n'Diamonds level file
2448 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2449 "%03d.%s", nr, LEVELFILE_EXTENSION);
2450 if (fileExists(lfi->filename))
2453 // check for native Boulder Dash level file
2454 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2455 if (fileExists(lfi->filename))
2458 // check for Emerald Mine level file (V1)
2459 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2460 'a' + (nr / 10) % 26, '0' + nr % 10);
2461 if (fileExists(lfi->filename))
2463 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2464 'A' + (nr / 10) % 26, '0' + nr % 10);
2465 if (fileExists(lfi->filename))
2468 // check for Emerald Mine level file (V2 to V5)
2469 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2470 if (fileExists(lfi->filename))
2473 // check for Emerald Mine level file (V6 / single mode)
2474 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2475 if (fileExists(lfi->filename))
2477 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2478 if (fileExists(lfi->filename))
2481 // check for Emerald Mine level file (V6 / teamwork mode)
2482 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2483 if (fileExists(lfi->filename))
2485 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2486 if (fileExists(lfi->filename))
2489 // check for various packed level file formats
2490 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2491 if (fileExists(lfi->filename))
2494 // no known level file found -- use default values (and fail later)
2495 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2496 "%03d.%s", nr, LEVELFILE_EXTENSION);
2499 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2501 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2502 lfi->type = getFileTypeFromBasename(lfi->basename);
2504 if (lfi->type == LEVEL_FILE_TYPE_RND)
2505 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2508 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2510 // always start with reliable default values
2511 setFileInfoToDefaults(level_file_info);
2513 level_file_info->nr = nr; // set requested level number
2515 determineLevelFileInfo_Filename(level_file_info);
2516 determineLevelFileInfo_Filetype(level_file_info);
2519 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2520 struct LevelFileInfo *lfi_to)
2522 lfi_to->nr = lfi_from->nr;
2523 lfi_to->type = lfi_from->type;
2524 lfi_to->packed = lfi_from->packed;
2526 setString(&lfi_to->basename, lfi_from->basename);
2527 setString(&lfi_to->filename, lfi_from->filename);
2530 // ----------------------------------------------------------------------------
2531 // functions for loading R'n'D level
2532 // ----------------------------------------------------------------------------
2534 int getMappedElement(int element)
2536 // remap some (historic, now obsolete) elements
2540 case EL_PLAYER_OBSOLETE:
2541 element = EL_PLAYER_1;
2544 case EL_KEY_OBSOLETE:
2548 case EL_EM_KEY_1_FILE_OBSOLETE:
2549 element = EL_EM_KEY_1;
2552 case EL_EM_KEY_2_FILE_OBSOLETE:
2553 element = EL_EM_KEY_2;
2556 case EL_EM_KEY_3_FILE_OBSOLETE:
2557 element = EL_EM_KEY_3;
2560 case EL_EM_KEY_4_FILE_OBSOLETE:
2561 element = EL_EM_KEY_4;
2564 case EL_ENVELOPE_OBSOLETE:
2565 element = EL_ENVELOPE_1;
2573 if (element >= NUM_FILE_ELEMENTS)
2575 Warn("invalid level element %d", element);
2577 element = EL_UNKNOWN;
2585 static int getMappedElementByVersion(int element, int game_version)
2587 // remap some elements due to certain game version
2589 if (game_version <= VERSION_IDENT(2,2,0,0))
2591 // map game font elements
2592 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2593 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2594 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2595 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2598 if (game_version < VERSION_IDENT(3,0,0,0))
2600 // map Supaplex gravity tube elements
2601 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2602 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2603 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2604 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2611 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2613 level->file_version = getFileVersion(file);
2614 level->game_version = getFileVersion(file);
2619 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2621 level->creation_date.year = getFile16BitBE(file);
2622 level->creation_date.month = getFile8Bit(file);
2623 level->creation_date.day = getFile8Bit(file);
2625 level->creation_date.src = DATE_SRC_LEVELFILE;
2630 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2632 int initial_player_stepsize;
2633 int initial_player_gravity;
2636 level->fieldx = getFile8Bit(file);
2637 level->fieldy = getFile8Bit(file);
2639 level->time = getFile16BitBE(file);
2640 level->gems_needed = getFile16BitBE(file);
2642 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2643 level->name[i] = getFile8Bit(file);
2644 level->name[MAX_LEVEL_NAME_LEN] = 0;
2646 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2647 level->score[i] = getFile8Bit(file);
2649 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2650 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2651 for (y = 0; y < 3; y++)
2652 for (x = 0; x < 3; x++)
2653 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2655 level->amoeba_speed = getFile8Bit(file);
2656 level->time_magic_wall = getFile8Bit(file);
2657 level->time_wheel = getFile8Bit(file);
2658 level->amoeba_content = getMappedElement(getFile8Bit(file));
2660 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2663 for (i = 0; i < MAX_PLAYERS; i++)
2664 level->initial_player_stepsize[i] = initial_player_stepsize;
2666 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2668 for (i = 0; i < MAX_PLAYERS; i++)
2669 level->initial_player_gravity[i] = initial_player_gravity;
2671 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2672 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2674 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2676 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2677 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2678 level->can_move_into_acid_bits = getFile32BitBE(file);
2679 level->dont_collide_with_bits = getFile8Bit(file);
2681 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2682 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2684 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2685 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2686 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2688 level->game_engine_type = getFile8Bit(file);
2690 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2695 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2699 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2700 level->name[i] = getFile8Bit(file);
2701 level->name[MAX_LEVEL_NAME_LEN] = 0;
2706 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2710 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2711 level->author[i] = getFile8Bit(file);
2712 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2717 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2720 int chunk_size_expected = level->fieldx * level->fieldy;
2722 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2723 stored with 16-bit encoding (and should be twice as big then).
2724 Even worse, playfield data was stored 16-bit when only yamyam content
2725 contained 16-bit elements and vice versa. */
2727 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2728 chunk_size_expected *= 2;
2730 if (chunk_size_expected != chunk_size)
2732 ReadUnusedBytesFromFile(file, chunk_size);
2733 return chunk_size_expected;
2736 for (y = 0; y < level->fieldy; y++)
2737 for (x = 0; x < level->fieldx; x++)
2738 level->field[x][y] =
2739 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2744 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2747 int header_size = 4;
2748 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2749 int chunk_size_expected = header_size + content_size;
2751 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2752 stored with 16-bit encoding (and should be twice as big then).
2753 Even worse, playfield data was stored 16-bit when only yamyam content
2754 contained 16-bit elements and vice versa. */
2756 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2757 chunk_size_expected += content_size;
2759 if (chunk_size_expected != chunk_size)
2761 ReadUnusedBytesFromFile(file, chunk_size);
2762 return chunk_size_expected;
2766 level->num_yamyam_contents = getFile8Bit(file);
2770 // correct invalid number of content fields -- should never happen
2771 if (level->num_yamyam_contents < 1 ||
2772 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2773 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2775 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2776 for (y = 0; y < 3; y++)
2777 for (x = 0; x < 3; x++)
2778 level->yamyam_content[i].e[x][y] =
2779 getMappedElement(level->encoding_16bit_field ?
2780 getFile16BitBE(file) : getFile8Bit(file));
2784 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2789 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2791 element = getMappedElement(getFile16BitBE(file));
2792 num_contents = getFile8Bit(file);
2794 getFile8Bit(file); // content x size (unused)
2795 getFile8Bit(file); // content y size (unused)
2797 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2799 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2800 for (y = 0; y < 3; y++)
2801 for (x = 0; x < 3; x++)
2802 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2804 // correct invalid number of content fields -- should never happen
2805 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2806 num_contents = STD_ELEMENT_CONTENTS;
2808 if (element == EL_YAMYAM)
2810 level->num_yamyam_contents = num_contents;
2812 for (i = 0; i < num_contents; i++)
2813 for (y = 0; y < 3; y++)
2814 for (x = 0; x < 3; x++)
2815 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2817 else if (element == EL_BD_AMOEBA)
2819 level->amoeba_content = content_array[0][0][0];
2823 Warn("cannot load content for element '%d'", element);
2829 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2835 int chunk_size_expected;
2837 element = getMappedElement(getFile16BitBE(file));
2838 if (!IS_ENVELOPE(element))
2839 element = EL_ENVELOPE_1;
2841 envelope_nr = element - EL_ENVELOPE_1;
2843 envelope_len = getFile16BitBE(file);
2845 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2846 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2848 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2850 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2851 if (chunk_size_expected != chunk_size)
2853 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2854 return chunk_size_expected;
2857 for (i = 0; i < envelope_len; i++)
2858 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2863 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2865 int num_changed_custom_elements = getFile16BitBE(file);
2866 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2869 if (chunk_size_expected != chunk_size)
2871 ReadUnusedBytesFromFile(file, chunk_size - 2);
2872 return chunk_size_expected;
2875 for (i = 0; i < num_changed_custom_elements; i++)
2877 int element = getMappedElement(getFile16BitBE(file));
2878 int properties = getFile32BitBE(file);
2880 if (IS_CUSTOM_ELEMENT(element))
2881 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2883 Warn("invalid custom element number %d", element);
2885 // older game versions that wrote level files with CUS1 chunks used
2886 // different default push delay values (not yet stored in level file)
2887 element_info[element].push_delay_fixed = 2;
2888 element_info[element].push_delay_random = 8;
2891 level->file_has_custom_elements = TRUE;
2896 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2898 int num_changed_custom_elements = getFile16BitBE(file);
2899 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2902 if (chunk_size_expected != chunk_size)
2904 ReadUnusedBytesFromFile(file, chunk_size - 2);
2905 return chunk_size_expected;
2908 for (i = 0; i < num_changed_custom_elements; i++)
2910 int element = getMappedElement(getFile16BitBE(file));
2911 int custom_target_element = getMappedElement(getFile16BitBE(file));
2913 if (IS_CUSTOM_ELEMENT(element))
2914 element_info[element].change->target_element = custom_target_element;
2916 Warn("invalid custom element number %d", element);
2919 level->file_has_custom_elements = TRUE;
2924 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2926 int num_changed_custom_elements = getFile16BitBE(file);
2927 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2930 if (chunk_size_expected != chunk_size)
2932 ReadUnusedBytesFromFile(file, chunk_size - 2);
2933 return chunk_size_expected;
2936 for (i = 0; i < num_changed_custom_elements; i++)
2938 int element = getMappedElement(getFile16BitBE(file));
2939 struct ElementInfo *ei = &element_info[element];
2940 unsigned int event_bits;
2942 if (!IS_CUSTOM_ELEMENT(element))
2944 Warn("invalid custom element number %d", element);
2946 element = EL_INTERNAL_DUMMY;
2949 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2950 ei->description[j] = getFile8Bit(file);
2951 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2953 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2955 // some free bytes for future properties and padding
2956 ReadUnusedBytesFromFile(file, 7);
2958 ei->use_gfx_element = getFile8Bit(file);
2959 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2961 ei->collect_score_initial = getFile8Bit(file);
2962 ei->collect_count_initial = getFile8Bit(file);
2964 ei->push_delay_fixed = getFile16BitBE(file);
2965 ei->push_delay_random = getFile16BitBE(file);
2966 ei->move_delay_fixed = getFile16BitBE(file);
2967 ei->move_delay_random = getFile16BitBE(file);
2969 ei->move_pattern = getFile16BitBE(file);
2970 ei->move_direction_initial = getFile8Bit(file);
2971 ei->move_stepsize = getFile8Bit(file);
2973 for (y = 0; y < 3; y++)
2974 for (x = 0; x < 3; x++)
2975 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2977 // bits 0 - 31 of "has_event[]"
2978 event_bits = getFile32BitBE(file);
2979 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2980 if (event_bits & (1u << j))
2981 ei->change->has_event[j] = TRUE;
2983 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2985 ei->change->delay_fixed = getFile16BitBE(file);
2986 ei->change->delay_random = getFile16BitBE(file);
2987 ei->change->delay_frames = getFile16BitBE(file);
2989 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2991 ei->change->explode = getFile8Bit(file);
2992 ei->change->use_target_content = getFile8Bit(file);
2993 ei->change->only_if_complete = getFile8Bit(file);
2994 ei->change->use_random_replace = getFile8Bit(file);
2996 ei->change->random_percentage = getFile8Bit(file);
2997 ei->change->replace_when = getFile8Bit(file);
2999 for (y = 0; y < 3; y++)
3000 for (x = 0; x < 3; x++)
3001 ei->change->target_content.e[x][y] =
3002 getMappedElement(getFile16BitBE(file));
3004 ei->slippery_type = getFile8Bit(file);
3006 // some free bytes for future properties and padding
3007 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3009 // mark that this custom element has been modified
3010 ei->modified_settings = TRUE;
3013 level->file_has_custom_elements = TRUE;
3018 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3020 struct ElementInfo *ei;
3021 int chunk_size_expected;
3025 // ---------- custom element base property values (96 bytes) ----------------
3027 element = getMappedElement(getFile16BitBE(file));
3029 if (!IS_CUSTOM_ELEMENT(element))
3031 Warn("invalid custom element number %d", element);
3033 ReadUnusedBytesFromFile(file, chunk_size - 2);
3038 ei = &element_info[element];
3040 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3041 ei->description[i] = getFile8Bit(file);
3042 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3044 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3046 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3048 ei->num_change_pages = getFile8Bit(file);
3050 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3051 if (chunk_size_expected != chunk_size)
3053 ReadUnusedBytesFromFile(file, chunk_size - 43);
3054 return chunk_size_expected;
3057 ei->ce_value_fixed_initial = getFile16BitBE(file);
3058 ei->ce_value_random_initial = getFile16BitBE(file);
3059 ei->use_last_ce_value = getFile8Bit(file);
3061 ei->use_gfx_element = getFile8Bit(file);
3062 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3064 ei->collect_score_initial = getFile8Bit(file);
3065 ei->collect_count_initial = getFile8Bit(file);
3067 ei->drop_delay_fixed = getFile8Bit(file);
3068 ei->push_delay_fixed = getFile8Bit(file);
3069 ei->drop_delay_random = getFile8Bit(file);
3070 ei->push_delay_random = getFile8Bit(file);
3071 ei->move_delay_fixed = getFile16BitBE(file);
3072 ei->move_delay_random = getFile16BitBE(file);
3074 // bits 0 - 15 of "move_pattern" ...
3075 ei->move_pattern = getFile16BitBE(file);
3076 ei->move_direction_initial = getFile8Bit(file);
3077 ei->move_stepsize = getFile8Bit(file);
3079 ei->slippery_type = getFile8Bit(file);
3081 for (y = 0; y < 3; y++)
3082 for (x = 0; x < 3; x++)
3083 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3085 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3086 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3087 ei->move_leave_type = getFile8Bit(file);
3089 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3090 ei->move_pattern |= (getFile16BitBE(file) << 16);
3092 ei->access_direction = getFile8Bit(file);
3094 ei->explosion_delay = getFile8Bit(file);
3095 ei->ignition_delay = getFile8Bit(file);
3096 ei->explosion_type = getFile8Bit(file);
3098 // some free bytes for future custom property values and padding
3099 ReadUnusedBytesFromFile(file, 1);
3101 // ---------- change page property values (48 bytes) ------------------------
3103 setElementChangePages(ei, ei->num_change_pages);
3105 for (i = 0; i < ei->num_change_pages; i++)
3107 struct ElementChangeInfo *change = &ei->change_page[i];
3108 unsigned int event_bits;
3110 // always start with reliable default values
3111 setElementChangeInfoToDefaults(change);
3113 // bits 0 - 31 of "has_event[]" ...
3114 event_bits = getFile32BitBE(file);
3115 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3116 if (event_bits & (1u << j))
3117 change->has_event[j] = TRUE;
3119 change->target_element = getMappedElement(getFile16BitBE(file));
3121 change->delay_fixed = getFile16BitBE(file);
3122 change->delay_random = getFile16BitBE(file);
3123 change->delay_frames = getFile16BitBE(file);
3125 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3127 change->explode = getFile8Bit(file);
3128 change->use_target_content = getFile8Bit(file);
3129 change->only_if_complete = getFile8Bit(file);
3130 change->use_random_replace = getFile8Bit(file);
3132 change->random_percentage = getFile8Bit(file);
3133 change->replace_when = getFile8Bit(file);
3135 for (y = 0; y < 3; y++)
3136 for (x = 0; x < 3; x++)
3137 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3139 change->can_change = getFile8Bit(file);
3141 change->trigger_side = getFile8Bit(file);
3143 change->trigger_player = getFile8Bit(file);
3144 change->trigger_page = getFile8Bit(file);
3146 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3147 CH_PAGE_ANY : (1 << change->trigger_page));
3149 change->has_action = getFile8Bit(file);
3150 change->action_type = getFile8Bit(file);
3151 change->action_mode = getFile8Bit(file);
3152 change->action_arg = getFile16BitBE(file);
3154 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3155 event_bits = getFile8Bit(file);
3156 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3157 if (event_bits & (1u << (j - 32)))
3158 change->has_event[j] = TRUE;
3161 // mark this custom element as modified
3162 ei->modified_settings = TRUE;
3164 level->file_has_custom_elements = TRUE;
3169 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3171 struct ElementInfo *ei;
3172 struct ElementGroupInfo *group;
3176 element = getMappedElement(getFile16BitBE(file));
3178 if (!IS_GROUP_ELEMENT(element))
3180 Warn("invalid group element number %d", element);
3182 ReadUnusedBytesFromFile(file, chunk_size - 2);
3187 ei = &element_info[element];
3189 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3190 ei->description[i] = getFile8Bit(file);
3191 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3193 group = element_info[element].group;
3195 group->num_elements = getFile8Bit(file);
3197 ei->use_gfx_element = getFile8Bit(file);
3198 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3200 group->choice_mode = getFile8Bit(file);
3202 // some free bytes for future values and padding
3203 ReadUnusedBytesFromFile(file, 3);
3205 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3206 group->element[i] = getMappedElement(getFile16BitBE(file));
3208 // mark this group element as modified
3209 element_info[element].modified_settings = TRUE;
3211 level->file_has_custom_elements = TRUE;
3216 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3217 int element, int real_element)
3219 int micro_chunk_size = 0;
3220 int conf_type = getFile8Bit(file);
3221 int byte_mask = conf_type & CONF_MASK_BYTES;
3222 boolean element_found = FALSE;
3225 micro_chunk_size += 1;
3227 if (byte_mask == CONF_MASK_MULTI_BYTES)
3229 int num_bytes = getFile16BitBE(file);
3230 byte *buffer = checked_malloc(num_bytes);
3232 ReadBytesFromFile(file, buffer, num_bytes);
3234 for (i = 0; conf[i].data_type != -1; i++)
3236 if (conf[i].element == element &&
3237 conf[i].conf_type == conf_type)
3239 int data_type = conf[i].data_type;
3240 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3241 int max_num_entities = conf[i].max_num_entities;
3243 if (num_entities > max_num_entities)
3245 Warn("truncating number of entities for element %d from %d to %d",
3246 element, num_entities, max_num_entities);
3248 num_entities = max_num_entities;
3251 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3252 data_type == TYPE_CONTENT_LIST))
3254 // for element and content lists, zero entities are not allowed
3255 Warn("found empty list of entities for element %d", element);
3257 // do not set "num_entities" here to prevent reading behind buffer
3259 *(int *)(conf[i].num_entities) = 1; // at least one is required
3263 *(int *)(conf[i].num_entities) = num_entities;
3266 element_found = TRUE;
3268 if (data_type == TYPE_STRING)
3270 char *string = (char *)(conf[i].value);
3273 for (j = 0; j < max_num_entities; j++)
3274 string[j] = (j < num_entities ? buffer[j] : '\0');
3276 else if (data_type == TYPE_ELEMENT_LIST)
3278 int *element_array = (int *)(conf[i].value);
3281 for (j = 0; j < num_entities; j++)
3283 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3285 else if (data_type == TYPE_CONTENT_LIST)
3287 struct Content *content= (struct Content *)(conf[i].value);
3290 for (c = 0; c < num_entities; c++)
3291 for (y = 0; y < 3; y++)
3292 for (x = 0; x < 3; x++)
3293 content[c].e[x][y] =
3294 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3297 element_found = FALSE;
3303 checked_free(buffer);
3305 micro_chunk_size += 2 + num_bytes;
3307 else // constant size configuration data (1, 2 or 4 bytes)
3309 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3310 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3311 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3313 for (i = 0; conf[i].data_type != -1; i++)
3315 if (conf[i].element == element &&
3316 conf[i].conf_type == conf_type)
3318 int data_type = conf[i].data_type;
3320 if (data_type == TYPE_ELEMENT)
3321 value = getMappedElement(value);
3323 if (data_type == TYPE_BOOLEAN)
3324 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3326 *(int *) (conf[i].value) = value;
3328 element_found = TRUE;
3334 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3339 char *error_conf_chunk_bytes =
3340 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3341 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3342 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3343 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3344 int error_element = real_element;
3346 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3347 error_conf_chunk_bytes, error_conf_chunk_token,
3348 error_element, EL_NAME(error_element));
3351 return micro_chunk_size;
3354 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3356 int real_chunk_size = 0;
3358 li = *level; // copy level data into temporary buffer
3360 while (!checkEndOfFile(file))
3362 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3364 if (real_chunk_size >= chunk_size)
3368 *level = li; // copy temporary buffer back to level data
3370 return real_chunk_size;
3373 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3375 int real_chunk_size = 0;
3377 li = *level; // copy level data into temporary buffer
3379 while (!checkEndOfFile(file))
3381 int element = getMappedElement(getFile16BitBE(file));
3383 real_chunk_size += 2;
3384 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3386 if (real_chunk_size >= chunk_size)
3390 *level = li; // copy temporary buffer back to level data
3392 return real_chunk_size;
3395 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3397 int real_chunk_size = 0;
3399 li = *level; // copy level data into temporary buffer
3401 while (!checkEndOfFile(file))
3403 int element = getMappedElement(getFile16BitBE(file));
3405 real_chunk_size += 2;
3406 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3408 if (real_chunk_size >= chunk_size)
3412 *level = li; // copy temporary buffer back to level data
3414 return real_chunk_size;
3417 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3419 int element = getMappedElement(getFile16BitBE(file));
3420 int envelope_nr = element - EL_ENVELOPE_1;
3421 int real_chunk_size = 2;
3423 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3425 while (!checkEndOfFile(file))
3427 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3430 if (real_chunk_size >= chunk_size)
3434 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3436 return real_chunk_size;
3439 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3441 int element = getMappedElement(getFile16BitBE(file));
3442 int real_chunk_size = 2;
3443 struct ElementInfo *ei = &element_info[element];
3446 xx_ei = *ei; // copy element data into temporary buffer
3448 xx_ei.num_change_pages = -1;
3450 while (!checkEndOfFile(file))
3452 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3454 if (xx_ei.num_change_pages != -1)
3457 if (real_chunk_size >= chunk_size)
3463 if (ei->num_change_pages == -1)
3465 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3468 ei->num_change_pages = 1;
3470 setElementChangePages(ei, 1);
3471 setElementChangeInfoToDefaults(ei->change);
3473 return real_chunk_size;
3476 // initialize number of change pages stored for this custom element
3477 setElementChangePages(ei, ei->num_change_pages);
3478 for (i = 0; i < ei->num_change_pages; i++)
3479 setElementChangeInfoToDefaults(&ei->change_page[i]);
3481 // start with reading properties for the first change page
3482 xx_current_change_page = 0;
3484 while (!checkEndOfFile(file))
3486 // level file might contain invalid change page number
3487 if (xx_current_change_page >= ei->num_change_pages)
3490 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3492 xx_change = *change; // copy change data into temporary buffer
3494 resetEventBits(); // reset bits; change page might have changed
3496 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3499 *change = xx_change;
3501 setEventFlagsFromEventBits(change);
3503 if (real_chunk_size >= chunk_size)
3507 level->file_has_custom_elements = TRUE;
3509 return real_chunk_size;
3512 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3514 int element = getMappedElement(getFile16BitBE(file));
3515 int real_chunk_size = 2;
3516 struct ElementInfo *ei = &element_info[element];
3517 struct ElementGroupInfo *group = ei->group;
3522 xx_ei = *ei; // copy element data into temporary buffer
3523 xx_group = *group; // copy group data into temporary buffer
3525 while (!checkEndOfFile(file))
3527 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3530 if (real_chunk_size >= chunk_size)
3537 level->file_has_custom_elements = TRUE;
3539 return real_chunk_size;
3542 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3544 int element = getMappedElement(getFile16BitBE(file));
3545 int real_chunk_size = 2;
3546 struct ElementInfo *ei = &element_info[element];
3548 xx_ei = *ei; // copy element data into temporary buffer
3550 while (!checkEndOfFile(file))
3552 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3555 if (real_chunk_size >= chunk_size)
3561 level->file_has_custom_elements = TRUE;
3563 return real_chunk_size;
3566 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3567 struct LevelFileInfo *level_file_info,
3568 boolean level_info_only)
3570 char *filename = level_file_info->filename;
3571 char cookie[MAX_LINE_LEN];
3572 char chunk_name[CHUNK_ID_LEN + 1];
3576 if (!(file = openFile(filename, MODE_READ)))
3578 level->no_valid_file = TRUE;
3579 level->no_level_file = TRUE;
3581 if (level_info_only)
3584 Warn("cannot read level '%s' -- using empty level", filename);
3586 if (!setup.editor.use_template_for_new_levels)
3589 // if level file not found, try to initialize level data from template
3590 filename = getGlobalLevelTemplateFilename();
3592 if (!(file = openFile(filename, MODE_READ)))
3595 // default: for empty levels, use level template for custom elements
3596 level->use_custom_template = TRUE;
3598 level->no_valid_file = FALSE;
3601 getFileChunkBE(file, chunk_name, NULL);
3602 if (strEqual(chunk_name, "RND1"))
3604 getFile32BitBE(file); // not used
3606 getFileChunkBE(file, chunk_name, NULL);
3607 if (!strEqual(chunk_name, "CAVE"))
3609 level->no_valid_file = TRUE;
3611 Warn("unknown format of level file '%s'", filename);
3618 else // check for pre-2.0 file format with cookie string
3620 strcpy(cookie, chunk_name);
3621 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3623 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3624 cookie[strlen(cookie) - 1] = '\0';
3626 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3628 level->no_valid_file = TRUE;
3630 Warn("unknown format of level file '%s'", filename);
3637 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3639 level->no_valid_file = TRUE;
3641 Warn("unsupported version of level file '%s'", filename);
3648 // pre-2.0 level files have no game version, so use file version here
3649 level->game_version = level->file_version;
3652 if (level->file_version < FILE_VERSION_1_2)
3654 // level files from versions before 1.2.0 without chunk structure
3655 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3656 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3664 int (*loader)(File *, int, struct LevelInfo *);
3668 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3669 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3670 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3671 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3672 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3673 { "INFO", -1, LoadLevel_INFO },
3674 { "BODY", -1, LoadLevel_BODY },
3675 { "CONT", -1, LoadLevel_CONT },
3676 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3677 { "CNT3", -1, LoadLevel_CNT3 },
3678 { "CUS1", -1, LoadLevel_CUS1 },
3679 { "CUS2", -1, LoadLevel_CUS2 },
3680 { "CUS3", -1, LoadLevel_CUS3 },
3681 { "CUS4", -1, LoadLevel_CUS4 },
3682 { "GRP1", -1, LoadLevel_GRP1 },
3683 { "CONF", -1, LoadLevel_CONF },
3684 { "ELEM", -1, LoadLevel_ELEM },
3685 { "NOTE", -1, LoadLevel_NOTE },
3686 { "CUSX", -1, LoadLevel_CUSX },
3687 { "GRPX", -1, LoadLevel_GRPX },
3688 { "EMPX", -1, LoadLevel_EMPX },
3693 while (getFileChunkBE(file, chunk_name, &chunk_size))
3697 while (chunk_info[i].name != NULL &&
3698 !strEqual(chunk_name, chunk_info[i].name))
3701 if (chunk_info[i].name == NULL)
3703 Warn("unknown chunk '%s' in level file '%s'",
3704 chunk_name, filename);
3706 ReadUnusedBytesFromFile(file, chunk_size);
3708 else if (chunk_info[i].size != -1 &&
3709 chunk_info[i].size != chunk_size)
3711 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3712 chunk_size, chunk_name, filename);
3714 ReadUnusedBytesFromFile(file, chunk_size);
3718 // call function to load this level chunk
3719 int chunk_size_expected =
3720 (chunk_info[i].loader)(file, chunk_size, level);
3722 if (chunk_size_expected < 0)
3724 Warn("error reading chunk '%s' in level file '%s'",
3725 chunk_name, filename);
3730 // the size of some chunks cannot be checked before reading other
3731 // chunks first (like "HEAD" and "BODY") that contain some header
3732 // information, so check them here
3733 if (chunk_size_expected != chunk_size)
3735 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3736 chunk_size, chunk_name, filename);
3748 // ----------------------------------------------------------------------------
3749 // functions for loading BD level
3750 // ----------------------------------------------------------------------------
3752 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3754 struct LevelInfo_BD *level_bd = level->native_bd_level;
3755 GdCave *cave = NULL; // will be changed below
3756 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3757 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3760 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3762 // cave and map newly allocated when set to defaults above
3763 cave = level_bd->cave;
3766 cave->intermission = level->bd_intermission;
3769 cave->level_time[0] = level->time;
3770 cave->level_diamonds[0] = level->gems_needed;
3773 cave->scheduling = level->bd_scheduling_type;
3774 cave->pal_timing = level->bd_pal_timing;
3775 cave->level_speed[0] = level->bd_cycle_delay_ms;
3776 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3777 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3778 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3781 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3782 cave->diamond_value = level->score[SC_EMERALD];
3783 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3785 // compatibility settings
3786 cave->lineshift = level->bd_line_shifting_borders;
3787 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3788 cave->short_explosions = level->bd_short_explosions;
3789 cave->gravity_affects_all = level->bd_gravity_affects_all;
3791 // player properties
3792 cave->diagonal_movements = level->bd_diagonal_movements;
3793 cave->active_is_first_found = level->bd_topmost_player_active;
3794 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3795 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3796 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3798 // element properties
3799 cave->level_magic_wall_time[0] = level->time_magic_wall;
3802 strncpy(cave->name, level->name, sizeof(GdString));
3803 cave->name[sizeof(GdString) - 1] = '\0';
3805 // playfield elements
3806 for (x = 0; x < cave->w; x++)
3807 for (y = 0; y < cave->h; y++)
3808 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3811 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3813 struct LevelInfo_BD *level_bd = level->native_bd_level;
3814 GdCave *cave = level_bd->cave;
3815 int bd_level_nr = level_bd->level_nr;
3818 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3819 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3822 level->bd_intermission = cave->intermission;
3825 level->time = cave->level_time[bd_level_nr];
3826 level->gems_needed = cave->level_diamonds[bd_level_nr];
3829 level->bd_scheduling_type = cave->scheduling;
3830 level->bd_pal_timing = cave->pal_timing;
3831 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3832 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3833 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3834 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3837 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3838 level->score[SC_EMERALD] = cave->diamond_value;
3839 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3841 // compatibility settings
3842 level->bd_line_shifting_borders = cave->lineshift;
3843 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
3844 level->bd_short_explosions = cave->short_explosions;
3845 level->bd_gravity_affects_all = cave->gravity_affects_all;
3847 // player properties
3848 level->bd_diagonal_movements = cave->diagonal_movements;
3849 level->bd_topmost_player_active = cave->active_is_first_found;
3850 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
3851 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
3852 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
3854 // element properties
3855 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3858 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3860 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3861 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3863 // playfield elements
3864 for (x = 0; x < level->fieldx; x++)
3865 for (y = 0; y < level->fieldy; y++)
3866 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3868 checked_free(cave_name);
3871 static void setTapeInfoToDefaults(void);
3873 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3875 struct LevelInfo_BD *level_bd = level->native_bd_level;
3876 GdCave *cave = level_bd->cave;
3877 GdReplay *replay = level_bd->replay;
3883 // always start with reliable default values
3884 setTapeInfoToDefaults();
3886 tape.level_nr = level_nr; // (currently not used)
3887 tape.random_seed = replay->seed;
3889 TapeSetDateFromIsoDateString(replay->date);
3892 tape.pos[tape.counter].delay = 0;
3894 tape.bd_replay = TRUE;
3896 // all time calculations only used to display approximate tape time
3897 int cave_speed = cave->speed;
3898 int milliseconds_game = 0;
3899 int milliseconds_elapsed = 20;
3901 for (i = 0; i < replay->movements->len; i++)
3903 int replay_action = replay->movements->data[i];
3904 int tape_action = map_action_BD_to_RND(replay_action);
3905 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3906 boolean success = 0;
3910 success = TapeAddAction(action);
3912 milliseconds_game += milliseconds_elapsed;
3914 if (milliseconds_game >= cave_speed)
3916 milliseconds_game -= cave_speed;
3923 tape.pos[tape.counter].delay = 0;
3924 tape.pos[tape.counter].action[0] = 0;
3928 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3934 TapeHaltRecording();
3938 // ----------------------------------------------------------------------------
3939 // functions for loading EM level
3940 // ----------------------------------------------------------------------------
3942 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3944 static int ball_xy[8][2] =
3955 struct LevelInfo_EM *level_em = level->native_em_level;
3956 struct CAVE *cav = level_em->cav;
3959 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3960 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3962 cav->time_seconds = level->time;
3963 cav->gems_needed = level->gems_needed;
3965 cav->emerald_score = level->score[SC_EMERALD];
3966 cav->diamond_score = level->score[SC_DIAMOND];
3967 cav->alien_score = level->score[SC_ROBOT];
3968 cav->tank_score = level->score[SC_SPACESHIP];
3969 cav->bug_score = level->score[SC_BUG];
3970 cav->eater_score = level->score[SC_YAMYAM];
3971 cav->nut_score = level->score[SC_NUT];
3972 cav->dynamite_score = level->score[SC_DYNAMITE];
3973 cav->key_score = level->score[SC_KEY];
3974 cav->exit_score = level->score[SC_TIME_BONUS];
3976 cav->num_eater_arrays = level->num_yamyam_contents;
3978 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3979 for (y = 0; y < 3; y++)
3980 for (x = 0; x < 3; x++)
3981 cav->eater_array[i][y * 3 + x] =
3982 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3984 cav->amoeba_time = level->amoeba_speed;
3985 cav->wonderwall_time = level->time_magic_wall;
3986 cav->wheel_time = level->time_wheel;
3988 cav->android_move_time = level->android_move_time;
3989 cav->android_clone_time = level->android_clone_time;
3990 cav->ball_random = level->ball_random;
3991 cav->ball_active = level->ball_active_initial;
3992 cav->ball_time = level->ball_time;
3993 cav->num_ball_arrays = level->num_ball_contents;
3995 cav->lenses_score = level->lenses_score;
3996 cav->magnify_score = level->magnify_score;
3997 cav->slurp_score = level->slurp_score;
3999 cav->lenses_time = level->lenses_time;
4000 cav->magnify_time = level->magnify_time;
4002 cav->wind_time = 9999;
4003 cav->wind_direction =
4004 map_direction_RND_to_EM(level->wind_direction_initial);
4006 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4007 for (j = 0; j < 8; j++)
4008 cav->ball_array[i][j] =
4009 map_element_RND_to_EM_cave(level->ball_content[i].
4010 e[ball_xy[j][0]][ball_xy[j][1]]);
4012 map_android_clone_elements_RND_to_EM(level);
4014 // first fill the complete playfield with the empty space element
4015 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4016 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4017 cav->cave[x][y] = Cblank;
4019 // then copy the real level contents from level file into the playfield
4020 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4022 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4024 if (level->field[x][y] == EL_AMOEBA_DEAD)
4025 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4027 cav->cave[x][y] = new_element;
4030 for (i = 0; i < MAX_PLAYERS; i++)
4032 cav->player_x[i] = -1;
4033 cav->player_y[i] = -1;
4036 // initialize player positions and delete players from the playfield
4037 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4039 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4041 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4043 cav->player_x[player_nr] = x;
4044 cav->player_y[player_nr] = y;
4046 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4051 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4053 static int ball_xy[8][2] =
4064 struct LevelInfo_EM *level_em = level->native_em_level;
4065 struct CAVE *cav = level_em->cav;
4068 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4069 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4071 level->time = cav->time_seconds;
4072 level->gems_needed = cav->gems_needed;
4074 sprintf(level->name, "Level %d", level->file_info.nr);
4076 level->score[SC_EMERALD] = cav->emerald_score;
4077 level->score[SC_DIAMOND] = cav->diamond_score;
4078 level->score[SC_ROBOT] = cav->alien_score;
4079 level->score[SC_SPACESHIP] = cav->tank_score;
4080 level->score[SC_BUG] = cav->bug_score;
4081 level->score[SC_YAMYAM] = cav->eater_score;
4082 level->score[SC_NUT] = cav->nut_score;
4083 level->score[SC_DYNAMITE] = cav->dynamite_score;
4084 level->score[SC_KEY] = cav->key_score;
4085 level->score[SC_TIME_BONUS] = cav->exit_score;
4087 level->num_yamyam_contents = cav->num_eater_arrays;
4089 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4090 for (y = 0; y < 3; y++)
4091 for (x = 0; x < 3; x++)
4092 level->yamyam_content[i].e[x][y] =
4093 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4095 level->amoeba_speed = cav->amoeba_time;
4096 level->time_magic_wall = cav->wonderwall_time;
4097 level->time_wheel = cav->wheel_time;
4099 level->android_move_time = cav->android_move_time;
4100 level->android_clone_time = cav->android_clone_time;
4101 level->ball_random = cav->ball_random;
4102 level->ball_active_initial = cav->ball_active;
4103 level->ball_time = cav->ball_time;
4104 level->num_ball_contents = cav->num_ball_arrays;
4106 level->lenses_score = cav->lenses_score;
4107 level->magnify_score = cav->magnify_score;
4108 level->slurp_score = cav->slurp_score;
4110 level->lenses_time = cav->lenses_time;
4111 level->magnify_time = cav->magnify_time;
4113 level->wind_direction_initial =
4114 map_direction_EM_to_RND(cav->wind_direction);
4116 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4117 for (j = 0; j < 8; j++)
4118 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4119 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4121 map_android_clone_elements_EM_to_RND(level);
4123 // convert the playfield (some elements need special treatment)
4124 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4126 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4128 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4129 new_element = EL_AMOEBA_DEAD;
4131 level->field[x][y] = new_element;
4134 for (i = 0; i < MAX_PLAYERS; i++)
4136 // in case of all players set to the same field, use the first player
4137 int nr = MAX_PLAYERS - i - 1;
4138 int jx = cav->player_x[nr];
4139 int jy = cav->player_y[nr];
4141 if (jx != -1 && jy != -1)
4142 level->field[jx][jy] = EL_PLAYER_1 + nr;
4145 // time score is counted for each 10 seconds left in Emerald Mine levels
4146 level->time_score_base = 10;
4150 // ----------------------------------------------------------------------------
4151 // functions for loading SP level
4152 // ----------------------------------------------------------------------------
4154 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4156 struct LevelInfo_SP *level_sp = level->native_sp_level;
4157 LevelInfoType *header = &level_sp->header;
4160 level_sp->width = level->fieldx;
4161 level_sp->height = level->fieldy;
4163 for (x = 0; x < level->fieldx; x++)
4164 for (y = 0; y < level->fieldy; y++)
4165 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4167 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4169 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4170 header->LevelTitle[i] = level->name[i];
4171 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4173 header->InfotronsNeeded = level->gems_needed;
4175 header->SpecialPortCount = 0;
4177 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4179 boolean gravity_port_found = FALSE;
4180 boolean gravity_port_valid = FALSE;
4181 int gravity_port_flag;
4182 int gravity_port_base_element;
4183 int element = level->field[x][y];
4185 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4186 element <= EL_SP_GRAVITY_ON_PORT_UP)
4188 gravity_port_found = TRUE;
4189 gravity_port_valid = TRUE;
4190 gravity_port_flag = 1;
4191 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4193 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4194 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4196 gravity_port_found = TRUE;
4197 gravity_port_valid = TRUE;
4198 gravity_port_flag = 0;
4199 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4201 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4202 element <= EL_SP_GRAVITY_PORT_UP)
4204 // change R'n'D style gravity inverting special port to normal port
4205 // (there are no gravity inverting ports in native Supaplex engine)
4207 gravity_port_found = TRUE;
4208 gravity_port_valid = FALSE;
4209 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4212 if (gravity_port_found)
4214 if (gravity_port_valid &&
4215 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4217 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4219 port->PortLocation = (y * level->fieldx + x) * 2;
4220 port->Gravity = gravity_port_flag;
4222 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4224 header->SpecialPortCount++;
4228 // change special gravity port to normal port
4230 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4233 level_sp->playfield[x][y] = element - EL_SP_START;
4238 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4240 struct LevelInfo_SP *level_sp = level->native_sp_level;
4241 LevelInfoType *header = &level_sp->header;
4242 boolean num_invalid_elements = 0;
4245 level->fieldx = level_sp->width;
4246 level->fieldy = level_sp->height;
4248 for (x = 0; x < level->fieldx; x++)
4250 for (y = 0; y < level->fieldy; y++)
4252 int element_old = level_sp->playfield[x][y];
4253 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4255 if (element_new == EL_UNKNOWN)
4257 num_invalid_elements++;
4259 Debug("level:native:SP", "invalid element %d at position %d, %d",
4263 level->field[x][y] = element_new;
4267 if (num_invalid_elements > 0)
4268 Warn("found %d invalid elements%s", num_invalid_elements,
4269 (!options.debug ? " (use '--debug' for more details)" : ""));
4271 for (i = 0; i < MAX_PLAYERS; i++)
4272 level->initial_player_gravity[i] =
4273 (header->InitialGravity == 1 ? TRUE : FALSE);
4275 // skip leading spaces
4276 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4277 if (header->LevelTitle[i] != ' ')
4281 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4282 level->name[j] = header->LevelTitle[i];
4283 level->name[j] = '\0';
4285 // cut trailing spaces
4287 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4288 level->name[j - 1] = '\0';
4290 level->gems_needed = header->InfotronsNeeded;
4292 for (i = 0; i < header->SpecialPortCount; i++)
4294 SpecialPortType *port = &header->SpecialPort[i];
4295 int port_location = port->PortLocation;
4296 int gravity = port->Gravity;
4297 int port_x, port_y, port_element;
4299 port_x = (port_location / 2) % level->fieldx;
4300 port_y = (port_location / 2) / level->fieldx;
4302 if (port_x < 0 || port_x >= level->fieldx ||
4303 port_y < 0 || port_y >= level->fieldy)
4305 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4310 port_element = level->field[port_x][port_y];
4312 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4313 port_element > EL_SP_GRAVITY_PORT_UP)
4315 Warn("no special port at position (%d, %d)", port_x, port_y);
4320 // change previous (wrong) gravity inverting special port to either
4321 // gravity enabling special port or gravity disabling special port
4322 level->field[port_x][port_y] +=
4323 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4324 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4327 // change special gravity ports without database entries to normal ports
4328 for (x = 0; x < level->fieldx; x++)
4329 for (y = 0; y < level->fieldy; y++)
4330 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4331 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4332 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4334 level->time = 0; // no time limit
4335 level->amoeba_speed = 0;
4336 level->time_magic_wall = 0;
4337 level->time_wheel = 0;
4338 level->amoeba_content = EL_EMPTY;
4340 // original Supaplex does not use score values -- rate by playing time
4341 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4342 level->score[i] = 0;
4344 level->rate_time_over_score = TRUE;
4346 // there are no yamyams in supaplex levels
4347 for (i = 0; i < level->num_yamyam_contents; i++)
4348 for (x = 0; x < 3; x++)
4349 for (y = 0; y < 3; y++)
4350 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4353 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4355 struct LevelInfo_SP *level_sp = level->native_sp_level;
4356 struct DemoInfo_SP *demo = &level_sp->demo;
4359 // always start with reliable default values
4360 demo->is_available = FALSE;
4363 if (TAPE_IS_EMPTY(tape))
4366 demo->level_nr = tape.level_nr; // (currently not used)
4368 level_sp->header.DemoRandomSeed = tape.random_seed;
4372 for (i = 0; i < tape.length; i++)
4374 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4375 int demo_repeat = tape.pos[i].delay;
4376 int demo_entries = (demo_repeat + 15) / 16;
4378 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4380 Warn("tape truncated: size exceeds maximum SP demo size %d",
4386 for (j = 0; j < demo_repeat / 16; j++)
4387 demo->data[demo->length++] = 0xf0 | demo_action;
4389 if (demo_repeat % 16)
4390 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4393 demo->is_available = TRUE;
4396 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4398 struct LevelInfo_SP *level_sp = level->native_sp_level;
4399 struct DemoInfo_SP *demo = &level_sp->demo;
4400 char *filename = level->file_info.filename;
4403 // always start with reliable default values
4404 setTapeInfoToDefaults();
4406 if (!demo->is_available)
4409 tape.level_nr = demo->level_nr; // (currently not used)
4410 tape.random_seed = level_sp->header.DemoRandomSeed;
4412 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4415 tape.pos[tape.counter].delay = 0;
4417 for (i = 0; i < demo->length; i++)
4419 int demo_action = demo->data[i] & 0x0f;
4420 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4421 int tape_action = map_key_SP_to_RND(demo_action);
4422 int tape_repeat = demo_repeat + 1;
4423 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4424 boolean success = 0;
4427 for (j = 0; j < tape_repeat; j++)
4428 success = TapeAddAction(action);
4432 Warn("SP demo truncated: size exceeds maximum tape size %d",
4439 TapeHaltRecording();
4443 // ----------------------------------------------------------------------------
4444 // functions for loading MM level
4445 // ----------------------------------------------------------------------------
4447 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4449 struct LevelInfo_MM *level_mm = level->native_mm_level;
4452 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4453 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4455 level_mm->time = level->time;
4456 level_mm->kettles_needed = level->gems_needed;
4457 level_mm->auto_count_kettles = level->auto_count_gems;
4459 level_mm->mm_laser_red = level->mm_laser_red;
4460 level_mm->mm_laser_green = level->mm_laser_green;
4461 level_mm->mm_laser_blue = level->mm_laser_blue;
4463 level_mm->df_laser_red = level->df_laser_red;
4464 level_mm->df_laser_green = level->df_laser_green;
4465 level_mm->df_laser_blue = level->df_laser_blue;
4467 strcpy(level_mm->name, level->name);
4468 strcpy(level_mm->author, level->author);
4470 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4471 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4472 level_mm->score[SC_KEY] = level->score[SC_KEY];
4473 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4474 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4476 level_mm->amoeba_speed = level->amoeba_speed;
4477 level_mm->time_fuse = level->mm_time_fuse;
4478 level_mm->time_bomb = level->mm_time_bomb;
4479 level_mm->time_ball = level->mm_time_ball;
4480 level_mm->time_block = level->mm_time_block;
4482 level_mm->num_ball_contents = level->num_mm_ball_contents;
4483 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4484 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4485 level_mm->explode_ball = level->explode_mm_ball;
4487 for (i = 0; i < level->num_mm_ball_contents; i++)
4488 level_mm->ball_content[i] =
4489 map_element_RND_to_MM(level->mm_ball_content[i]);
4491 for (x = 0; x < level->fieldx; x++)
4492 for (y = 0; y < level->fieldy; y++)
4494 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4497 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4499 struct LevelInfo_MM *level_mm = level->native_mm_level;
4502 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4503 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4505 level->time = level_mm->time;
4506 level->gems_needed = level_mm->kettles_needed;
4507 level->auto_count_gems = level_mm->auto_count_kettles;
4509 level->mm_laser_red = level_mm->mm_laser_red;
4510 level->mm_laser_green = level_mm->mm_laser_green;
4511 level->mm_laser_blue = level_mm->mm_laser_blue;
4513 level->df_laser_red = level_mm->df_laser_red;
4514 level->df_laser_green = level_mm->df_laser_green;
4515 level->df_laser_blue = level_mm->df_laser_blue;
4517 strcpy(level->name, level_mm->name);
4519 // only overwrite author from 'levelinfo.conf' if author defined in level
4520 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4521 strcpy(level->author, level_mm->author);
4523 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4524 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4525 level->score[SC_KEY] = level_mm->score[SC_KEY];
4526 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4527 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4529 level->amoeba_speed = level_mm->amoeba_speed;
4530 level->mm_time_fuse = level_mm->time_fuse;
4531 level->mm_time_bomb = level_mm->time_bomb;
4532 level->mm_time_ball = level_mm->time_ball;
4533 level->mm_time_block = level_mm->time_block;
4535 level->num_mm_ball_contents = level_mm->num_ball_contents;
4536 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4537 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4538 level->explode_mm_ball = level_mm->explode_ball;
4540 for (i = 0; i < level->num_mm_ball_contents; i++)
4541 level->mm_ball_content[i] =
4542 map_element_MM_to_RND(level_mm->ball_content[i]);
4544 for (x = 0; x < level->fieldx; x++)
4545 for (y = 0; y < level->fieldy; y++)
4546 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4550 // ----------------------------------------------------------------------------
4551 // functions for loading DC level
4552 // ----------------------------------------------------------------------------
4554 #define DC_LEVEL_HEADER_SIZE 344
4556 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4559 static int last_data_encoded;
4563 int diff_hi, diff_lo;
4564 int data_hi, data_lo;
4565 unsigned short data_decoded;
4569 last_data_encoded = 0;
4576 diff = data_encoded - last_data_encoded;
4577 diff_hi = diff & ~0xff;
4578 diff_lo = diff & 0xff;
4582 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4583 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4584 data_hi = data_hi & 0xff00;
4586 data_decoded = data_hi | data_lo;
4588 last_data_encoded = data_encoded;
4590 offset1 = (offset1 + 1) % 31;
4591 offset2 = offset2 & 0xff;
4593 return data_decoded;
4596 static int getMappedElement_DC(int element)
4604 // 0x0117 - 0x036e: (?)
4607 // 0x042d - 0x0684: (?)
4623 element = EL_CRYSTAL;
4626 case 0x0e77: // quicksand (boulder)
4627 element = EL_QUICKSAND_FAST_FULL;
4630 case 0x0e99: // slow quicksand (boulder)
4631 element = EL_QUICKSAND_FULL;
4635 element = EL_EM_EXIT_OPEN;
4639 element = EL_EM_EXIT_CLOSED;
4643 element = EL_EM_STEEL_EXIT_OPEN;
4647 element = EL_EM_STEEL_EXIT_CLOSED;
4650 case 0x0f4f: // dynamite (lit 1)
4651 element = EL_EM_DYNAMITE_ACTIVE;
4654 case 0x0f57: // dynamite (lit 2)
4655 element = EL_EM_DYNAMITE_ACTIVE;
4658 case 0x0f5f: // dynamite (lit 3)
4659 element = EL_EM_DYNAMITE_ACTIVE;
4662 case 0x0f67: // dynamite (lit 4)
4663 element = EL_EM_DYNAMITE_ACTIVE;
4670 element = EL_AMOEBA_WET;
4674 element = EL_AMOEBA_DROP;
4678 element = EL_DC_MAGIC_WALL;
4682 element = EL_SPACESHIP_UP;
4686 element = EL_SPACESHIP_DOWN;
4690 element = EL_SPACESHIP_LEFT;
4694 element = EL_SPACESHIP_RIGHT;
4698 element = EL_BUG_UP;
4702 element = EL_BUG_DOWN;
4706 element = EL_BUG_LEFT;
4710 element = EL_BUG_RIGHT;
4714 element = EL_MOLE_UP;
4718 element = EL_MOLE_DOWN;
4722 element = EL_MOLE_LEFT;
4726 element = EL_MOLE_RIGHT;
4734 element = EL_YAMYAM_UP;
4738 element = EL_SWITCHGATE_OPEN;
4742 element = EL_SWITCHGATE_CLOSED;
4746 element = EL_DC_SWITCHGATE_SWITCH_UP;
4750 element = EL_TIMEGATE_CLOSED;
4753 case 0x144c: // conveyor belt switch (green)
4754 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4757 case 0x144f: // conveyor belt switch (red)
4758 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4761 case 0x1452: // conveyor belt switch (blue)
4762 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4766 element = EL_CONVEYOR_BELT_3_MIDDLE;
4770 element = EL_CONVEYOR_BELT_3_LEFT;
4774 element = EL_CONVEYOR_BELT_3_RIGHT;
4778 element = EL_CONVEYOR_BELT_1_MIDDLE;
4782 element = EL_CONVEYOR_BELT_1_LEFT;
4786 element = EL_CONVEYOR_BELT_1_RIGHT;
4790 element = EL_CONVEYOR_BELT_4_MIDDLE;
4794 element = EL_CONVEYOR_BELT_4_LEFT;
4798 element = EL_CONVEYOR_BELT_4_RIGHT;
4802 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4806 element = EL_EXPANDABLE_WALL_VERTICAL;
4810 element = EL_EXPANDABLE_WALL_ANY;
4813 case 0x14ce: // growing steel wall (left/right)
4814 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4817 case 0x14df: // growing steel wall (up/down)
4818 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4821 case 0x14e8: // growing steel wall (up/down/left/right)
4822 element = EL_EXPANDABLE_STEELWALL_ANY;
4826 element = EL_SHIELD_DEADLY;
4830 element = EL_EXTRA_TIME;
4838 element = EL_EMPTY_SPACE;
4841 case 0x1578: // quicksand (empty)
4842 element = EL_QUICKSAND_FAST_EMPTY;
4845 case 0x1579: // slow quicksand (empty)
4846 element = EL_QUICKSAND_EMPTY;
4856 element = EL_EM_DYNAMITE;
4859 case 0x15a1: // key (red)
4860 element = EL_EM_KEY_1;
4863 case 0x15a2: // key (yellow)
4864 element = EL_EM_KEY_2;
4867 case 0x15a3: // key (blue)
4868 element = EL_EM_KEY_4;
4871 case 0x15a4: // key (green)
4872 element = EL_EM_KEY_3;
4875 case 0x15a5: // key (white)
4876 element = EL_DC_KEY_WHITE;
4880 element = EL_WALL_SLIPPERY;
4887 case 0x15a8: // wall (not round)
4891 case 0x15a9: // (blue)
4892 element = EL_CHAR_A;
4895 case 0x15aa: // (blue)
4896 element = EL_CHAR_B;
4899 case 0x15ab: // (blue)
4900 element = EL_CHAR_C;
4903 case 0x15ac: // (blue)
4904 element = EL_CHAR_D;
4907 case 0x15ad: // (blue)
4908 element = EL_CHAR_E;
4911 case 0x15ae: // (blue)
4912 element = EL_CHAR_F;
4915 case 0x15af: // (blue)
4916 element = EL_CHAR_G;
4919 case 0x15b0: // (blue)
4920 element = EL_CHAR_H;
4923 case 0x15b1: // (blue)
4924 element = EL_CHAR_I;
4927 case 0x15b2: // (blue)
4928 element = EL_CHAR_J;
4931 case 0x15b3: // (blue)
4932 element = EL_CHAR_K;
4935 case 0x15b4: // (blue)
4936 element = EL_CHAR_L;
4939 case 0x15b5: // (blue)
4940 element = EL_CHAR_M;
4943 case 0x15b6: // (blue)
4944 element = EL_CHAR_N;
4947 case 0x15b7: // (blue)
4948 element = EL_CHAR_O;
4951 case 0x15b8: // (blue)
4952 element = EL_CHAR_P;
4955 case 0x15b9: // (blue)
4956 element = EL_CHAR_Q;
4959 case 0x15ba: // (blue)
4960 element = EL_CHAR_R;
4963 case 0x15bb: // (blue)
4964 element = EL_CHAR_S;
4967 case 0x15bc: // (blue)
4968 element = EL_CHAR_T;
4971 case 0x15bd: // (blue)
4972 element = EL_CHAR_U;
4975 case 0x15be: // (blue)
4976 element = EL_CHAR_V;
4979 case 0x15bf: // (blue)
4980 element = EL_CHAR_W;
4983 case 0x15c0: // (blue)
4984 element = EL_CHAR_X;
4987 case 0x15c1: // (blue)
4988 element = EL_CHAR_Y;
4991 case 0x15c2: // (blue)
4992 element = EL_CHAR_Z;
4995 case 0x15c3: // (blue)
4996 element = EL_CHAR_AUMLAUT;
4999 case 0x15c4: // (blue)
5000 element = EL_CHAR_OUMLAUT;
5003 case 0x15c5: // (blue)
5004 element = EL_CHAR_UUMLAUT;
5007 case 0x15c6: // (blue)
5008 element = EL_CHAR_0;
5011 case 0x15c7: // (blue)
5012 element = EL_CHAR_1;
5015 case 0x15c8: // (blue)
5016 element = EL_CHAR_2;
5019 case 0x15c9: // (blue)
5020 element = EL_CHAR_3;
5023 case 0x15ca: // (blue)
5024 element = EL_CHAR_4;
5027 case 0x15cb: // (blue)
5028 element = EL_CHAR_5;
5031 case 0x15cc: // (blue)
5032 element = EL_CHAR_6;
5035 case 0x15cd: // (blue)
5036 element = EL_CHAR_7;
5039 case 0x15ce: // (blue)
5040 element = EL_CHAR_8;
5043 case 0x15cf: // (blue)
5044 element = EL_CHAR_9;
5047 case 0x15d0: // (blue)
5048 element = EL_CHAR_PERIOD;
5051 case 0x15d1: // (blue)
5052 element = EL_CHAR_EXCLAM;
5055 case 0x15d2: // (blue)
5056 element = EL_CHAR_COLON;
5059 case 0x15d3: // (blue)
5060 element = EL_CHAR_LESS;
5063 case 0x15d4: // (blue)
5064 element = EL_CHAR_GREATER;
5067 case 0x15d5: // (blue)
5068 element = EL_CHAR_QUESTION;
5071 case 0x15d6: // (blue)
5072 element = EL_CHAR_COPYRIGHT;
5075 case 0x15d7: // (blue)
5076 element = EL_CHAR_UP;
5079 case 0x15d8: // (blue)
5080 element = EL_CHAR_DOWN;
5083 case 0x15d9: // (blue)
5084 element = EL_CHAR_BUTTON;
5087 case 0x15da: // (blue)
5088 element = EL_CHAR_PLUS;
5091 case 0x15db: // (blue)
5092 element = EL_CHAR_MINUS;
5095 case 0x15dc: // (blue)
5096 element = EL_CHAR_APOSTROPHE;
5099 case 0x15dd: // (blue)
5100 element = EL_CHAR_PARENLEFT;
5103 case 0x15de: // (blue)
5104 element = EL_CHAR_PARENRIGHT;
5107 case 0x15df: // (green)
5108 element = EL_CHAR_A;
5111 case 0x15e0: // (green)
5112 element = EL_CHAR_B;
5115 case 0x15e1: // (green)
5116 element = EL_CHAR_C;
5119 case 0x15e2: // (green)
5120 element = EL_CHAR_D;
5123 case 0x15e3: // (green)
5124 element = EL_CHAR_E;
5127 case 0x15e4: // (green)
5128 element = EL_CHAR_F;
5131 case 0x15e5: // (green)
5132 element = EL_CHAR_G;
5135 case 0x15e6: // (green)
5136 element = EL_CHAR_H;
5139 case 0x15e7: // (green)
5140 element = EL_CHAR_I;
5143 case 0x15e8: // (green)
5144 element = EL_CHAR_J;
5147 case 0x15e9: // (green)
5148 element = EL_CHAR_K;
5151 case 0x15ea: // (green)
5152 element = EL_CHAR_L;
5155 case 0x15eb: // (green)
5156 element = EL_CHAR_M;
5159 case 0x15ec: // (green)
5160 element = EL_CHAR_N;
5163 case 0x15ed: // (green)
5164 element = EL_CHAR_O;
5167 case 0x15ee: // (green)
5168 element = EL_CHAR_P;
5171 case 0x15ef: // (green)
5172 element = EL_CHAR_Q;
5175 case 0x15f0: // (green)
5176 element = EL_CHAR_R;
5179 case 0x15f1: // (green)
5180 element = EL_CHAR_S;
5183 case 0x15f2: // (green)
5184 element = EL_CHAR_T;
5187 case 0x15f3: // (green)
5188 element = EL_CHAR_U;
5191 case 0x15f4: // (green)
5192 element = EL_CHAR_V;
5195 case 0x15f5: // (green)
5196 element = EL_CHAR_W;
5199 case 0x15f6: // (green)
5200 element = EL_CHAR_X;
5203 case 0x15f7: // (green)
5204 element = EL_CHAR_Y;
5207 case 0x15f8: // (green)
5208 element = EL_CHAR_Z;
5211 case 0x15f9: // (green)
5212 element = EL_CHAR_AUMLAUT;
5215 case 0x15fa: // (green)
5216 element = EL_CHAR_OUMLAUT;
5219 case 0x15fb: // (green)
5220 element = EL_CHAR_UUMLAUT;
5223 case 0x15fc: // (green)
5224 element = EL_CHAR_0;
5227 case 0x15fd: // (green)
5228 element = EL_CHAR_1;
5231 case 0x15fe: // (green)
5232 element = EL_CHAR_2;
5235 case 0x15ff: // (green)
5236 element = EL_CHAR_3;
5239 case 0x1600: // (green)
5240 element = EL_CHAR_4;
5243 case 0x1601: // (green)
5244 element = EL_CHAR_5;
5247 case 0x1602: // (green)
5248 element = EL_CHAR_6;
5251 case 0x1603: // (green)
5252 element = EL_CHAR_7;
5255 case 0x1604: // (green)
5256 element = EL_CHAR_8;
5259 case 0x1605: // (green)
5260 element = EL_CHAR_9;
5263 case 0x1606: // (green)
5264 element = EL_CHAR_PERIOD;
5267 case 0x1607: // (green)
5268 element = EL_CHAR_EXCLAM;
5271 case 0x1608: // (green)
5272 element = EL_CHAR_COLON;
5275 case 0x1609: // (green)
5276 element = EL_CHAR_LESS;
5279 case 0x160a: // (green)
5280 element = EL_CHAR_GREATER;
5283 case 0x160b: // (green)
5284 element = EL_CHAR_QUESTION;
5287 case 0x160c: // (green)
5288 element = EL_CHAR_COPYRIGHT;
5291 case 0x160d: // (green)
5292 element = EL_CHAR_UP;
5295 case 0x160e: // (green)
5296 element = EL_CHAR_DOWN;
5299 case 0x160f: // (green)
5300 element = EL_CHAR_BUTTON;
5303 case 0x1610: // (green)
5304 element = EL_CHAR_PLUS;
5307 case 0x1611: // (green)
5308 element = EL_CHAR_MINUS;
5311 case 0x1612: // (green)
5312 element = EL_CHAR_APOSTROPHE;
5315 case 0x1613: // (green)
5316 element = EL_CHAR_PARENLEFT;
5319 case 0x1614: // (green)
5320 element = EL_CHAR_PARENRIGHT;
5323 case 0x1615: // (blue steel)
5324 element = EL_STEEL_CHAR_A;
5327 case 0x1616: // (blue steel)
5328 element = EL_STEEL_CHAR_B;
5331 case 0x1617: // (blue steel)
5332 element = EL_STEEL_CHAR_C;
5335 case 0x1618: // (blue steel)
5336 element = EL_STEEL_CHAR_D;
5339 case 0x1619: // (blue steel)
5340 element = EL_STEEL_CHAR_E;
5343 case 0x161a: // (blue steel)
5344 element = EL_STEEL_CHAR_F;
5347 case 0x161b: // (blue steel)
5348 element = EL_STEEL_CHAR_G;
5351 case 0x161c: // (blue steel)
5352 element = EL_STEEL_CHAR_H;
5355 case 0x161d: // (blue steel)
5356 element = EL_STEEL_CHAR_I;
5359 case 0x161e: // (blue steel)
5360 element = EL_STEEL_CHAR_J;
5363 case 0x161f: // (blue steel)
5364 element = EL_STEEL_CHAR_K;
5367 case 0x1620: // (blue steel)
5368 element = EL_STEEL_CHAR_L;
5371 case 0x1621: // (blue steel)
5372 element = EL_STEEL_CHAR_M;
5375 case 0x1622: // (blue steel)
5376 element = EL_STEEL_CHAR_N;
5379 case 0x1623: // (blue steel)
5380 element = EL_STEEL_CHAR_O;
5383 case 0x1624: // (blue steel)
5384 element = EL_STEEL_CHAR_P;
5387 case 0x1625: // (blue steel)
5388 element = EL_STEEL_CHAR_Q;
5391 case 0x1626: // (blue steel)
5392 element = EL_STEEL_CHAR_R;
5395 case 0x1627: // (blue steel)
5396 element = EL_STEEL_CHAR_S;
5399 case 0x1628: // (blue steel)
5400 element = EL_STEEL_CHAR_T;
5403 case 0x1629: // (blue steel)
5404 element = EL_STEEL_CHAR_U;
5407 case 0x162a: // (blue steel)
5408 element = EL_STEEL_CHAR_V;
5411 case 0x162b: // (blue steel)
5412 element = EL_STEEL_CHAR_W;
5415 case 0x162c: // (blue steel)
5416 element = EL_STEEL_CHAR_X;
5419 case 0x162d: // (blue steel)
5420 element = EL_STEEL_CHAR_Y;
5423 case 0x162e: // (blue steel)
5424 element = EL_STEEL_CHAR_Z;
5427 case 0x162f: // (blue steel)
5428 element = EL_STEEL_CHAR_AUMLAUT;
5431 case 0x1630: // (blue steel)
5432 element = EL_STEEL_CHAR_OUMLAUT;
5435 case 0x1631: // (blue steel)
5436 element = EL_STEEL_CHAR_UUMLAUT;
5439 case 0x1632: // (blue steel)
5440 element = EL_STEEL_CHAR_0;
5443 case 0x1633: // (blue steel)
5444 element = EL_STEEL_CHAR_1;
5447 case 0x1634: // (blue steel)
5448 element = EL_STEEL_CHAR_2;
5451 case 0x1635: // (blue steel)
5452 element = EL_STEEL_CHAR_3;
5455 case 0x1636: // (blue steel)
5456 element = EL_STEEL_CHAR_4;
5459 case 0x1637: // (blue steel)
5460 element = EL_STEEL_CHAR_5;
5463 case 0x1638: // (blue steel)
5464 element = EL_STEEL_CHAR_6;
5467 case 0x1639: // (blue steel)
5468 element = EL_STEEL_CHAR_7;
5471 case 0x163a: // (blue steel)
5472 element = EL_STEEL_CHAR_8;
5475 case 0x163b: // (blue steel)
5476 element = EL_STEEL_CHAR_9;
5479 case 0x163c: // (blue steel)
5480 element = EL_STEEL_CHAR_PERIOD;
5483 case 0x163d: // (blue steel)
5484 element = EL_STEEL_CHAR_EXCLAM;
5487 case 0x163e: // (blue steel)
5488 element = EL_STEEL_CHAR_COLON;
5491 case 0x163f: // (blue steel)
5492 element = EL_STEEL_CHAR_LESS;
5495 case 0x1640: // (blue steel)
5496 element = EL_STEEL_CHAR_GREATER;
5499 case 0x1641: // (blue steel)
5500 element = EL_STEEL_CHAR_QUESTION;
5503 case 0x1642: // (blue steel)
5504 element = EL_STEEL_CHAR_COPYRIGHT;
5507 case 0x1643: // (blue steel)
5508 element = EL_STEEL_CHAR_UP;
5511 case 0x1644: // (blue steel)
5512 element = EL_STEEL_CHAR_DOWN;
5515 case 0x1645: // (blue steel)
5516 element = EL_STEEL_CHAR_BUTTON;
5519 case 0x1646: // (blue steel)
5520 element = EL_STEEL_CHAR_PLUS;
5523 case 0x1647: // (blue steel)
5524 element = EL_STEEL_CHAR_MINUS;
5527 case 0x1648: // (blue steel)
5528 element = EL_STEEL_CHAR_APOSTROPHE;
5531 case 0x1649: // (blue steel)
5532 element = EL_STEEL_CHAR_PARENLEFT;
5535 case 0x164a: // (blue steel)
5536 element = EL_STEEL_CHAR_PARENRIGHT;
5539 case 0x164b: // (green steel)
5540 element = EL_STEEL_CHAR_A;
5543 case 0x164c: // (green steel)
5544 element = EL_STEEL_CHAR_B;
5547 case 0x164d: // (green steel)
5548 element = EL_STEEL_CHAR_C;
5551 case 0x164e: // (green steel)
5552 element = EL_STEEL_CHAR_D;
5555 case 0x164f: // (green steel)
5556 element = EL_STEEL_CHAR_E;
5559 case 0x1650: // (green steel)
5560 element = EL_STEEL_CHAR_F;
5563 case 0x1651: // (green steel)
5564 element = EL_STEEL_CHAR_G;
5567 case 0x1652: // (green steel)
5568 element = EL_STEEL_CHAR_H;
5571 case 0x1653: // (green steel)
5572 element = EL_STEEL_CHAR_I;
5575 case 0x1654: // (green steel)
5576 element = EL_STEEL_CHAR_J;
5579 case 0x1655: // (green steel)
5580 element = EL_STEEL_CHAR_K;
5583 case 0x1656: // (green steel)
5584 element = EL_STEEL_CHAR_L;
5587 case 0x1657: // (green steel)
5588 element = EL_STEEL_CHAR_M;
5591 case 0x1658: // (green steel)
5592 element = EL_STEEL_CHAR_N;
5595 case 0x1659: // (green steel)
5596 element = EL_STEEL_CHAR_O;
5599 case 0x165a: // (green steel)
5600 element = EL_STEEL_CHAR_P;
5603 case 0x165b: // (green steel)
5604 element = EL_STEEL_CHAR_Q;
5607 case 0x165c: // (green steel)
5608 element = EL_STEEL_CHAR_R;
5611 case 0x165d: // (green steel)
5612 element = EL_STEEL_CHAR_S;
5615 case 0x165e: // (green steel)
5616 element = EL_STEEL_CHAR_T;
5619 case 0x165f: // (green steel)
5620 element = EL_STEEL_CHAR_U;
5623 case 0x1660: // (green steel)
5624 element = EL_STEEL_CHAR_V;
5627 case 0x1661: // (green steel)
5628 element = EL_STEEL_CHAR_W;
5631 case 0x1662: // (green steel)
5632 element = EL_STEEL_CHAR_X;
5635 case 0x1663: // (green steel)
5636 element = EL_STEEL_CHAR_Y;
5639 case 0x1664: // (green steel)
5640 element = EL_STEEL_CHAR_Z;
5643 case 0x1665: // (green steel)
5644 element = EL_STEEL_CHAR_AUMLAUT;
5647 case 0x1666: // (green steel)
5648 element = EL_STEEL_CHAR_OUMLAUT;
5651 case 0x1667: // (green steel)
5652 element = EL_STEEL_CHAR_UUMLAUT;
5655 case 0x1668: // (green steel)
5656 element = EL_STEEL_CHAR_0;
5659 case 0x1669: // (green steel)
5660 element = EL_STEEL_CHAR_1;
5663 case 0x166a: // (green steel)
5664 element = EL_STEEL_CHAR_2;
5667 case 0x166b: // (green steel)
5668 element = EL_STEEL_CHAR_3;
5671 case 0x166c: // (green steel)
5672 element = EL_STEEL_CHAR_4;
5675 case 0x166d: // (green steel)
5676 element = EL_STEEL_CHAR_5;
5679 case 0x166e: // (green steel)
5680 element = EL_STEEL_CHAR_6;
5683 case 0x166f: // (green steel)
5684 element = EL_STEEL_CHAR_7;
5687 case 0x1670: // (green steel)
5688 element = EL_STEEL_CHAR_8;
5691 case 0x1671: // (green steel)
5692 element = EL_STEEL_CHAR_9;
5695 case 0x1672: // (green steel)
5696 element = EL_STEEL_CHAR_PERIOD;
5699 case 0x1673: // (green steel)
5700 element = EL_STEEL_CHAR_EXCLAM;
5703 case 0x1674: // (green steel)
5704 element = EL_STEEL_CHAR_COLON;
5707 case 0x1675: // (green steel)
5708 element = EL_STEEL_CHAR_LESS;
5711 case 0x1676: // (green steel)
5712 element = EL_STEEL_CHAR_GREATER;
5715 case 0x1677: // (green steel)
5716 element = EL_STEEL_CHAR_QUESTION;
5719 case 0x1678: // (green steel)
5720 element = EL_STEEL_CHAR_COPYRIGHT;
5723 case 0x1679: // (green steel)
5724 element = EL_STEEL_CHAR_UP;
5727 case 0x167a: // (green steel)
5728 element = EL_STEEL_CHAR_DOWN;
5731 case 0x167b: // (green steel)
5732 element = EL_STEEL_CHAR_BUTTON;
5735 case 0x167c: // (green steel)
5736 element = EL_STEEL_CHAR_PLUS;
5739 case 0x167d: // (green steel)
5740 element = EL_STEEL_CHAR_MINUS;
5743 case 0x167e: // (green steel)
5744 element = EL_STEEL_CHAR_APOSTROPHE;
5747 case 0x167f: // (green steel)
5748 element = EL_STEEL_CHAR_PARENLEFT;
5751 case 0x1680: // (green steel)
5752 element = EL_STEEL_CHAR_PARENRIGHT;
5755 case 0x1681: // gate (red)
5756 element = EL_EM_GATE_1;
5759 case 0x1682: // secret gate (red)
5760 element = EL_EM_GATE_1_GRAY;
5763 case 0x1683: // gate (yellow)
5764 element = EL_EM_GATE_2;
5767 case 0x1684: // secret gate (yellow)
5768 element = EL_EM_GATE_2_GRAY;
5771 case 0x1685: // gate (blue)
5772 element = EL_EM_GATE_4;
5775 case 0x1686: // secret gate (blue)
5776 element = EL_EM_GATE_4_GRAY;
5779 case 0x1687: // gate (green)
5780 element = EL_EM_GATE_3;
5783 case 0x1688: // secret gate (green)
5784 element = EL_EM_GATE_3_GRAY;
5787 case 0x1689: // gate (white)
5788 element = EL_DC_GATE_WHITE;
5791 case 0x168a: // secret gate (white)
5792 element = EL_DC_GATE_WHITE_GRAY;
5795 case 0x168b: // secret gate (no key)
5796 element = EL_DC_GATE_FAKE_GRAY;
5800 element = EL_ROBOT_WHEEL;
5804 element = EL_DC_TIMEGATE_SWITCH;
5808 element = EL_ACID_POOL_BOTTOM;
5812 element = EL_ACID_POOL_TOPLEFT;
5816 element = EL_ACID_POOL_TOPRIGHT;
5820 element = EL_ACID_POOL_BOTTOMLEFT;
5824 element = EL_ACID_POOL_BOTTOMRIGHT;
5828 element = EL_STEELWALL;
5832 element = EL_STEELWALL_SLIPPERY;
5835 case 0x1695: // steel wall (not round)
5836 element = EL_STEELWALL;
5839 case 0x1696: // steel wall (left)
5840 element = EL_DC_STEELWALL_1_LEFT;
5843 case 0x1697: // steel wall (bottom)
5844 element = EL_DC_STEELWALL_1_BOTTOM;
5847 case 0x1698: // steel wall (right)
5848 element = EL_DC_STEELWALL_1_RIGHT;
5851 case 0x1699: // steel wall (top)
5852 element = EL_DC_STEELWALL_1_TOP;
5855 case 0x169a: // steel wall (left/bottom)
5856 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5859 case 0x169b: // steel wall (right/bottom)
5860 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5863 case 0x169c: // steel wall (right/top)
5864 element = EL_DC_STEELWALL_1_TOPRIGHT;
5867 case 0x169d: // steel wall (left/top)
5868 element = EL_DC_STEELWALL_1_TOPLEFT;
5871 case 0x169e: // steel wall (right/bottom small)
5872 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5875 case 0x169f: // steel wall (left/bottom small)
5876 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5879 case 0x16a0: // steel wall (right/top small)
5880 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5883 case 0x16a1: // steel wall (left/top small)
5884 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5887 case 0x16a2: // steel wall (left/right)
5888 element = EL_DC_STEELWALL_1_VERTICAL;
5891 case 0x16a3: // steel wall (top/bottom)
5892 element = EL_DC_STEELWALL_1_HORIZONTAL;
5895 case 0x16a4: // steel wall 2 (left end)
5896 element = EL_DC_STEELWALL_2_LEFT;
5899 case 0x16a5: // steel wall 2 (right end)
5900 element = EL_DC_STEELWALL_2_RIGHT;
5903 case 0x16a6: // steel wall 2 (top end)
5904 element = EL_DC_STEELWALL_2_TOP;
5907 case 0x16a7: // steel wall 2 (bottom end)
5908 element = EL_DC_STEELWALL_2_BOTTOM;
5911 case 0x16a8: // steel wall 2 (left/right)
5912 element = EL_DC_STEELWALL_2_HORIZONTAL;
5915 case 0x16a9: // steel wall 2 (up/down)
5916 element = EL_DC_STEELWALL_2_VERTICAL;
5919 case 0x16aa: // steel wall 2 (mid)
5920 element = EL_DC_STEELWALL_2_MIDDLE;
5924 element = EL_SIGN_EXCLAMATION;
5928 element = EL_SIGN_RADIOACTIVITY;
5932 element = EL_SIGN_STOP;
5936 element = EL_SIGN_WHEELCHAIR;
5940 element = EL_SIGN_PARKING;
5944 element = EL_SIGN_NO_ENTRY;
5948 element = EL_SIGN_HEART;
5952 element = EL_SIGN_GIVE_WAY;
5956 element = EL_SIGN_ENTRY_FORBIDDEN;
5960 element = EL_SIGN_EMERGENCY_EXIT;
5964 element = EL_SIGN_YIN_YANG;
5968 element = EL_WALL_EMERALD;
5972 element = EL_WALL_DIAMOND;
5976 element = EL_WALL_PEARL;
5980 element = EL_WALL_CRYSTAL;
5984 element = EL_INVISIBLE_WALL;
5988 element = EL_INVISIBLE_STEELWALL;
5992 // EL_INVISIBLE_SAND
5995 element = EL_LIGHT_SWITCH;
5999 element = EL_ENVELOPE_1;
6003 if (element >= 0x0117 && element <= 0x036e) // (?)
6004 element = EL_DIAMOND;
6005 else if (element >= 0x042d && element <= 0x0684) // (?)
6006 element = EL_EMERALD;
6007 else if (element >= 0x157c && element <= 0x158b)
6009 else if (element >= 0x1590 && element <= 0x159f)
6010 element = EL_DC_LANDMINE;
6011 else if (element >= 0x16bc && element <= 0x16cb)
6012 element = EL_INVISIBLE_SAND;
6015 Warn("unknown Diamond Caves element 0x%04x", element);
6017 element = EL_UNKNOWN;
6022 return getMappedElement(element);
6025 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6027 byte header[DC_LEVEL_HEADER_SIZE];
6029 int envelope_header_pos = 62;
6030 int envelope_content_pos = 94;
6031 int level_name_pos = 251;
6032 int level_author_pos = 292;
6033 int envelope_header_len;
6034 int envelope_content_len;
6036 int level_author_len;
6038 int num_yamyam_contents;
6041 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6043 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6045 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6047 header[i * 2 + 0] = header_word >> 8;
6048 header[i * 2 + 1] = header_word & 0xff;
6051 // read some values from level header to check level decoding integrity
6052 fieldx = header[6] | (header[7] << 8);
6053 fieldy = header[8] | (header[9] << 8);
6054 num_yamyam_contents = header[60] | (header[61] << 8);
6056 // do some simple sanity checks to ensure that level was correctly decoded
6057 if (fieldx < 1 || fieldx > 256 ||
6058 fieldy < 1 || fieldy > 256 ||
6059 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6061 level->no_valid_file = TRUE;
6063 Warn("cannot decode level from stream -- using empty level");
6068 // maximum envelope header size is 31 bytes
6069 envelope_header_len = header[envelope_header_pos];
6070 // maximum envelope content size is 110 (156?) bytes
6071 envelope_content_len = header[envelope_content_pos];
6073 // maximum level title size is 40 bytes
6074 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6075 // maximum level author size is 30 (51?) bytes
6076 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6080 for (i = 0; i < envelope_header_len; i++)
6081 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6082 level->envelope[0].text[envelope_size++] =
6083 header[envelope_header_pos + 1 + i];
6085 if (envelope_header_len > 0 && envelope_content_len > 0)
6087 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6088 level->envelope[0].text[envelope_size++] = '\n';
6089 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6090 level->envelope[0].text[envelope_size++] = '\n';
6093 for (i = 0; i < envelope_content_len; i++)
6094 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6095 level->envelope[0].text[envelope_size++] =
6096 header[envelope_content_pos + 1 + i];
6098 level->envelope[0].text[envelope_size] = '\0';
6100 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6101 level->envelope[0].ysize = 10;
6102 level->envelope[0].autowrap = TRUE;
6103 level->envelope[0].centered = TRUE;
6105 for (i = 0; i < level_name_len; i++)
6106 level->name[i] = header[level_name_pos + 1 + i];
6107 level->name[level_name_len] = '\0';
6109 for (i = 0; i < level_author_len; i++)
6110 level->author[i] = header[level_author_pos + 1 + i];
6111 level->author[level_author_len] = '\0';
6113 num_yamyam_contents = header[60] | (header[61] << 8);
6114 level->num_yamyam_contents =
6115 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6117 for (i = 0; i < num_yamyam_contents; i++)
6119 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6121 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6122 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6124 if (i < MAX_ELEMENT_CONTENTS)
6125 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6129 fieldx = header[6] | (header[7] << 8);
6130 fieldy = header[8] | (header[9] << 8);
6131 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6132 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6134 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6136 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6137 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6139 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6140 level->field[x][y] = getMappedElement_DC(element_dc);
6143 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6144 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6145 level->field[x][y] = EL_PLAYER_1;
6147 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6148 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6149 level->field[x][y] = EL_PLAYER_2;
6151 level->gems_needed = header[18] | (header[19] << 8);
6153 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6154 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6155 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6156 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6157 level->score[SC_NUT] = header[28] | (header[29] << 8);
6158 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6159 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6160 level->score[SC_BUG] = header[34] | (header[35] << 8);
6161 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6162 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6163 level->score[SC_KEY] = header[40] | (header[41] << 8);
6164 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6166 level->time = header[44] | (header[45] << 8);
6168 level->amoeba_speed = header[46] | (header[47] << 8);
6169 level->time_light = header[48] | (header[49] << 8);
6170 level->time_timegate = header[50] | (header[51] << 8);
6171 level->time_wheel = header[52] | (header[53] << 8);
6172 level->time_magic_wall = header[54] | (header[55] << 8);
6173 level->extra_time = header[56] | (header[57] << 8);
6174 level->shield_normal_time = header[58] | (header[59] << 8);
6176 // shield and extra time elements do not have a score
6177 level->score[SC_SHIELD] = 0;
6178 level->extra_time_score = 0;
6180 // set time for normal and deadly shields to the same value
6181 level->shield_deadly_time = level->shield_normal_time;
6183 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6184 // can slip down from flat walls, like normal walls and steel walls
6185 level->em_slippery_gems = TRUE;
6187 // time score is counted for each 10 seconds left in Diamond Caves levels
6188 level->time_score_base = 10;
6191 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6192 struct LevelFileInfo *level_file_info,
6193 boolean level_info_only)
6195 char *filename = level_file_info->filename;
6197 int num_magic_bytes = 8;
6198 char magic_bytes[num_magic_bytes + 1];
6199 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6201 if (!(file = openFile(filename, MODE_READ)))
6203 level->no_valid_file = TRUE;
6205 if (!level_info_only)
6206 Warn("cannot read level '%s' -- using empty level", filename);
6211 // fseek(file, 0x0000, SEEK_SET);
6213 if (level_file_info->packed)
6215 // read "magic bytes" from start of file
6216 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6217 magic_bytes[0] = '\0';
6219 // check "magic bytes" for correct file format
6220 if (!strPrefix(magic_bytes, "DC2"))
6222 level->no_valid_file = TRUE;
6224 Warn("unknown DC level file '%s' -- using empty level", filename);
6229 if (strPrefix(magic_bytes, "DC2Win95") ||
6230 strPrefix(magic_bytes, "DC2Win98"))
6232 int position_first_level = 0x00fa;
6233 int extra_bytes = 4;
6236 // advance file stream to first level inside the level package
6237 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6239 // each block of level data is followed by block of non-level data
6240 num_levels_to_skip *= 2;
6242 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6243 while (num_levels_to_skip >= 0)
6245 // advance file stream to next level inside the level package
6246 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6248 level->no_valid_file = TRUE;
6250 Warn("cannot fseek in file '%s' -- using empty level", filename);
6255 // skip apparently unused extra bytes following each level
6256 ReadUnusedBytesFromFile(file, extra_bytes);
6258 // read size of next level in level package
6259 skip_bytes = getFile32BitLE(file);
6261 num_levels_to_skip--;
6266 level->no_valid_file = TRUE;
6268 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6274 LoadLevelFromFileStream_DC(file, level);
6280 // ----------------------------------------------------------------------------
6281 // functions for loading SB level
6282 // ----------------------------------------------------------------------------
6284 int getMappedElement_SB(int element_ascii, boolean use_ces)
6292 sb_element_mapping[] =
6294 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6295 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6296 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6297 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6298 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6299 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6300 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6301 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6308 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6309 if (element_ascii == sb_element_mapping[i].ascii)
6310 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6312 return EL_UNDEFINED;
6315 static void SetLevelSettings_SB(struct LevelInfo *level)
6319 level->use_step_counter = TRUE;
6322 level->score[SC_TIME_BONUS] = 0;
6323 level->time_score_base = 1;
6324 level->rate_time_over_score = TRUE;
6327 level->auto_exit_sokoban = TRUE;
6330 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6331 struct LevelFileInfo *level_file_info,
6332 boolean level_info_only)
6334 char *filename = level_file_info->filename;
6335 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6336 char last_comment[MAX_LINE_LEN];
6337 char level_name[MAX_LINE_LEN];
6340 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6341 boolean read_continued_line = FALSE;
6342 boolean reading_playfield = FALSE;
6343 boolean got_valid_playfield_line = FALSE;
6344 boolean invalid_playfield_char = FALSE;
6345 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6346 int file_level_nr = 0;
6347 int x = 0, y = 0; // initialized to make compilers happy
6349 last_comment[0] = '\0';
6350 level_name[0] = '\0';
6352 if (!(file = openFile(filename, MODE_READ)))
6354 level->no_valid_file = TRUE;
6356 if (!level_info_only)
6357 Warn("cannot read level '%s' -- using empty level", filename);
6362 while (!checkEndOfFile(file))
6364 // level successfully read, but next level may follow here
6365 if (!got_valid_playfield_line && reading_playfield)
6367 // read playfield from single level file -- skip remaining file
6368 if (!level_file_info->packed)
6371 if (file_level_nr >= num_levels_to_skip)
6376 last_comment[0] = '\0';
6377 level_name[0] = '\0';
6379 reading_playfield = FALSE;
6382 got_valid_playfield_line = FALSE;
6384 // read next line of input file
6385 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6388 // cut trailing line break (this can be newline and/or carriage return)
6389 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6390 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6393 // copy raw input line for later use (mainly debugging output)
6394 strcpy(line_raw, line);
6396 if (read_continued_line)
6398 // append new line to existing line, if there is enough space
6399 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6400 strcat(previous_line, line_ptr);
6402 strcpy(line, previous_line); // copy storage buffer to line
6404 read_continued_line = FALSE;
6407 // if the last character is '\', continue at next line
6408 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6410 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6411 strcpy(previous_line, line); // copy line to storage buffer
6413 read_continued_line = TRUE;
6419 if (line[0] == '\0')
6422 // extract comment text from comment line
6425 for (line_ptr = line; *line_ptr; line_ptr++)
6426 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6429 strcpy(last_comment, line_ptr);
6434 // extract level title text from line containing level title
6435 if (line[0] == '\'')
6437 strcpy(level_name, &line[1]);
6439 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6440 level_name[strlen(level_name) - 1] = '\0';
6445 // skip lines containing only spaces (or empty lines)
6446 for (line_ptr = line; *line_ptr; line_ptr++)
6447 if (*line_ptr != ' ')
6449 if (*line_ptr == '\0')
6452 // at this point, we have found a line containing part of a playfield
6454 got_valid_playfield_line = TRUE;
6456 if (!reading_playfield)
6458 reading_playfield = TRUE;
6459 invalid_playfield_char = FALSE;
6461 for (x = 0; x < MAX_LEV_FIELDX; x++)
6462 for (y = 0; y < MAX_LEV_FIELDY; y++)
6463 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6468 // start with topmost tile row
6472 // skip playfield line if larger row than allowed
6473 if (y >= MAX_LEV_FIELDY)
6476 // start with leftmost tile column
6479 // read playfield elements from line
6480 for (line_ptr = line; *line_ptr; line_ptr++)
6482 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6484 // stop parsing playfield line if larger column than allowed
6485 if (x >= MAX_LEV_FIELDX)
6488 if (mapped_sb_element == EL_UNDEFINED)
6490 invalid_playfield_char = TRUE;
6495 level->field[x][y] = mapped_sb_element;
6497 // continue with next tile column
6500 level->fieldx = MAX(x, level->fieldx);
6503 if (invalid_playfield_char)
6505 // if first playfield line, treat invalid lines as comment lines
6507 reading_playfield = FALSE;
6512 // continue with next tile row
6520 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6521 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6523 if (!reading_playfield)
6525 level->no_valid_file = TRUE;
6527 Warn("cannot read level '%s' -- using empty level", filename);
6532 if (*level_name != '\0')
6534 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6535 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6537 else if (*last_comment != '\0')
6539 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6540 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6544 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6547 // set all empty fields beyond the border walls to invisible steel wall
6548 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6550 if ((x == 0 || x == level->fieldx - 1 ||
6551 y == 0 || y == level->fieldy - 1) &&
6552 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6553 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6554 level->field, level->fieldx, level->fieldy);
6557 // set special level settings for Sokoban levels
6558 SetLevelSettings_SB(level);
6560 if (load_xsb_to_ces)
6562 // special global settings can now be set in level template
6563 level->use_custom_template = TRUE;
6568 // -------------------------------------------------------------------------
6569 // functions for handling native levels
6570 // -------------------------------------------------------------------------
6572 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6573 struct LevelFileInfo *level_file_info,
6574 boolean level_info_only)
6578 // determine position of requested level inside level package
6579 if (level_file_info->packed)
6580 pos = level_file_info->nr - leveldir_current->first_level;
6582 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6583 level->no_valid_file = TRUE;
6586 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6587 struct LevelFileInfo *level_file_info,
6588 boolean level_info_only)
6590 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6591 level->no_valid_file = TRUE;
6594 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6595 struct LevelFileInfo *level_file_info,
6596 boolean level_info_only)
6600 // determine position of requested level inside level package
6601 if (level_file_info->packed)
6602 pos = level_file_info->nr - leveldir_current->first_level;
6604 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6605 level->no_valid_file = TRUE;
6608 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6609 struct LevelFileInfo *level_file_info,
6610 boolean level_info_only)
6612 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6613 level->no_valid_file = TRUE;
6616 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6618 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6619 CopyNativeLevel_RND_to_BD(level);
6620 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6621 CopyNativeLevel_RND_to_EM(level);
6622 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6623 CopyNativeLevel_RND_to_SP(level);
6624 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6625 CopyNativeLevel_RND_to_MM(level);
6628 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6630 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6631 CopyNativeLevel_BD_to_RND(level);
6632 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6633 CopyNativeLevel_EM_to_RND(level);
6634 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6635 CopyNativeLevel_SP_to_RND(level);
6636 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6637 CopyNativeLevel_MM_to_RND(level);
6640 void SaveNativeLevel(struct LevelInfo *level)
6642 // saving native level files only supported for some game engines
6643 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6644 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6647 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6648 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6649 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6650 char *filename = getLevelFilenameFromBasename(basename);
6652 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6655 boolean success = FALSE;
6657 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6659 CopyNativeLevel_RND_to_BD(level);
6660 // CopyNativeTape_RND_to_BD(level);
6662 success = SaveNativeLevel_BD(filename);
6664 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6666 CopyNativeLevel_RND_to_SP(level);
6667 CopyNativeTape_RND_to_SP(level);
6669 success = SaveNativeLevel_SP(filename);
6673 Request("Native level file saved!", REQ_CONFIRM);
6675 Request("Failed to save native level file!", REQ_CONFIRM);
6679 // ----------------------------------------------------------------------------
6680 // functions for loading generic level
6681 // ----------------------------------------------------------------------------
6683 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6684 struct LevelFileInfo *level_file_info,
6685 boolean level_info_only)
6687 // always start with reliable default values
6688 setLevelInfoToDefaults(level, level_info_only, TRUE);
6690 switch (level_file_info->type)
6692 case LEVEL_FILE_TYPE_RND:
6693 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6696 case LEVEL_FILE_TYPE_BD:
6697 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6698 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6701 case LEVEL_FILE_TYPE_EM:
6702 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6703 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6706 case LEVEL_FILE_TYPE_SP:
6707 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6708 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6711 case LEVEL_FILE_TYPE_MM:
6712 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6713 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6716 case LEVEL_FILE_TYPE_DC:
6717 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6720 case LEVEL_FILE_TYPE_SB:
6721 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6725 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6729 // if level file is invalid, restore level structure to default values
6730 if (level->no_valid_file)
6731 setLevelInfoToDefaults(level, level_info_only, FALSE);
6733 if (check_special_flags("use_native_bd_game_engine"))
6734 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6736 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6737 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6739 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6740 CopyNativeLevel_Native_to_RND(level);
6743 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6745 static struct LevelFileInfo level_file_info;
6747 // always start with reliable default values
6748 setFileInfoToDefaults(&level_file_info);
6750 level_file_info.nr = 0; // unknown level number
6751 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6753 setString(&level_file_info.filename, filename);
6755 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6758 static void LoadLevel_InitVersion(struct LevelInfo *level)
6762 if (leveldir_current == NULL) // only when dumping level
6765 // all engine modifications also valid for levels which use latest engine
6766 if (level->game_version < VERSION_IDENT(3,2,0,5))
6768 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6769 level->time_score_base = 10;
6772 if (leveldir_current->latest_engine)
6774 // ---------- use latest game engine --------------------------------------
6776 /* For all levels which are forced to use the latest game engine version
6777 (normally all but user contributed, private and undefined levels), set
6778 the game engine version to the actual version; this allows for actual
6779 corrections in the game engine to take effect for existing, converted
6780 levels (from "classic" or other existing games) to make the emulation
6781 of the corresponding game more accurate, while (hopefully) not breaking
6782 existing levels created from other players. */
6784 level->game_version = GAME_VERSION_ACTUAL;
6786 /* Set special EM style gems behaviour: EM style gems slip down from
6787 normal, steel and growing wall. As this is a more fundamental change,
6788 it seems better to set the default behaviour to "off" (as it is more
6789 natural) and make it configurable in the level editor (as a property
6790 of gem style elements). Already existing converted levels (neither
6791 private nor contributed levels) are changed to the new behaviour. */
6793 if (level->file_version < FILE_VERSION_2_0)
6794 level->em_slippery_gems = TRUE;
6799 // ---------- use game engine the level was created with --------------------
6801 /* For all levels which are not forced to use the latest game engine
6802 version (normally user contributed, private and undefined levels),
6803 use the version of the game engine the levels were created for.
6805 Since 2.0.1, the game engine version is now directly stored
6806 in the level file (chunk "VERS"), so there is no need anymore
6807 to set the game version from the file version (except for old,
6808 pre-2.0 levels, where the game version is still taken from the
6809 file format version used to store the level -- see above). */
6811 // player was faster than enemies in 1.0.0 and before
6812 if (level->file_version == FILE_VERSION_1_0)
6813 for (i = 0; i < MAX_PLAYERS; i++)
6814 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6816 // default behaviour for EM style gems was "slippery" only in 2.0.1
6817 if (level->game_version == VERSION_IDENT(2,0,1,0))
6818 level->em_slippery_gems = TRUE;
6820 // springs could be pushed over pits before (pre-release version) 2.2.0
6821 if (level->game_version < VERSION_IDENT(2,2,0,0))
6822 level->use_spring_bug = TRUE;
6824 if (level->game_version < VERSION_IDENT(3,2,0,5))
6826 // time orb caused limited time in endless time levels before 3.2.0-5
6827 level->use_time_orb_bug = TRUE;
6829 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6830 level->block_snap_field = FALSE;
6832 // extra time score was same value as time left score before 3.2.0-5
6833 level->extra_time_score = level->score[SC_TIME_BONUS];
6836 if (level->game_version < VERSION_IDENT(3,2,0,7))
6838 // default behaviour for snapping was "not continuous" before 3.2.0-7
6839 level->continuous_snapping = FALSE;
6842 // only few elements were able to actively move into acid before 3.1.0
6843 // trigger settings did not exist before 3.1.0; set to default "any"
6844 if (level->game_version < VERSION_IDENT(3,1,0,0))
6846 // correct "can move into acid" settings (all zero in old levels)
6848 level->can_move_into_acid_bits = 0; // nothing can move into acid
6849 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6851 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6852 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6853 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6854 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6856 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6857 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6859 // correct trigger settings (stored as zero == "none" in old levels)
6861 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6863 int element = EL_CUSTOM_START + i;
6864 struct ElementInfo *ei = &element_info[element];
6866 for (j = 0; j < ei->num_change_pages; j++)
6868 struct ElementChangeInfo *change = &ei->change_page[j];
6870 change->trigger_player = CH_PLAYER_ANY;
6871 change->trigger_page = CH_PAGE_ANY;
6876 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6878 int element = EL_CUSTOM_256;
6879 struct ElementInfo *ei = &element_info[element];
6880 struct ElementChangeInfo *change = &ei->change_page[0];
6882 /* This is needed to fix a problem that was caused by a bugfix in function
6883 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6884 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6885 not replace walkable elements, but instead just placed the player on it,
6886 without placing the Sokoban field under the player). Unfortunately, this
6887 breaks "Snake Bite" style levels when the snake is halfway through a door
6888 that just closes (the snake head is still alive and can be moved in this
6889 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6890 player (without Sokoban element) which then gets killed as designed). */
6892 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6893 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6894 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6895 change->target_element = EL_PLAYER_1;
6898 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6899 if (level->game_version < VERSION_IDENT(3,2,5,0))
6901 /* This is needed to fix a problem that was caused by a bugfix in function
6902 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6903 corrects the behaviour when a custom element changes to another custom
6904 element with a higher element number that has change actions defined.
6905 Normally, only one change per frame is allowed for custom elements.
6906 Therefore, it is checked if a custom element already changed in the
6907 current frame; if it did, subsequent changes are suppressed.
6908 Unfortunately, this is only checked for element changes, but not for
6909 change actions, which are still executed. As the function above loops
6910 through all custom elements from lower to higher, an element change
6911 resulting in a lower CE number won't be checked again, while a target
6912 element with a higher number will also be checked, and potential change
6913 actions will get executed for this CE, too (which is wrong), while
6914 further changes are ignored (which is correct). As this bugfix breaks
6915 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6916 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6917 behaviour for existing levels and tapes that make use of this bug */
6919 level->use_action_after_change_bug = TRUE;
6922 // not centering level after relocating player was default only in 3.2.3
6923 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6924 level->shifted_relocation = TRUE;
6926 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6927 if (level->game_version < VERSION_IDENT(3,2,6,0))
6928 level->em_explodes_by_fire = TRUE;
6930 // levels were solved by the first player entering an exit up to 4.1.0.0
6931 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6932 level->solved_by_one_player = TRUE;
6934 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6935 if (level->game_version < VERSION_IDENT(4,1,1,1))
6936 level->use_life_bugs = TRUE;
6938 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6939 if (level->game_version < VERSION_IDENT(4,1,1,1))
6940 level->sb_objects_needed = FALSE;
6942 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6943 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6944 level->finish_dig_collect = FALSE;
6946 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6947 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6948 level->keep_walkable_ce = TRUE;
6951 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6953 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6956 // check if this level is (not) a Sokoban level
6957 for (y = 0; y < level->fieldy; y++)
6958 for (x = 0; x < level->fieldx; x++)
6959 if (!IS_SB_ELEMENT(Tile[x][y]))
6960 is_sokoban_level = FALSE;
6962 if (is_sokoban_level)
6964 // set special level settings for Sokoban levels
6965 SetLevelSettings_SB(level);
6969 static void LoadLevel_InitSettings(struct LevelInfo *level)
6971 // adjust level settings for (non-native) Sokoban-style levels
6972 LoadLevel_InitSettings_SB(level);
6974 // rename levels with title "nameless level" or if renaming is forced
6975 if (leveldir_current->empty_level_name != NULL &&
6976 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6977 leveldir_current->force_level_name))
6978 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6979 leveldir_current->empty_level_name, level_nr);
6982 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6986 // map elements that have changed in newer versions
6987 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6988 level->game_version);
6989 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6990 for (x = 0; x < 3; x++)
6991 for (y = 0; y < 3; y++)
6992 level->yamyam_content[i].e[x][y] =
6993 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6994 level->game_version);
6998 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7002 // map custom element change events that have changed in newer versions
7003 // (these following values were accidentally changed in version 3.0.1)
7004 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7005 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7007 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7009 int element = EL_CUSTOM_START + i;
7011 // order of checking and copying events to be mapped is important
7012 // (do not change the start and end value -- they are constant)
7013 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7015 if (HAS_CHANGE_EVENT(element, j - 2))
7017 SET_CHANGE_EVENT(element, j - 2, FALSE);
7018 SET_CHANGE_EVENT(element, j, TRUE);
7022 // order of checking and copying events to be mapped is important
7023 // (do not change the start and end value -- they are constant)
7024 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7026 if (HAS_CHANGE_EVENT(element, j - 1))
7028 SET_CHANGE_EVENT(element, j - 1, FALSE);
7029 SET_CHANGE_EVENT(element, j, TRUE);
7035 // initialize "can_change" field for old levels with only one change page
7036 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7038 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7040 int element = EL_CUSTOM_START + i;
7042 if (CAN_CHANGE(element))
7043 element_info[element].change->can_change = TRUE;
7047 // correct custom element values (for old levels without these options)
7048 if (level->game_version < VERSION_IDENT(3,1,1,0))
7050 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7052 int element = EL_CUSTOM_START + i;
7053 struct ElementInfo *ei = &element_info[element];
7055 if (ei->access_direction == MV_NO_DIRECTION)
7056 ei->access_direction = MV_ALL_DIRECTIONS;
7060 // correct custom element values (fix invalid values for all versions)
7063 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7065 int element = EL_CUSTOM_START + i;
7066 struct ElementInfo *ei = &element_info[element];
7068 for (j = 0; j < ei->num_change_pages; j++)
7070 struct ElementChangeInfo *change = &ei->change_page[j];
7072 if (change->trigger_player == CH_PLAYER_NONE)
7073 change->trigger_player = CH_PLAYER_ANY;
7075 if (change->trigger_side == CH_SIDE_NONE)
7076 change->trigger_side = CH_SIDE_ANY;
7081 // initialize "can_explode" field for old levels which did not store this
7082 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7083 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7085 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7087 int element = EL_CUSTOM_START + i;
7089 if (EXPLODES_1X1_OLD(element))
7090 element_info[element].explosion_type = EXPLODES_1X1;
7092 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7093 EXPLODES_SMASHED(element) ||
7094 EXPLODES_IMPACT(element)));
7098 // correct previously hard-coded move delay values for maze runner style
7099 if (level->game_version < VERSION_IDENT(3,1,1,0))
7101 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7103 int element = EL_CUSTOM_START + i;
7105 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7107 // previously hard-coded and therefore ignored
7108 element_info[element].move_delay_fixed = 9;
7109 element_info[element].move_delay_random = 0;
7114 // set some other uninitialized values of custom elements in older levels
7115 if (level->game_version < VERSION_IDENT(3,1,0,0))
7117 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7119 int element = EL_CUSTOM_START + i;
7121 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7123 element_info[element].explosion_delay = 17;
7124 element_info[element].ignition_delay = 8;
7128 // set mouse click change events to work for left/middle/right mouse button
7129 if (level->game_version < VERSION_IDENT(4,2,3,0))
7131 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7133 int element = EL_CUSTOM_START + i;
7134 struct ElementInfo *ei = &element_info[element];
7136 for (j = 0; j < ei->num_change_pages; j++)
7138 struct ElementChangeInfo *change = &ei->change_page[j];
7140 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7141 change->has_event[CE_PRESSED_BY_MOUSE] ||
7142 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7143 change->has_event[CE_MOUSE_PRESSED_ON_X])
7144 change->trigger_side = CH_SIDE_ANY;
7150 static void LoadLevel_InitElements(struct LevelInfo *level)
7152 LoadLevel_InitStandardElements(level);
7154 if (level->file_has_custom_elements)
7155 LoadLevel_InitCustomElements(level);
7157 // initialize element properties for level editor etc.
7158 InitElementPropertiesEngine(level->game_version);
7159 InitElementPropertiesGfxElement();
7162 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7166 // map elements that have changed in newer versions
7167 for (y = 0; y < level->fieldy; y++)
7168 for (x = 0; x < level->fieldx; x++)
7169 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7170 level->game_version);
7172 // clear unused playfield data (nicer if level gets resized in editor)
7173 for (x = 0; x < MAX_LEV_FIELDX; x++)
7174 for (y = 0; y < MAX_LEV_FIELDY; y++)
7175 if (x >= level->fieldx || y >= level->fieldy)
7176 level->field[x][y] = EL_EMPTY;
7178 // copy elements to runtime playfield array
7179 for (x = 0; x < MAX_LEV_FIELDX; x++)
7180 for (y = 0; y < MAX_LEV_FIELDY; y++)
7181 Tile[x][y] = level->field[x][y];
7183 // initialize level size variables for faster access
7184 lev_fieldx = level->fieldx;
7185 lev_fieldy = level->fieldy;
7187 // determine border element for this level
7188 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7189 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7194 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7196 struct LevelFileInfo *level_file_info = &level->file_info;
7198 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7199 CopyNativeLevel_RND_to_Native(level);
7202 static void LoadLevelTemplate_LoadAndInit(void)
7204 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7206 LoadLevel_InitVersion(&level_template);
7207 LoadLevel_InitElements(&level_template);
7208 LoadLevel_InitSettings(&level_template);
7210 ActivateLevelTemplate();
7213 void LoadLevelTemplate(int nr)
7215 if (!fileExists(getGlobalLevelTemplateFilename()))
7217 Warn("no level template found for this level");
7222 setLevelFileInfo(&level_template.file_info, nr);
7224 LoadLevelTemplate_LoadAndInit();
7227 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7229 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7231 LoadLevelTemplate_LoadAndInit();
7234 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7236 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7238 if (level.use_custom_template)
7240 if (network_level != NULL)
7241 LoadNetworkLevelTemplate(network_level);
7243 LoadLevelTemplate(-1);
7246 LoadLevel_InitVersion(&level);
7247 LoadLevel_InitElements(&level);
7248 LoadLevel_InitPlayfield(&level);
7249 LoadLevel_InitSettings(&level);
7251 LoadLevel_InitNativeEngines(&level);
7254 void LoadLevel(int nr)
7256 SetLevelSetInfo(leveldir_current->identifier, nr);
7258 setLevelFileInfo(&level.file_info, nr);
7260 LoadLevel_LoadAndInit(NULL);
7263 void LoadLevelInfoOnly(int nr)
7265 setLevelFileInfo(&level.file_info, nr);
7267 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7270 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7272 SetLevelSetInfo(network_level->leveldir_identifier,
7273 network_level->file_info.nr);
7275 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7277 LoadLevel_LoadAndInit(network_level);
7280 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7284 chunk_size += putFileVersion(file, level->file_version);
7285 chunk_size += putFileVersion(file, level->game_version);
7290 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7294 chunk_size += putFile16BitBE(file, level->creation_date.year);
7295 chunk_size += putFile8Bit(file, level->creation_date.month);
7296 chunk_size += putFile8Bit(file, level->creation_date.day);
7301 #if ENABLE_HISTORIC_CHUNKS
7302 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7306 putFile8Bit(file, level->fieldx);
7307 putFile8Bit(file, level->fieldy);
7309 putFile16BitBE(file, level->time);
7310 putFile16BitBE(file, level->gems_needed);
7312 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7313 putFile8Bit(file, level->name[i]);
7315 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7316 putFile8Bit(file, level->score[i]);
7318 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7319 for (y = 0; y < 3; y++)
7320 for (x = 0; x < 3; x++)
7321 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7322 level->yamyam_content[i].e[x][y]));
7323 putFile8Bit(file, level->amoeba_speed);
7324 putFile8Bit(file, level->time_magic_wall);
7325 putFile8Bit(file, level->time_wheel);
7326 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7327 level->amoeba_content));
7328 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7329 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7330 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7331 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7333 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7335 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7336 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7337 putFile32BitBE(file, level->can_move_into_acid_bits);
7338 putFile8Bit(file, level->dont_collide_with_bits);
7340 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7341 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7343 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7344 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7345 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7347 putFile8Bit(file, level->game_engine_type);
7349 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7353 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7358 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7359 chunk_size += putFile8Bit(file, level->name[i]);
7364 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7369 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7370 chunk_size += putFile8Bit(file, level->author[i]);
7375 #if ENABLE_HISTORIC_CHUNKS
7376 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7381 for (y = 0; y < level->fieldy; y++)
7382 for (x = 0; x < level->fieldx; x++)
7383 if (level->encoding_16bit_field)
7384 chunk_size += putFile16BitBE(file, level->field[x][y]);
7386 chunk_size += putFile8Bit(file, level->field[x][y]);
7392 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7397 for (y = 0; y < level->fieldy; y++)
7398 for (x = 0; x < level->fieldx; x++)
7399 chunk_size += putFile16BitBE(file, level->field[x][y]);
7404 #if ENABLE_HISTORIC_CHUNKS
7405 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7409 putFile8Bit(file, EL_YAMYAM);
7410 putFile8Bit(file, level->num_yamyam_contents);
7411 putFile8Bit(file, 0);
7412 putFile8Bit(file, 0);
7414 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7415 for (y = 0; y < 3; y++)
7416 for (x = 0; x < 3; x++)
7417 if (level->encoding_16bit_field)
7418 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7420 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7424 #if ENABLE_HISTORIC_CHUNKS
7425 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7428 int num_contents, content_xsize, content_ysize;
7429 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7431 if (element == EL_YAMYAM)
7433 num_contents = level->num_yamyam_contents;
7437 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7438 for (y = 0; y < 3; y++)
7439 for (x = 0; x < 3; x++)
7440 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7442 else if (element == EL_BD_AMOEBA)
7448 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7449 for (y = 0; y < 3; y++)
7450 for (x = 0; x < 3; x++)
7451 content_array[i][x][y] = EL_EMPTY;
7452 content_array[0][0][0] = level->amoeba_content;
7456 // chunk header already written -- write empty chunk data
7457 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7459 Warn("cannot save content for element '%d'", element);
7464 putFile16BitBE(file, element);
7465 putFile8Bit(file, num_contents);
7466 putFile8Bit(file, content_xsize);
7467 putFile8Bit(file, content_ysize);
7469 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7471 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7472 for (y = 0; y < 3; y++)
7473 for (x = 0; x < 3; x++)
7474 putFile16BitBE(file, content_array[i][x][y]);
7478 #if ENABLE_HISTORIC_CHUNKS
7479 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7481 int envelope_nr = element - EL_ENVELOPE_1;
7482 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7486 chunk_size += putFile16BitBE(file, element);
7487 chunk_size += putFile16BitBE(file, envelope_len);
7488 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7489 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7491 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7492 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7494 for (i = 0; i < envelope_len; i++)
7495 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7501 #if ENABLE_HISTORIC_CHUNKS
7502 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7503 int num_changed_custom_elements)
7507 putFile16BitBE(file, num_changed_custom_elements);
7509 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7511 int element = EL_CUSTOM_START + i;
7513 struct ElementInfo *ei = &element_info[element];
7515 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7517 if (check < num_changed_custom_elements)
7519 putFile16BitBE(file, element);
7520 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7527 if (check != num_changed_custom_elements) // should not happen
7528 Warn("inconsistent number of custom element properties");
7532 #if ENABLE_HISTORIC_CHUNKS
7533 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7534 int num_changed_custom_elements)
7538 putFile16BitBE(file, num_changed_custom_elements);
7540 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7542 int element = EL_CUSTOM_START + i;
7544 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7546 if (check < num_changed_custom_elements)
7548 putFile16BitBE(file, element);
7549 putFile16BitBE(file, element_info[element].change->target_element);
7556 if (check != num_changed_custom_elements) // should not happen
7557 Warn("inconsistent number of custom target elements");
7561 #if ENABLE_HISTORIC_CHUNKS
7562 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7563 int num_changed_custom_elements)
7565 int i, j, x, y, check = 0;
7567 putFile16BitBE(file, num_changed_custom_elements);
7569 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7571 int element = EL_CUSTOM_START + i;
7572 struct ElementInfo *ei = &element_info[element];
7574 if (ei->modified_settings)
7576 if (check < num_changed_custom_elements)
7578 putFile16BitBE(file, element);
7580 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7581 putFile8Bit(file, ei->description[j]);
7583 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7585 // some free bytes for future properties and padding
7586 WriteUnusedBytesToFile(file, 7);
7588 putFile8Bit(file, ei->use_gfx_element);
7589 putFile16BitBE(file, ei->gfx_element_initial);
7591 putFile8Bit(file, ei->collect_score_initial);
7592 putFile8Bit(file, ei->collect_count_initial);
7594 putFile16BitBE(file, ei->push_delay_fixed);
7595 putFile16BitBE(file, ei->push_delay_random);
7596 putFile16BitBE(file, ei->move_delay_fixed);
7597 putFile16BitBE(file, ei->move_delay_random);
7599 putFile16BitBE(file, ei->move_pattern);
7600 putFile8Bit(file, ei->move_direction_initial);
7601 putFile8Bit(file, ei->move_stepsize);
7603 for (y = 0; y < 3; y++)
7604 for (x = 0; x < 3; x++)
7605 putFile16BitBE(file, ei->content.e[x][y]);
7607 putFile32BitBE(file, ei->change->events);
7609 putFile16BitBE(file, ei->change->target_element);
7611 putFile16BitBE(file, ei->change->delay_fixed);
7612 putFile16BitBE(file, ei->change->delay_random);
7613 putFile16BitBE(file, ei->change->delay_frames);
7615 putFile16BitBE(file, ei->change->initial_trigger_element);
7617 putFile8Bit(file, ei->change->explode);
7618 putFile8Bit(file, ei->change->use_target_content);
7619 putFile8Bit(file, ei->change->only_if_complete);
7620 putFile8Bit(file, ei->change->use_random_replace);
7622 putFile8Bit(file, ei->change->random_percentage);
7623 putFile8Bit(file, ei->change->replace_when);
7625 for (y = 0; y < 3; y++)
7626 for (x = 0; x < 3; x++)
7627 putFile16BitBE(file, ei->change->content.e[x][y]);
7629 putFile8Bit(file, ei->slippery_type);
7631 // some free bytes for future properties and padding
7632 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7639 if (check != num_changed_custom_elements) // should not happen
7640 Warn("inconsistent number of custom element properties");
7644 #if ENABLE_HISTORIC_CHUNKS
7645 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7647 struct ElementInfo *ei = &element_info[element];
7650 // ---------- custom element base property values (96 bytes) ----------------
7652 putFile16BitBE(file, element);
7654 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7655 putFile8Bit(file, ei->description[i]);
7657 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7659 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7661 putFile8Bit(file, ei->num_change_pages);
7663 putFile16BitBE(file, ei->ce_value_fixed_initial);
7664 putFile16BitBE(file, ei->ce_value_random_initial);
7665 putFile8Bit(file, ei->use_last_ce_value);
7667 putFile8Bit(file, ei->use_gfx_element);
7668 putFile16BitBE(file, ei->gfx_element_initial);
7670 putFile8Bit(file, ei->collect_score_initial);
7671 putFile8Bit(file, ei->collect_count_initial);
7673 putFile8Bit(file, ei->drop_delay_fixed);
7674 putFile8Bit(file, ei->push_delay_fixed);
7675 putFile8Bit(file, ei->drop_delay_random);
7676 putFile8Bit(file, ei->push_delay_random);
7677 putFile16BitBE(file, ei->move_delay_fixed);
7678 putFile16BitBE(file, ei->move_delay_random);
7680 // bits 0 - 15 of "move_pattern" ...
7681 putFile16BitBE(file, ei->move_pattern & 0xffff);
7682 putFile8Bit(file, ei->move_direction_initial);
7683 putFile8Bit(file, ei->move_stepsize);
7685 putFile8Bit(file, ei->slippery_type);
7687 for (y = 0; y < 3; y++)
7688 for (x = 0; x < 3; x++)
7689 putFile16BitBE(file, ei->content.e[x][y]);
7691 putFile16BitBE(file, ei->move_enter_element);
7692 putFile16BitBE(file, ei->move_leave_element);
7693 putFile8Bit(file, ei->move_leave_type);
7695 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7696 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7698 putFile8Bit(file, ei->access_direction);
7700 putFile8Bit(file, ei->explosion_delay);
7701 putFile8Bit(file, ei->ignition_delay);
7702 putFile8Bit(file, ei->explosion_type);
7704 // some free bytes for future custom property values and padding
7705 WriteUnusedBytesToFile(file, 1);
7707 // ---------- change page property values (48 bytes) ------------------------
7709 for (i = 0; i < ei->num_change_pages; i++)
7711 struct ElementChangeInfo *change = &ei->change_page[i];
7712 unsigned int event_bits;
7714 // bits 0 - 31 of "has_event[]" ...
7716 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7717 if (change->has_event[j])
7718 event_bits |= (1u << j);
7719 putFile32BitBE(file, event_bits);
7721 putFile16BitBE(file, change->target_element);
7723 putFile16BitBE(file, change->delay_fixed);
7724 putFile16BitBE(file, change->delay_random);
7725 putFile16BitBE(file, change->delay_frames);
7727 putFile16BitBE(file, change->initial_trigger_element);
7729 putFile8Bit(file, change->explode);
7730 putFile8Bit(file, change->use_target_content);
7731 putFile8Bit(file, change->only_if_complete);
7732 putFile8Bit(file, change->use_random_replace);
7734 putFile8Bit(file, change->random_percentage);
7735 putFile8Bit(file, change->replace_when);
7737 for (y = 0; y < 3; y++)
7738 for (x = 0; x < 3; x++)
7739 putFile16BitBE(file, change->target_content.e[x][y]);
7741 putFile8Bit(file, change->can_change);
7743 putFile8Bit(file, change->trigger_side);
7745 putFile8Bit(file, change->trigger_player);
7746 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7747 log_2(change->trigger_page)));
7749 putFile8Bit(file, change->has_action);
7750 putFile8Bit(file, change->action_type);
7751 putFile8Bit(file, change->action_mode);
7752 putFile16BitBE(file, change->action_arg);
7754 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7756 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7757 if (change->has_event[j])
7758 event_bits |= (1u << (j - 32));
7759 putFile8Bit(file, event_bits);
7764 #if ENABLE_HISTORIC_CHUNKS
7765 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7767 struct ElementInfo *ei = &element_info[element];
7768 struct ElementGroupInfo *group = ei->group;
7771 putFile16BitBE(file, element);
7773 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7774 putFile8Bit(file, ei->description[i]);
7776 putFile8Bit(file, group->num_elements);
7778 putFile8Bit(file, ei->use_gfx_element);
7779 putFile16BitBE(file, ei->gfx_element_initial);
7781 putFile8Bit(file, group->choice_mode);
7783 // some free bytes for future values and padding
7784 WriteUnusedBytesToFile(file, 3);
7786 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7787 putFile16BitBE(file, group->element[i]);
7791 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7792 boolean write_element)
7794 int save_type = entry->save_type;
7795 int data_type = entry->data_type;
7796 int conf_type = entry->conf_type;
7797 int byte_mask = conf_type & CONF_MASK_BYTES;
7798 int element = entry->element;
7799 int default_value = entry->default_value;
7801 boolean modified = FALSE;
7803 if (byte_mask != CONF_MASK_MULTI_BYTES)
7805 void *value_ptr = entry->value;
7806 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7809 // check if any settings have been modified before saving them
7810 if (value != default_value)
7813 // do not save if explicitly told or if unmodified default settings
7814 if ((save_type == SAVE_CONF_NEVER) ||
7815 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7819 num_bytes += putFile16BitBE(file, element);
7821 num_bytes += putFile8Bit(file, conf_type);
7822 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7823 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7824 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7827 else if (data_type == TYPE_STRING)
7829 char *default_string = entry->default_string;
7830 char *string = (char *)(entry->value);
7831 int string_length = strlen(string);
7834 // check if any settings have been modified before saving them
7835 if (!strEqual(string, default_string))
7838 // do not save if explicitly told or if unmodified default settings
7839 if ((save_type == SAVE_CONF_NEVER) ||
7840 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7844 num_bytes += putFile16BitBE(file, element);
7846 num_bytes += putFile8Bit(file, conf_type);
7847 num_bytes += putFile16BitBE(file, string_length);
7849 for (i = 0; i < string_length; i++)
7850 num_bytes += putFile8Bit(file, string[i]);
7852 else if (data_type == TYPE_ELEMENT_LIST)
7854 int *element_array = (int *)(entry->value);
7855 int num_elements = *(int *)(entry->num_entities);
7858 // check if any settings have been modified before saving them
7859 for (i = 0; i < num_elements; i++)
7860 if (element_array[i] != default_value)
7863 // do not save if explicitly told or if unmodified default settings
7864 if ((save_type == SAVE_CONF_NEVER) ||
7865 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7869 num_bytes += putFile16BitBE(file, element);
7871 num_bytes += putFile8Bit(file, conf_type);
7872 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7874 for (i = 0; i < num_elements; i++)
7875 num_bytes += putFile16BitBE(file, element_array[i]);
7877 else if (data_type == TYPE_CONTENT_LIST)
7879 struct Content *content = (struct Content *)(entry->value);
7880 int num_contents = *(int *)(entry->num_entities);
7883 // check if any settings have been modified before saving them
7884 for (i = 0; i < num_contents; i++)
7885 for (y = 0; y < 3; y++)
7886 for (x = 0; x < 3; x++)
7887 if (content[i].e[x][y] != default_value)
7890 // do not save if explicitly told or if unmodified default settings
7891 if ((save_type == SAVE_CONF_NEVER) ||
7892 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7896 num_bytes += putFile16BitBE(file, element);
7898 num_bytes += putFile8Bit(file, conf_type);
7899 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7901 for (i = 0; i < num_contents; i++)
7902 for (y = 0; y < 3; y++)
7903 for (x = 0; x < 3; x++)
7904 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7910 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7915 li = *level; // copy level data into temporary buffer
7917 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7918 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7923 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7928 li = *level; // copy level data into temporary buffer
7930 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7931 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7936 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7938 int envelope_nr = element - EL_ENVELOPE_1;
7942 chunk_size += putFile16BitBE(file, element);
7944 // copy envelope data into temporary buffer
7945 xx_envelope = level->envelope[envelope_nr];
7947 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7948 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7953 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7955 struct ElementInfo *ei = &element_info[element];
7959 chunk_size += putFile16BitBE(file, element);
7961 xx_ei = *ei; // copy element data into temporary buffer
7963 // set default description string for this specific element
7964 strcpy(xx_default_description, getDefaultElementDescription(ei));
7966 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7967 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7969 for (i = 0; i < ei->num_change_pages; i++)
7971 struct ElementChangeInfo *change = &ei->change_page[i];
7973 xx_current_change_page = i;
7975 xx_change = *change; // copy change data into temporary buffer
7978 setEventBitsFromEventFlags(change);
7980 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7981 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7988 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7990 struct ElementInfo *ei = &element_info[element];
7991 struct ElementGroupInfo *group = ei->group;
7995 chunk_size += putFile16BitBE(file, element);
7997 xx_ei = *ei; // copy element data into temporary buffer
7998 xx_group = *group; // copy group data into temporary buffer
8000 // set default description string for this specific element
8001 strcpy(xx_default_description, getDefaultElementDescription(ei));
8003 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8004 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8009 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8011 struct ElementInfo *ei = &element_info[element];
8015 chunk_size += putFile16BitBE(file, element);
8017 xx_ei = *ei; // copy element data into temporary buffer
8019 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8020 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8025 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8026 boolean save_as_template)
8032 if (!(file = fopen(filename, MODE_WRITE)))
8034 Warn("cannot save level file '%s'", filename);
8039 level->file_version = FILE_VERSION_ACTUAL;
8040 level->game_version = GAME_VERSION_ACTUAL;
8042 level->creation_date = getCurrentDate();
8044 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8045 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8047 chunk_size = SaveLevel_VERS(NULL, level);
8048 putFileChunkBE(file, "VERS", chunk_size);
8049 SaveLevel_VERS(file, level);
8051 chunk_size = SaveLevel_DATE(NULL, level);
8052 putFileChunkBE(file, "DATE", chunk_size);
8053 SaveLevel_DATE(file, level);
8055 chunk_size = SaveLevel_NAME(NULL, level);
8056 putFileChunkBE(file, "NAME", chunk_size);
8057 SaveLevel_NAME(file, level);
8059 chunk_size = SaveLevel_AUTH(NULL, level);
8060 putFileChunkBE(file, "AUTH", chunk_size);
8061 SaveLevel_AUTH(file, level);
8063 chunk_size = SaveLevel_INFO(NULL, level);
8064 putFileChunkBE(file, "INFO", chunk_size);
8065 SaveLevel_INFO(file, level);
8067 chunk_size = SaveLevel_BODY(NULL, level);
8068 putFileChunkBE(file, "BODY", chunk_size);
8069 SaveLevel_BODY(file, level);
8071 chunk_size = SaveLevel_ELEM(NULL, level);
8072 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8074 putFileChunkBE(file, "ELEM", chunk_size);
8075 SaveLevel_ELEM(file, level);
8078 for (i = 0; i < NUM_ENVELOPES; i++)
8080 int element = EL_ENVELOPE_1 + i;
8082 chunk_size = SaveLevel_NOTE(NULL, level, element);
8083 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8085 putFileChunkBE(file, "NOTE", chunk_size);
8086 SaveLevel_NOTE(file, level, element);
8090 // if not using template level, check for non-default custom/group elements
8091 if (!level->use_custom_template || save_as_template)
8093 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8095 int element = EL_CUSTOM_START + i;
8097 chunk_size = SaveLevel_CUSX(NULL, level, element);
8098 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8100 putFileChunkBE(file, "CUSX", chunk_size);
8101 SaveLevel_CUSX(file, level, element);
8105 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8107 int element = EL_GROUP_START + i;
8109 chunk_size = SaveLevel_GRPX(NULL, level, element);
8110 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8112 putFileChunkBE(file, "GRPX", chunk_size);
8113 SaveLevel_GRPX(file, level, element);
8117 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8119 int element = GET_EMPTY_ELEMENT(i);
8121 chunk_size = SaveLevel_EMPX(NULL, level, element);
8122 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8124 putFileChunkBE(file, "EMPX", chunk_size);
8125 SaveLevel_EMPX(file, level, element);
8132 SetFilePermissions(filename, PERMS_PRIVATE);
8135 void SaveLevel(int nr)
8137 char *filename = getDefaultLevelFilename(nr);
8139 SaveLevelFromFilename(&level, filename, FALSE);
8142 void SaveLevelTemplate(void)
8144 char *filename = getLocalLevelTemplateFilename();
8146 SaveLevelFromFilename(&level, filename, TRUE);
8149 boolean SaveLevelChecked(int nr)
8151 char *filename = getDefaultLevelFilename(nr);
8152 boolean new_level = !fileExists(filename);
8153 boolean level_saved = FALSE;
8155 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8160 Request("Level saved!", REQ_CONFIRM);
8168 void DumpLevel(struct LevelInfo *level)
8170 if (level->no_level_file || level->no_valid_file)
8172 Warn("cannot dump -- no valid level file found");
8178 Print("Level xxx (file version %08d, game version %08d)\n",
8179 level->file_version, level->game_version);
8182 Print("Level author: '%s'\n", level->author);
8183 Print("Level title: '%s'\n", level->name);
8185 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8187 Print("Level time: %d seconds\n", level->time);
8188 Print("Gems needed: %d\n", level->gems_needed);
8190 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8191 Print("Time for wheel: %d seconds\n", level->time_wheel);
8192 Print("Time for light: %d seconds\n", level->time_light);
8193 Print("Time for timegate: %d seconds\n", level->time_timegate);
8195 Print("Amoeba speed: %d\n", level->amoeba_speed);
8198 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8199 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8200 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8201 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8202 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8203 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8209 for (i = 0; i < NUM_ENVELOPES; i++)
8211 char *text = level->envelope[i].text;
8212 int text_len = strlen(text);
8213 boolean has_text = FALSE;
8215 for (j = 0; j < text_len; j++)
8216 if (text[j] != ' ' && text[j] != '\n')
8222 Print("Envelope %d:\n'%s'\n", i + 1, text);
8230 void DumpLevels(void)
8232 static LevelDirTree *dumplevel_leveldir = NULL;
8234 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8235 global.dumplevel_leveldir);
8237 if (dumplevel_leveldir == NULL)
8238 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8240 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8241 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8242 Fail("no such level number: %d", global.dumplevel_level_nr);
8244 leveldir_current = dumplevel_leveldir;
8246 LoadLevel(global.dumplevel_level_nr);
8253 // ============================================================================
8254 // tape file functions
8255 // ============================================================================
8257 static void setTapeInfoToDefaults(void)
8261 // always start with reliable default values (empty tape)
8264 // default values (also for pre-1.2 tapes) with only the first player
8265 tape.player_participates[0] = TRUE;
8266 for (i = 1; i < MAX_PLAYERS; i++)
8267 tape.player_participates[i] = FALSE;
8269 // at least one (default: the first) player participates in every tape
8270 tape.num_participating_players = 1;
8272 tape.property_bits = TAPE_PROPERTY_NONE;
8274 tape.level_nr = level_nr;
8276 tape.changed = FALSE;
8277 tape.solved = FALSE;
8279 tape.recording = FALSE;
8280 tape.playing = FALSE;
8281 tape.pausing = FALSE;
8283 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8284 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8286 tape.no_info_chunk = TRUE;
8287 tape.no_valid_file = FALSE;
8290 static int getTapePosSize(struct TapeInfo *tape)
8292 int tape_pos_size = 0;
8294 if (tape->use_key_actions)
8295 tape_pos_size += tape->num_participating_players;
8297 if (tape->use_mouse_actions)
8298 tape_pos_size += 3; // x and y position and mouse button mask
8300 tape_pos_size += 1; // tape action delay value
8302 return tape_pos_size;
8305 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8307 tape->use_key_actions = FALSE;
8308 tape->use_mouse_actions = FALSE;
8310 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8311 tape->use_key_actions = TRUE;
8313 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8314 tape->use_mouse_actions = TRUE;
8317 static int getTapeActionValue(struct TapeInfo *tape)
8319 return (tape->use_key_actions &&
8320 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8321 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8322 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8323 TAPE_ACTIONS_DEFAULT);
8326 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8328 tape->file_version = getFileVersion(file);
8329 tape->game_version = getFileVersion(file);
8334 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8338 tape->random_seed = getFile32BitBE(file);
8339 tape->date = getFile32BitBE(file);
8340 tape->length = getFile32BitBE(file);
8342 // read header fields that are new since version 1.2
8343 if (tape->file_version >= FILE_VERSION_1_2)
8345 byte store_participating_players = getFile8Bit(file);
8348 // since version 1.2, tapes store which players participate in the tape
8349 tape->num_participating_players = 0;
8350 for (i = 0; i < MAX_PLAYERS; i++)
8352 tape->player_participates[i] = FALSE;
8354 if (store_participating_players & (1 << i))
8356 tape->player_participates[i] = TRUE;
8357 tape->num_participating_players++;
8361 setTapeActionFlags(tape, getFile8Bit(file));
8363 tape->property_bits = getFile8Bit(file);
8364 tape->solved = getFile8Bit(file);
8366 engine_version = getFileVersion(file);
8367 if (engine_version > 0)
8368 tape->engine_version = engine_version;
8370 tape->engine_version = tape->game_version;
8376 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8378 tape->scr_fieldx = getFile8Bit(file);
8379 tape->scr_fieldy = getFile8Bit(file);
8384 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8386 char *level_identifier = NULL;
8387 int level_identifier_size;
8390 tape->no_info_chunk = FALSE;
8392 level_identifier_size = getFile16BitBE(file);
8394 level_identifier = checked_malloc(level_identifier_size);
8396 for (i = 0; i < level_identifier_size; i++)
8397 level_identifier[i] = getFile8Bit(file);
8399 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8400 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8402 checked_free(level_identifier);
8404 tape->level_nr = getFile16BitBE(file);
8406 chunk_size = 2 + level_identifier_size + 2;
8411 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8414 int tape_pos_size = getTapePosSize(tape);
8415 int chunk_size_expected = tape_pos_size * tape->length;
8417 if (chunk_size_expected != chunk_size)
8419 ReadUnusedBytesFromFile(file, chunk_size);
8420 return chunk_size_expected;
8423 for (i = 0; i < tape->length; i++)
8425 if (i >= MAX_TAPE_LEN)
8427 Warn("tape truncated -- size exceeds maximum tape size %d",
8430 // tape too large; read and ignore remaining tape data from this chunk
8431 for (;i < tape->length; i++)
8432 ReadUnusedBytesFromFile(file, tape_pos_size);
8437 if (tape->use_key_actions)
8439 for (j = 0; j < MAX_PLAYERS; j++)
8441 tape->pos[i].action[j] = MV_NONE;
8443 if (tape->player_participates[j])
8444 tape->pos[i].action[j] = getFile8Bit(file);
8448 if (tape->use_mouse_actions)
8450 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8451 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8452 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8455 tape->pos[i].delay = getFile8Bit(file);
8457 if (tape->file_version == FILE_VERSION_1_0)
8459 // eliminate possible diagonal moves in old tapes
8460 // this is only for backward compatibility
8462 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8463 byte action = tape->pos[i].action[0];
8464 int k, num_moves = 0;
8466 for (k = 0; k < 4; k++)
8468 if (action & joy_dir[k])
8470 tape->pos[i + num_moves].action[0] = joy_dir[k];
8472 tape->pos[i + num_moves].delay = 0;
8481 tape->length += num_moves;
8484 else if (tape->file_version < FILE_VERSION_2_0)
8486 // convert pre-2.0 tapes to new tape format
8488 if (tape->pos[i].delay > 1)
8491 tape->pos[i + 1] = tape->pos[i];
8492 tape->pos[i + 1].delay = 1;
8495 for (j = 0; j < MAX_PLAYERS; j++)
8496 tape->pos[i].action[j] = MV_NONE;
8497 tape->pos[i].delay--;
8504 if (checkEndOfFile(file))
8508 if (i != tape->length)
8509 chunk_size = tape_pos_size * i;
8514 static void LoadTape_SokobanSolution(char *filename)
8517 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8519 if (!(file = openFile(filename, MODE_READ)))
8521 tape.no_valid_file = TRUE;
8526 while (!checkEndOfFile(file))
8528 unsigned char c = getByteFromFile(file);
8530 if (checkEndOfFile(file))
8537 tape.pos[tape.length].action[0] = MV_UP;
8538 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8544 tape.pos[tape.length].action[0] = MV_DOWN;
8545 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8551 tape.pos[tape.length].action[0] = MV_LEFT;
8552 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8558 tape.pos[tape.length].action[0] = MV_RIGHT;
8559 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8567 // ignore white-space characters
8571 tape.no_valid_file = TRUE;
8573 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8581 if (tape.no_valid_file)
8584 tape.length_frames = GetTapeLengthFrames();
8585 tape.length_seconds = GetTapeLengthSeconds();
8588 void LoadTapeFromFilename(char *filename)
8590 char cookie[MAX_LINE_LEN];
8591 char chunk_name[CHUNK_ID_LEN + 1];
8595 // always start with reliable default values
8596 setTapeInfoToDefaults();
8598 if (strSuffix(filename, ".sln"))
8600 LoadTape_SokobanSolution(filename);
8605 if (!(file = openFile(filename, MODE_READ)))
8607 tape.no_valid_file = TRUE;
8612 getFileChunkBE(file, chunk_name, NULL);
8613 if (strEqual(chunk_name, "RND1"))
8615 getFile32BitBE(file); // not used
8617 getFileChunkBE(file, chunk_name, NULL);
8618 if (!strEqual(chunk_name, "TAPE"))
8620 tape.no_valid_file = TRUE;
8622 Warn("unknown format of tape file '%s'", filename);
8629 else // check for pre-2.0 file format with cookie string
8631 strcpy(cookie, chunk_name);
8632 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8634 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8635 cookie[strlen(cookie) - 1] = '\0';
8637 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8639 tape.no_valid_file = TRUE;
8641 Warn("unknown format of tape file '%s'", filename);
8648 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8650 tape.no_valid_file = TRUE;
8652 Warn("unsupported version of tape file '%s'", filename);
8659 // pre-2.0 tape files have no game version, so use file version here
8660 tape.game_version = tape.file_version;
8663 if (tape.file_version < FILE_VERSION_1_2)
8665 // tape files from versions before 1.2.0 without chunk structure
8666 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8667 LoadTape_BODY(file, 2 * tape.length, &tape);
8675 int (*loader)(File *, int, struct TapeInfo *);
8679 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8680 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8681 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8682 { "INFO", -1, LoadTape_INFO },
8683 { "BODY", -1, LoadTape_BODY },
8687 while (getFileChunkBE(file, chunk_name, &chunk_size))
8691 while (chunk_info[i].name != NULL &&
8692 !strEqual(chunk_name, chunk_info[i].name))
8695 if (chunk_info[i].name == NULL)
8697 Warn("unknown chunk '%s' in tape file '%s'",
8698 chunk_name, filename);
8700 ReadUnusedBytesFromFile(file, chunk_size);
8702 else if (chunk_info[i].size != -1 &&
8703 chunk_info[i].size != chunk_size)
8705 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8706 chunk_size, chunk_name, filename);
8708 ReadUnusedBytesFromFile(file, chunk_size);
8712 // call function to load this tape chunk
8713 int chunk_size_expected =
8714 (chunk_info[i].loader)(file, chunk_size, &tape);
8716 // the size of some chunks cannot be checked before reading other
8717 // chunks first (like "HEAD" and "BODY") that contain some header
8718 // information, so check them here
8719 if (chunk_size_expected != chunk_size)
8721 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8722 chunk_size, chunk_name, filename);
8730 tape.length_frames = GetTapeLengthFrames();
8731 tape.length_seconds = GetTapeLengthSeconds();
8734 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8736 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8738 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8739 tape.engine_version);
8743 void LoadTape(int nr)
8745 char *filename = getTapeFilename(nr);
8747 LoadTapeFromFilename(filename);
8750 void LoadSolutionTape(int nr)
8752 char *filename = getSolutionTapeFilename(nr);
8754 LoadTapeFromFilename(filename);
8756 if (TAPE_IS_EMPTY(tape))
8758 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8759 level.native_bd_level->replay != NULL)
8760 CopyNativeTape_BD_to_RND(&level);
8761 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8762 level.native_sp_level->demo.is_available)
8763 CopyNativeTape_SP_to_RND(&level);
8767 void LoadScoreTape(char *score_tape_basename, int nr)
8769 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8771 LoadTapeFromFilename(filename);
8774 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8776 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8778 LoadTapeFromFilename(filename);
8781 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8783 // chunk required for team mode tapes with non-default screen size
8784 return (tape->num_participating_players > 1 &&
8785 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8786 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8789 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8791 putFileVersion(file, tape->file_version);
8792 putFileVersion(file, tape->game_version);
8795 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8798 byte store_participating_players = 0;
8800 // set bits for participating players for compact storage
8801 for (i = 0; i < MAX_PLAYERS; i++)
8802 if (tape->player_participates[i])
8803 store_participating_players |= (1 << i);
8805 putFile32BitBE(file, tape->random_seed);
8806 putFile32BitBE(file, tape->date);
8807 putFile32BitBE(file, tape->length);
8809 putFile8Bit(file, store_participating_players);
8811 putFile8Bit(file, getTapeActionValue(tape));
8813 putFile8Bit(file, tape->property_bits);
8814 putFile8Bit(file, tape->solved);
8816 putFileVersion(file, tape->engine_version);
8819 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8821 putFile8Bit(file, tape->scr_fieldx);
8822 putFile8Bit(file, tape->scr_fieldy);
8825 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8827 int level_identifier_size = strlen(tape->level_identifier) + 1;
8830 putFile16BitBE(file, level_identifier_size);
8832 for (i = 0; i < level_identifier_size; i++)
8833 putFile8Bit(file, tape->level_identifier[i]);
8835 putFile16BitBE(file, tape->level_nr);
8838 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8842 for (i = 0; i < tape->length; i++)
8844 if (tape->use_key_actions)
8846 for (j = 0; j < MAX_PLAYERS; j++)
8847 if (tape->player_participates[j])
8848 putFile8Bit(file, tape->pos[i].action[j]);
8851 if (tape->use_mouse_actions)
8853 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8854 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8855 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8858 putFile8Bit(file, tape->pos[i].delay);
8862 void SaveTapeToFilename(char *filename)
8866 int info_chunk_size;
8867 int body_chunk_size;
8869 if (!(file = fopen(filename, MODE_WRITE)))
8871 Warn("cannot save level recording file '%s'", filename);
8876 tape_pos_size = getTapePosSize(&tape);
8878 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8879 body_chunk_size = tape_pos_size * tape.length;
8881 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8882 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8884 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8885 SaveTape_VERS(file, &tape);
8887 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8888 SaveTape_HEAD(file, &tape);
8890 if (checkSaveTape_SCRN(&tape))
8892 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8893 SaveTape_SCRN(file, &tape);
8896 putFileChunkBE(file, "INFO", info_chunk_size);
8897 SaveTape_INFO(file, &tape);
8899 putFileChunkBE(file, "BODY", body_chunk_size);
8900 SaveTape_BODY(file, &tape);
8904 SetFilePermissions(filename, PERMS_PRIVATE);
8907 static void SaveTapeExt(char *filename)
8911 tape.file_version = FILE_VERSION_ACTUAL;
8912 tape.game_version = GAME_VERSION_ACTUAL;
8914 tape.num_participating_players = 0;
8916 // count number of participating players
8917 for (i = 0; i < MAX_PLAYERS; i++)
8918 if (tape.player_participates[i])
8919 tape.num_participating_players++;
8921 SaveTapeToFilename(filename);
8923 tape.changed = FALSE;
8926 void SaveTape(int nr)
8928 char *filename = getTapeFilename(nr);
8930 InitTapeDirectory(leveldir_current->subdir);
8932 SaveTapeExt(filename);
8935 void SaveScoreTape(int nr)
8937 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8939 // used instead of "leveldir_current->subdir" (for network games)
8940 InitScoreTapeDirectory(levelset.identifier, nr);
8942 SaveTapeExt(filename);
8945 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8946 unsigned int req_state_added)
8948 char *filename = getTapeFilename(nr);
8949 boolean new_tape = !fileExists(filename);
8950 boolean tape_saved = FALSE;
8952 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8957 Request(msg_saved, REQ_CONFIRM | req_state_added);
8965 boolean SaveTapeChecked(int nr)
8967 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8970 boolean SaveTapeChecked_LevelSolved(int nr)
8972 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8973 "Level solved! Tape saved!", REQ_STAY_OPEN);
8976 void DumpTape(struct TapeInfo *tape)
8978 int tape_frame_counter;
8981 if (tape->no_valid_file)
8983 Warn("cannot dump -- no valid tape file found");
8990 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8991 tape->level_nr, tape->file_version, tape->game_version);
8992 Print(" (effective engine version %08d)\n",
8993 tape->engine_version);
8994 Print("Level series identifier: '%s'\n", tape->level_identifier);
8996 Print("Solution tape: %s\n",
8997 tape->solved ? "yes" :
8998 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9000 Print("Special tape properties: ");
9001 if (tape->property_bits == TAPE_PROPERTY_NONE)
9003 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9004 Print("[em_random_bug]");
9005 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9006 Print("[game_speed]");
9007 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9009 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9010 Print("[single_step]");
9011 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9012 Print("[snapshot]");
9013 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9014 Print("[replayed]");
9015 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9016 Print("[tas_keys]");
9017 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9018 Print("[small_graphics]");
9021 int year2 = tape->date / 10000;
9022 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9023 int month_index_raw = (tape->date / 100) % 100;
9024 int month_index = month_index_raw % 12; // prevent invalid index
9025 int month = month_index + 1;
9026 int day = tape->date % 100;
9028 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9032 tape_frame_counter = 0;
9034 for (i = 0; i < tape->length; i++)
9036 if (i >= MAX_TAPE_LEN)
9041 for (j = 0; j < MAX_PLAYERS; j++)
9043 if (tape->player_participates[j])
9045 int action = tape->pos[i].action[j];
9047 Print("%d:%02x ", j, action);
9048 Print("[%c%c%c%c|%c%c] - ",
9049 (action & JOY_LEFT ? '<' : ' '),
9050 (action & JOY_RIGHT ? '>' : ' '),
9051 (action & JOY_UP ? '^' : ' '),
9052 (action & JOY_DOWN ? 'v' : ' '),
9053 (action & JOY_BUTTON_1 ? '1' : ' '),
9054 (action & JOY_BUTTON_2 ? '2' : ' '));
9058 Print("(%03d) ", tape->pos[i].delay);
9059 Print("[%05d]\n", tape_frame_counter);
9061 tape_frame_counter += tape->pos[i].delay;
9067 void DumpTapes(void)
9069 static LevelDirTree *dumptape_leveldir = NULL;
9071 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9072 global.dumptape_leveldir);
9074 if (dumptape_leveldir == NULL)
9075 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9077 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9078 global.dumptape_level_nr > dumptape_leveldir->last_level)
9079 Fail("no such level number: %d", global.dumptape_level_nr);
9081 leveldir_current = dumptape_leveldir;
9083 if (options.mytapes)
9084 LoadTape(global.dumptape_level_nr);
9086 LoadSolutionTape(global.dumptape_level_nr);
9094 // ============================================================================
9095 // score file functions
9096 // ============================================================================
9098 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9102 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9104 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9105 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9106 scores->entry[i].score = 0;
9107 scores->entry[i].time = 0;
9109 scores->entry[i].id = -1;
9110 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9111 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9112 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9113 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9114 strcpy(scores->entry[i].country_code, "??");
9117 scores->num_entries = 0;
9118 scores->last_added = -1;
9119 scores->last_added_local = -1;
9121 scores->updated = FALSE;
9122 scores->uploaded = FALSE;
9123 scores->tape_downloaded = FALSE;
9124 scores->force_last_added = FALSE;
9126 // The following values are intentionally not reset here:
9130 // - continue_playing
9131 // - continue_on_return
9134 static void setScoreInfoToDefaults(void)
9136 setScoreInfoToDefaultsExt(&scores);
9139 static void setServerScoreInfoToDefaults(void)
9141 setScoreInfoToDefaultsExt(&server_scores);
9144 static void LoadScore_OLD(int nr)
9147 char *filename = getScoreFilename(nr);
9148 char cookie[MAX_LINE_LEN];
9149 char line[MAX_LINE_LEN];
9153 if (!(file = fopen(filename, MODE_READ)))
9156 // check file identifier
9157 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9159 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9160 cookie[strlen(cookie) - 1] = '\0';
9162 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9164 Warn("unknown format of score file '%s'", filename);
9171 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9173 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9174 Warn("fscanf() failed; %s", strerror(errno));
9176 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9179 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9180 line[strlen(line) - 1] = '\0';
9182 for (line_ptr = line; *line_ptr; line_ptr++)
9184 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9186 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9187 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9196 static void ConvertScore_OLD(void)
9198 // only convert score to time for levels that rate playing time over score
9199 if (!level.rate_time_over_score)
9202 // convert old score to playing time for score-less levels (like Supaplex)
9203 int time_final_max = 999;
9206 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9208 int score = scores.entry[i].score;
9210 if (score > 0 && score < time_final_max)
9211 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9215 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9217 scores->file_version = getFileVersion(file);
9218 scores->game_version = getFileVersion(file);
9223 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9225 char *level_identifier = NULL;
9226 int level_identifier_size;
9229 level_identifier_size = getFile16BitBE(file);
9231 level_identifier = checked_malloc(level_identifier_size);
9233 for (i = 0; i < level_identifier_size; i++)
9234 level_identifier[i] = getFile8Bit(file);
9236 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9237 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9239 checked_free(level_identifier);
9241 scores->level_nr = getFile16BitBE(file);
9242 scores->num_entries = getFile16BitBE(file);
9244 chunk_size = 2 + level_identifier_size + 2 + 2;
9249 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9253 for (i = 0; i < scores->num_entries; i++)
9255 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9256 scores->entry[i].name[j] = getFile8Bit(file);
9258 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9261 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9266 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9270 for (i = 0; i < scores->num_entries; i++)
9271 scores->entry[i].score = getFile16BitBE(file);
9273 chunk_size = scores->num_entries * 2;
9278 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9282 for (i = 0; i < scores->num_entries; i++)
9283 scores->entry[i].score = getFile32BitBE(file);
9285 chunk_size = scores->num_entries * 4;
9290 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9294 for (i = 0; i < scores->num_entries; i++)
9295 scores->entry[i].time = getFile32BitBE(file);
9297 chunk_size = scores->num_entries * 4;
9302 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9306 for (i = 0; i < scores->num_entries; i++)
9308 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9309 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9311 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9314 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9319 void LoadScore(int nr)
9321 char *filename = getScoreFilename(nr);
9322 char cookie[MAX_LINE_LEN];
9323 char chunk_name[CHUNK_ID_LEN + 1];
9325 boolean old_score_file_format = FALSE;
9328 // always start with reliable default values
9329 setScoreInfoToDefaults();
9331 if (!(file = openFile(filename, MODE_READ)))
9334 getFileChunkBE(file, chunk_name, NULL);
9335 if (strEqual(chunk_name, "RND1"))
9337 getFile32BitBE(file); // not used
9339 getFileChunkBE(file, chunk_name, NULL);
9340 if (!strEqual(chunk_name, "SCOR"))
9342 Warn("unknown format of score file '%s'", filename);
9349 else // check for old file format with cookie string
9351 strcpy(cookie, chunk_name);
9352 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9354 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9355 cookie[strlen(cookie) - 1] = '\0';
9357 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9359 Warn("unknown format of score file '%s'", filename);
9366 old_score_file_format = TRUE;
9369 if (old_score_file_format)
9371 // score files from versions before 4.2.4.0 without chunk structure
9374 // convert score to time, if possible (mainly for Supaplex levels)
9383 int (*loader)(File *, int, struct ScoreInfo *);
9387 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9388 { "INFO", -1, LoadScore_INFO },
9389 { "NAME", -1, LoadScore_NAME },
9390 { "SCOR", -1, LoadScore_SCOR },
9391 { "SC4R", -1, LoadScore_SC4R },
9392 { "TIME", -1, LoadScore_TIME },
9393 { "TAPE", -1, LoadScore_TAPE },
9398 while (getFileChunkBE(file, chunk_name, &chunk_size))
9402 while (chunk_info[i].name != NULL &&
9403 !strEqual(chunk_name, chunk_info[i].name))
9406 if (chunk_info[i].name == NULL)
9408 Warn("unknown chunk '%s' in score file '%s'",
9409 chunk_name, filename);
9411 ReadUnusedBytesFromFile(file, chunk_size);
9413 else if (chunk_info[i].size != -1 &&
9414 chunk_info[i].size != chunk_size)
9416 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9417 chunk_size, chunk_name, filename);
9419 ReadUnusedBytesFromFile(file, chunk_size);
9423 // call function to load this score chunk
9424 int chunk_size_expected =
9425 (chunk_info[i].loader)(file, chunk_size, &scores);
9427 // the size of some chunks cannot be checked before reading other
9428 // chunks first (like "HEAD" and "BODY") that contain some header
9429 // information, so check them here
9430 if (chunk_size_expected != chunk_size)
9432 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9433 chunk_size, chunk_name, filename);
9442 #if ENABLE_HISTORIC_CHUNKS
9443 void SaveScore_OLD(int nr)
9446 char *filename = getScoreFilename(nr);
9449 // used instead of "leveldir_current->subdir" (for network games)
9450 InitScoreDirectory(levelset.identifier);
9452 if (!(file = fopen(filename, MODE_WRITE)))
9454 Warn("cannot save score for level %d", nr);
9459 fprintf(file, "%s\n\n", SCORE_COOKIE);
9461 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9462 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9466 SetFilePermissions(filename, PERMS_PRIVATE);
9470 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9472 putFileVersion(file, scores->file_version);
9473 putFileVersion(file, scores->game_version);
9476 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9478 int level_identifier_size = strlen(scores->level_identifier) + 1;
9481 putFile16BitBE(file, level_identifier_size);
9483 for (i = 0; i < level_identifier_size; i++)
9484 putFile8Bit(file, scores->level_identifier[i]);
9486 putFile16BitBE(file, scores->level_nr);
9487 putFile16BitBE(file, scores->num_entries);
9490 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9494 for (i = 0; i < scores->num_entries; i++)
9496 int name_size = strlen(scores->entry[i].name);
9498 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9499 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9503 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9507 for (i = 0; i < scores->num_entries; i++)
9508 putFile16BitBE(file, scores->entry[i].score);
9511 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9515 for (i = 0; i < scores->num_entries; i++)
9516 putFile32BitBE(file, scores->entry[i].score);
9519 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9523 for (i = 0; i < scores->num_entries; i++)
9524 putFile32BitBE(file, scores->entry[i].time);
9527 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9531 for (i = 0; i < scores->num_entries; i++)
9533 int size = strlen(scores->entry[i].tape_basename);
9535 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9536 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9540 static void SaveScoreToFilename(char *filename)
9543 int info_chunk_size;
9544 int name_chunk_size;
9545 int scor_chunk_size;
9546 int sc4r_chunk_size;
9547 int time_chunk_size;
9548 int tape_chunk_size;
9549 boolean has_large_score_values;
9552 if (!(file = fopen(filename, MODE_WRITE)))
9554 Warn("cannot save score file '%s'", filename);
9559 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9560 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9561 scor_chunk_size = scores.num_entries * 2;
9562 sc4r_chunk_size = scores.num_entries * 4;
9563 time_chunk_size = scores.num_entries * 4;
9564 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9566 has_large_score_values = FALSE;
9567 for (i = 0; i < scores.num_entries; i++)
9568 if (scores.entry[i].score > 0xffff)
9569 has_large_score_values = TRUE;
9571 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9572 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9574 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9575 SaveScore_VERS(file, &scores);
9577 putFileChunkBE(file, "INFO", info_chunk_size);
9578 SaveScore_INFO(file, &scores);
9580 putFileChunkBE(file, "NAME", name_chunk_size);
9581 SaveScore_NAME(file, &scores);
9583 if (has_large_score_values)
9585 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9586 SaveScore_SC4R(file, &scores);
9590 putFileChunkBE(file, "SCOR", scor_chunk_size);
9591 SaveScore_SCOR(file, &scores);
9594 putFileChunkBE(file, "TIME", time_chunk_size);
9595 SaveScore_TIME(file, &scores);
9597 putFileChunkBE(file, "TAPE", tape_chunk_size);
9598 SaveScore_TAPE(file, &scores);
9602 SetFilePermissions(filename, PERMS_PRIVATE);
9605 void SaveScore(int nr)
9607 char *filename = getScoreFilename(nr);
9610 // used instead of "leveldir_current->subdir" (for network games)
9611 InitScoreDirectory(levelset.identifier);
9613 scores.file_version = FILE_VERSION_ACTUAL;
9614 scores.game_version = GAME_VERSION_ACTUAL;
9616 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9617 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9618 scores.level_nr = level_nr;
9620 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9621 if (scores.entry[i].score == 0 &&
9622 scores.entry[i].time == 0 &&
9623 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9626 scores.num_entries = i;
9628 if (scores.num_entries == 0)
9631 SaveScoreToFilename(filename);
9634 static void LoadServerScoreFromCache(int nr)
9636 struct ScoreEntry score_entry;
9645 { &score_entry.score, FALSE, 0 },
9646 { &score_entry.time, FALSE, 0 },
9647 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9648 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9649 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9650 { &score_entry.id, FALSE, 0 },
9651 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9652 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9653 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9654 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9658 char *filename = getScoreCacheFilename(nr);
9659 SetupFileHash *score_hash = loadSetupFileHash(filename);
9662 server_scores.num_entries = 0;
9664 if (score_hash == NULL)
9667 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9669 score_entry = server_scores.entry[i];
9671 for (j = 0; score_mapping[j].value != NULL; j++)
9675 sprintf(token, "%02d.%d", i, j);
9677 char *value = getHashEntry(score_hash, token);
9682 if (score_mapping[j].is_string)
9684 char *score_value = (char *)score_mapping[j].value;
9685 int value_size = score_mapping[j].string_size;
9687 strncpy(score_value, value, value_size);
9688 score_value[value_size] = '\0';
9692 int *score_value = (int *)score_mapping[j].value;
9694 *score_value = atoi(value);
9697 server_scores.num_entries = i + 1;
9700 server_scores.entry[i] = score_entry;
9703 freeSetupFileHash(score_hash);
9706 void LoadServerScore(int nr, boolean download_score)
9708 if (!setup.use_api_server)
9711 // always start with reliable default values
9712 setServerScoreInfoToDefaults();
9714 // 1st step: load server scores from cache file (which may not exist)
9715 // (this should prevent reading it while the thread is writing to it)
9716 LoadServerScoreFromCache(nr);
9718 if (download_score && runtime.use_api_server)
9720 // 2nd step: download server scores from score server to cache file
9721 // (as thread, as it might time out if the server is not reachable)
9722 ApiGetScoreAsThread(nr);
9726 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9728 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9730 // if score tape not uploaded, ask for uploading missing tapes later
9731 if (!setup.has_remaining_tapes)
9732 setup.ask_for_remaining_tapes = TRUE;
9734 setup.provide_uploading_tapes = TRUE;
9735 setup.has_remaining_tapes = TRUE;
9737 SaveSetup_ServerSetup();
9740 void SaveServerScore(int nr, boolean tape_saved)
9742 if (!runtime.use_api_server)
9744 PrepareScoreTapesForUpload(leveldir_current->subdir);
9749 ApiAddScoreAsThread(nr, tape_saved, NULL);
9752 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9753 char *score_tape_filename)
9755 if (!runtime.use_api_server)
9758 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9761 void LoadLocalAndServerScore(int nr, boolean download_score)
9763 int last_added_local = scores.last_added_local;
9764 boolean force_last_added = scores.force_last_added;
9766 // needed if only showing server scores
9767 setScoreInfoToDefaults();
9769 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9772 // restore last added local score entry (before merging server scores)
9773 scores.last_added = scores.last_added_local = last_added_local;
9775 if (setup.use_api_server &&
9776 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9778 // load server scores from cache file and trigger update from server
9779 LoadServerScore(nr, download_score);
9781 // merge local scores with scores from server
9785 if (force_last_added)
9786 scores.force_last_added = force_last_added;
9790 // ============================================================================
9791 // setup file functions
9792 // ============================================================================
9794 #define TOKEN_STR_PLAYER_PREFIX "player_"
9797 static struct TokenInfo global_setup_tokens[] =
9801 &setup.player_name, "player_name"
9805 &setup.multiple_users, "multiple_users"
9809 &setup.sound, "sound"
9813 &setup.sound_loops, "repeating_sound_loops"
9817 &setup.sound_music, "background_music"
9821 &setup.sound_simple, "simple_sound_effects"
9825 &setup.toons, "toons"
9829 &setup.global_animations, "global_animations"
9833 &setup.scroll_delay, "scroll_delay"
9837 &setup.forced_scroll_delay, "forced_scroll_delay"
9841 &setup.scroll_delay_value, "scroll_delay_value"
9845 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9849 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9853 &setup.fade_screens, "fade_screens"
9857 &setup.autorecord, "automatic_tape_recording"
9861 &setup.autorecord_after_replay, "autorecord_after_replay"
9865 &setup.auto_pause_on_start, "auto_pause_on_start"
9869 &setup.show_titlescreen, "show_titlescreen"
9873 &setup.quick_doors, "quick_doors"
9877 &setup.team_mode, "team_mode"
9881 &setup.handicap, "handicap"
9885 &setup.skip_levels, "skip_levels"
9889 &setup.increment_levels, "increment_levels"
9893 &setup.auto_play_next_level, "auto_play_next_level"
9897 &setup.count_score_after_game, "count_score_after_game"
9901 &setup.show_scores_after_game, "show_scores_after_game"
9905 &setup.time_limit, "time_limit"
9909 &setup.fullscreen, "fullscreen"
9913 &setup.window_scaling_percent, "window_scaling_percent"
9917 &setup.window_scaling_quality, "window_scaling_quality"
9921 &setup.screen_rendering_mode, "screen_rendering_mode"
9925 &setup.vsync_mode, "vsync_mode"
9929 &setup.ask_on_escape, "ask_on_escape"
9933 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9937 &setup.ask_on_game_over, "ask_on_game_over"
9941 &setup.ask_on_quit_game, "ask_on_quit_game"
9945 &setup.ask_on_quit_program, "ask_on_quit_program"
9949 &setup.quick_switch, "quick_player_switch"
9953 &setup.input_on_focus, "input_on_focus"
9957 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9961 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9965 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9969 &setup.game_speed_extended, "game_speed_extended"
9973 &setup.game_frame_delay, "game_frame_delay"
9977 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9981 &setup.bd_skip_hatching, "bd_skip_hatching"
9985 &setup.bd_scroll_delay, "bd_scroll_delay"
9989 &setup.bd_smooth_movements, "bd_smooth_movements"
9993 &setup.sp_show_border_elements, "sp_show_border_elements"
9997 &setup.small_game_graphics, "small_game_graphics"
10001 &setup.show_load_save_buttons, "show_load_save_buttons"
10005 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10009 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10013 &setup.graphics_set, "graphics_set"
10017 &setup.sounds_set, "sounds_set"
10021 &setup.music_set, "music_set"
10025 &setup.override_level_graphics, "override_level_graphics"
10029 &setup.override_level_sounds, "override_level_sounds"
10033 &setup.override_level_music, "override_level_music"
10037 &setup.volume_simple, "volume_simple"
10041 &setup.volume_loops, "volume_loops"
10045 &setup.volume_music, "volume_music"
10049 &setup.network_mode, "network_mode"
10053 &setup.network_player_nr, "network_player"
10057 &setup.network_server_hostname, "network_server_hostname"
10061 &setup.touch.control_type, "touch.control_type"
10065 &setup.touch.move_distance, "touch.move_distance"
10069 &setup.touch.drop_distance, "touch.drop_distance"
10073 &setup.touch.transparency, "touch.transparency"
10077 &setup.touch.draw_outlined, "touch.draw_outlined"
10081 &setup.touch.draw_pressed, "touch.draw_pressed"
10085 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10089 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10093 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10097 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10101 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10105 static struct TokenInfo auto_setup_tokens[] =
10109 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10113 static struct TokenInfo server_setup_tokens[] =
10117 &setup.player_uuid, "player_uuid"
10121 &setup.player_version, "player_version"
10125 &setup.use_api_server, TEST_PREFIX "use_api_server"
10129 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10133 &setup.api_server_password, TEST_PREFIX "api_server_password"
10137 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10141 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10145 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10149 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10153 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10157 static struct TokenInfo editor_setup_tokens[] =
10161 &setup.editor.el_classic, "editor.el_classic"
10165 &setup.editor.el_custom, "editor.el_custom"
10169 &setup.editor.el_user_defined, "editor.el_user_defined"
10173 &setup.editor.el_dynamic, "editor.el_dynamic"
10177 &setup.editor.el_headlines, "editor.el_headlines"
10181 &setup.editor.show_element_token, "editor.show_element_token"
10185 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10189 static struct TokenInfo editor_cascade_setup_tokens[] =
10193 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10197 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10201 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10205 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10209 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10213 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10217 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10221 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10225 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10229 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10233 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10237 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10241 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10245 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10249 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10253 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10257 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10261 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10265 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10269 static struct TokenInfo shortcut_setup_tokens[] =
10273 &setup.shortcut.save_game, "shortcut.save_game"
10277 &setup.shortcut.load_game, "shortcut.load_game"
10281 &setup.shortcut.restart_game, "shortcut.restart_game"
10285 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10289 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10293 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10297 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10301 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10305 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10309 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10313 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10317 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10321 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10325 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10329 &setup.shortcut.tape_record, "shortcut.tape_record"
10333 &setup.shortcut.tape_play, "shortcut.tape_play"
10337 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10341 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10345 &setup.shortcut.sound_music, "shortcut.sound_music"
10349 &setup.shortcut.snap_left, "shortcut.snap_left"
10353 &setup.shortcut.snap_right, "shortcut.snap_right"
10357 &setup.shortcut.snap_up, "shortcut.snap_up"
10361 &setup.shortcut.snap_down, "shortcut.snap_down"
10365 static struct SetupInputInfo setup_input;
10366 static struct TokenInfo player_setup_tokens[] =
10370 &setup_input.use_joystick, ".use_joystick"
10374 &setup_input.joy.device_name, ".joy.device_name"
10378 &setup_input.joy.xleft, ".joy.xleft"
10382 &setup_input.joy.xmiddle, ".joy.xmiddle"
10386 &setup_input.joy.xright, ".joy.xright"
10390 &setup_input.joy.yupper, ".joy.yupper"
10394 &setup_input.joy.ymiddle, ".joy.ymiddle"
10398 &setup_input.joy.ylower, ".joy.ylower"
10402 &setup_input.joy.snap, ".joy.snap_field"
10406 &setup_input.joy.drop, ".joy.place_bomb"
10410 &setup_input.key.left, ".key.move_left"
10414 &setup_input.key.right, ".key.move_right"
10418 &setup_input.key.up, ".key.move_up"
10422 &setup_input.key.down, ".key.move_down"
10426 &setup_input.key.snap, ".key.snap_field"
10430 &setup_input.key.drop, ".key.place_bomb"
10434 static struct TokenInfo system_setup_tokens[] =
10438 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10442 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10446 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10450 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10454 static struct TokenInfo internal_setup_tokens[] =
10458 &setup.internal.program_title, "program_title"
10462 &setup.internal.program_version, "program_version"
10466 &setup.internal.program_author, "program_author"
10470 &setup.internal.program_email, "program_email"
10474 &setup.internal.program_website, "program_website"
10478 &setup.internal.program_copyright, "program_copyright"
10482 &setup.internal.program_company, "program_company"
10486 &setup.internal.program_icon_file, "program_icon_file"
10490 &setup.internal.default_graphics_set, "default_graphics_set"
10494 &setup.internal.default_sounds_set, "default_sounds_set"
10498 &setup.internal.default_music_set, "default_music_set"
10502 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10506 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10510 &setup.internal.fallback_music_file, "fallback_music_file"
10514 &setup.internal.default_level_series, "default_level_series"
10518 &setup.internal.default_window_width, "default_window_width"
10522 &setup.internal.default_window_height, "default_window_height"
10526 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10530 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10534 &setup.internal.create_user_levelset, "create_user_levelset"
10538 &setup.internal.info_screens_from_main, "info_screens_from_main"
10542 &setup.internal.menu_game, "menu_game"
10546 &setup.internal.menu_engines, "menu_engines"
10550 &setup.internal.menu_editor, "menu_editor"
10554 &setup.internal.menu_graphics, "menu_graphics"
10558 &setup.internal.menu_sound, "menu_sound"
10562 &setup.internal.menu_artwork, "menu_artwork"
10566 &setup.internal.menu_input, "menu_input"
10570 &setup.internal.menu_touch, "menu_touch"
10574 &setup.internal.menu_shortcuts, "menu_shortcuts"
10578 &setup.internal.menu_exit, "menu_exit"
10582 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10586 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10590 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10594 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10598 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10602 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10606 &setup.internal.info_title, "info_title"
10610 &setup.internal.info_elements, "info_elements"
10614 &setup.internal.info_music, "info_music"
10618 &setup.internal.info_credits, "info_credits"
10622 &setup.internal.info_program, "info_program"
10626 &setup.internal.info_version, "info_version"
10630 &setup.internal.info_levelset, "info_levelset"
10634 &setup.internal.info_exit, "info_exit"
10638 static struct TokenInfo debug_setup_tokens[] =
10642 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10646 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10650 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10654 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10658 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10662 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10666 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10670 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10674 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10678 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10682 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10686 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10690 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10694 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10698 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10702 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10706 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10710 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10714 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10718 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10722 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10725 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10729 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10733 &setup.debug.xsn_mode, "debug.xsn_mode"
10737 &setup.debug.xsn_percent, "debug.xsn_percent"
10741 static struct TokenInfo options_setup_tokens[] =
10745 &setup.options.verbose, "options.verbose"
10749 &setup.options.debug, "options.debug"
10753 &setup.options.debug_mode, "options.debug_mode"
10757 static void setSetupInfoToDefaults(struct SetupInfo *si)
10761 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10763 si->multiple_users = TRUE;
10766 si->sound_loops = TRUE;
10767 si->sound_music = TRUE;
10768 si->sound_simple = TRUE;
10770 si->global_animations = TRUE;
10771 si->scroll_delay = TRUE;
10772 si->forced_scroll_delay = FALSE;
10773 si->scroll_delay_value = STD_SCROLL_DELAY;
10774 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10775 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10776 si->fade_screens = TRUE;
10777 si->autorecord = TRUE;
10778 si->autorecord_after_replay = TRUE;
10779 si->auto_pause_on_start = FALSE;
10780 si->show_titlescreen = TRUE;
10781 si->quick_doors = FALSE;
10782 si->team_mode = FALSE;
10783 si->handicap = TRUE;
10784 si->skip_levels = TRUE;
10785 si->increment_levels = TRUE;
10786 si->auto_play_next_level = TRUE;
10787 si->count_score_after_game = TRUE;
10788 si->show_scores_after_game = TRUE;
10789 si->time_limit = TRUE;
10790 si->fullscreen = FALSE;
10791 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10792 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10793 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10794 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10795 si->ask_on_escape = TRUE;
10796 si->ask_on_escape_editor = TRUE;
10797 si->ask_on_game_over = TRUE;
10798 si->ask_on_quit_game = TRUE;
10799 si->ask_on_quit_program = TRUE;
10800 si->quick_switch = FALSE;
10801 si->input_on_focus = FALSE;
10802 si->prefer_aga_graphics = TRUE;
10803 si->prefer_lowpass_sounds = FALSE;
10804 si->prefer_extra_panel_items = TRUE;
10805 si->game_speed_extended = FALSE;
10806 si->game_frame_delay = GAME_FRAME_DELAY;
10807 si->bd_skip_uncovering = FALSE;
10808 si->bd_skip_hatching = FALSE;
10809 si->bd_scroll_delay = TRUE;
10810 si->bd_smooth_movements = AUTO;
10811 si->sp_show_border_elements = FALSE;
10812 si->small_game_graphics = FALSE;
10813 si->show_load_save_buttons = FALSE;
10814 si->show_undo_redo_buttons = FALSE;
10815 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10817 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10818 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10819 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10821 si->override_level_graphics = FALSE;
10822 si->override_level_sounds = FALSE;
10823 si->override_level_music = FALSE;
10825 si->volume_simple = 100; // percent
10826 si->volume_loops = 100; // percent
10827 si->volume_music = 100; // percent
10829 si->network_mode = FALSE;
10830 si->network_player_nr = 0; // first player
10831 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10833 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10834 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10835 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10836 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10837 si->touch.draw_outlined = TRUE;
10838 si->touch.draw_pressed = TRUE;
10840 for (i = 0; i < 2; i++)
10842 char *default_grid_button[6][2] =
10848 { "111222", " vv " },
10849 { "111222", " vv " }
10851 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10852 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10853 int min_xsize = MIN(6, grid_xsize);
10854 int min_ysize = MIN(6, grid_ysize);
10855 int startx = grid_xsize - min_xsize;
10856 int starty = grid_ysize - min_ysize;
10859 // virtual buttons grid can only be set to defaults if video is initialized
10860 // (this will be repeated if virtual buttons are not loaded from setup file)
10861 if (video.initialized)
10863 si->touch.grid_xsize[i] = grid_xsize;
10864 si->touch.grid_ysize[i] = grid_ysize;
10868 si->touch.grid_xsize[i] = -1;
10869 si->touch.grid_ysize[i] = -1;
10872 for (x = 0; x < MAX_GRID_XSIZE; x++)
10873 for (y = 0; y < MAX_GRID_YSIZE; y++)
10874 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10876 for (x = 0; x < min_xsize; x++)
10877 for (y = 0; y < min_ysize; y++)
10878 si->touch.grid_button[i][x][starty + y] =
10879 default_grid_button[y][0][x];
10881 for (x = 0; x < min_xsize; x++)
10882 for (y = 0; y < min_ysize; y++)
10883 si->touch.grid_button[i][startx + x][starty + y] =
10884 default_grid_button[y][1][x];
10887 si->touch.grid_initialized = video.initialized;
10889 si->touch.overlay_buttons = FALSE;
10891 si->editor.el_boulderdash = TRUE;
10892 si->editor.el_boulderdash_native = TRUE;
10893 si->editor.el_emerald_mine = TRUE;
10894 si->editor.el_emerald_mine_club = TRUE;
10895 si->editor.el_more = TRUE;
10896 si->editor.el_sokoban = TRUE;
10897 si->editor.el_supaplex = TRUE;
10898 si->editor.el_diamond_caves = TRUE;
10899 si->editor.el_dx_boulderdash = TRUE;
10901 si->editor.el_mirror_magic = TRUE;
10902 si->editor.el_deflektor = TRUE;
10904 si->editor.el_chars = TRUE;
10905 si->editor.el_steel_chars = TRUE;
10907 si->editor.el_classic = TRUE;
10908 si->editor.el_custom = TRUE;
10910 si->editor.el_user_defined = FALSE;
10911 si->editor.el_dynamic = TRUE;
10913 si->editor.el_headlines = TRUE;
10915 si->editor.show_element_token = FALSE;
10917 si->editor.show_read_only_warning = TRUE;
10919 si->editor.use_template_for_new_levels = TRUE;
10921 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10922 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10923 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10924 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10925 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10927 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10928 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10929 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10930 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10931 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10933 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10934 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10935 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10936 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10937 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10938 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10940 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10941 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10942 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10944 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10945 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10946 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10947 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10949 for (i = 0; i < MAX_PLAYERS; i++)
10951 si->input[i].use_joystick = FALSE;
10952 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10953 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10954 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10955 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10956 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10957 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10958 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10959 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10960 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10961 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10962 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10963 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10964 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10965 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10966 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10969 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10970 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10971 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10972 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10974 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10975 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10976 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10977 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10978 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10979 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10980 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10982 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10984 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10985 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10986 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10988 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10989 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10990 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10992 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10993 si->internal.choose_from_top_leveldir = FALSE;
10994 si->internal.show_scaling_in_title = TRUE;
10995 si->internal.create_user_levelset = TRUE;
10996 si->internal.info_screens_from_main = FALSE;
10998 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10999 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11001 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11002 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11003 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11004 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11005 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11006 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11007 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11008 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11009 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11010 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11012 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11013 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11014 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11015 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11016 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11017 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11018 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11019 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11020 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11021 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11023 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11024 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11026 si->debug.show_frames_per_second = FALSE;
11028 si->debug.xsn_mode = AUTO;
11029 si->debug.xsn_percent = 0;
11031 si->options.verbose = FALSE;
11032 si->options.debug = FALSE;
11033 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11035 #if defined(PLATFORM_ANDROID)
11036 si->fullscreen = TRUE;
11037 si->touch.overlay_buttons = TRUE;
11040 setHideSetupEntry(&setup.debug.xsn_mode);
11043 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11045 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11048 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11050 si->player_uuid = NULL; // (will be set later)
11051 si->player_version = 1; // (will be set later)
11053 si->use_api_server = TRUE;
11054 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11055 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11056 si->ask_for_uploading_tapes = TRUE;
11057 si->ask_for_remaining_tapes = FALSE;
11058 si->provide_uploading_tapes = TRUE;
11059 si->ask_for_using_api_server = TRUE;
11060 si->has_remaining_tapes = FALSE;
11063 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11065 si->editor_cascade.el_bd = TRUE;
11066 si->editor_cascade.el_bd_native = TRUE;
11067 si->editor_cascade.el_em = TRUE;
11068 si->editor_cascade.el_emc = TRUE;
11069 si->editor_cascade.el_rnd = TRUE;
11070 si->editor_cascade.el_sb = TRUE;
11071 si->editor_cascade.el_sp = TRUE;
11072 si->editor_cascade.el_dc = TRUE;
11073 si->editor_cascade.el_dx = TRUE;
11075 si->editor_cascade.el_mm = TRUE;
11076 si->editor_cascade.el_df = TRUE;
11078 si->editor_cascade.el_chars = FALSE;
11079 si->editor_cascade.el_steel_chars = FALSE;
11080 si->editor_cascade.el_ce = FALSE;
11081 si->editor_cascade.el_ge = FALSE;
11082 si->editor_cascade.el_es = FALSE;
11083 si->editor_cascade.el_ref = FALSE;
11084 si->editor_cascade.el_user = FALSE;
11085 si->editor_cascade.el_dynamic = FALSE;
11088 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11090 static char *getHideSetupToken(void *setup_value)
11092 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11094 if (setup_value != NULL)
11095 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11097 return hide_setup_token;
11100 void setHideSetupEntry(void *setup_value)
11102 char *hide_setup_token = getHideSetupToken(setup_value);
11104 if (hide_setup_hash == NULL)
11105 hide_setup_hash = newSetupFileHash();
11107 if (setup_value != NULL)
11108 setHashEntry(hide_setup_hash, hide_setup_token, "");
11111 void removeHideSetupEntry(void *setup_value)
11113 char *hide_setup_token = getHideSetupToken(setup_value);
11115 if (setup_value != NULL)
11116 removeHashEntry(hide_setup_hash, hide_setup_token);
11119 boolean hideSetupEntry(void *setup_value)
11121 char *hide_setup_token = getHideSetupToken(setup_value);
11123 return (setup_value != NULL &&
11124 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11127 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11128 struct TokenInfo *token_info,
11129 int token_nr, char *token_text)
11131 char *token_hide_text = getStringCat2(token_text, ".hide");
11132 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11134 // set the value of this setup option in the setup option structure
11135 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11137 // check if this setup option should be hidden in the setup menu
11138 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11139 setHideSetupEntry(token_info[token_nr].value);
11141 free(token_hide_text);
11144 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11145 struct TokenInfo *token_info,
11148 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11149 token_info[token_nr].text);
11152 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11156 if (!setup_file_hash)
11159 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11160 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11162 setup.touch.grid_initialized = TRUE;
11163 for (i = 0; i < 2; i++)
11165 int grid_xsize = setup.touch.grid_xsize[i];
11166 int grid_ysize = setup.touch.grid_ysize[i];
11169 // if virtual buttons are not loaded from setup file, repeat initializing
11170 // virtual buttons grid with default values later when video is initialized
11171 if (grid_xsize == -1 ||
11174 setup.touch.grid_initialized = FALSE;
11179 for (y = 0; y < grid_ysize; y++)
11181 char token_string[MAX_LINE_LEN];
11183 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11185 char *value_string = getHashEntry(setup_file_hash, token_string);
11187 if (value_string == NULL)
11190 for (x = 0; x < grid_xsize; x++)
11192 char c = value_string[x];
11194 setup.touch.grid_button[i][x][y] =
11195 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11200 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11201 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11203 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11204 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11206 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11210 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11212 setup_input = setup.input[pnr];
11213 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11215 char full_token[100];
11217 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11218 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11221 setup.input[pnr] = setup_input;
11224 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11225 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11227 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11228 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11230 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11231 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11233 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11234 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11236 setHideRelatedSetupEntries();
11239 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11243 if (!setup_file_hash)
11246 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11247 setSetupInfo(auto_setup_tokens, i,
11248 getHashEntry(setup_file_hash,
11249 auto_setup_tokens[i].text));
11252 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11256 if (!setup_file_hash)
11259 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11260 setSetupInfo(server_setup_tokens, i,
11261 getHashEntry(setup_file_hash,
11262 server_setup_tokens[i].text));
11265 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11269 if (!setup_file_hash)
11272 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11273 setSetupInfo(editor_cascade_setup_tokens, i,
11274 getHashEntry(setup_file_hash,
11275 editor_cascade_setup_tokens[i].text));
11278 void LoadUserNames(void)
11280 int last_user_nr = user.nr;
11283 if (global.user_names != NULL)
11285 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11286 checked_free(global.user_names[i]);
11288 checked_free(global.user_names);
11291 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11293 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11297 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11299 if (setup_file_hash)
11301 char *player_name = getHashEntry(setup_file_hash, "player_name");
11303 global.user_names[i] = getFixedUserName(player_name);
11305 freeSetupFileHash(setup_file_hash);
11308 if (global.user_names[i] == NULL)
11309 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11312 user.nr = last_user_nr;
11315 void LoadSetupFromFilename(char *filename)
11317 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11319 if (setup_file_hash)
11321 decodeSetupFileHash_Default(setup_file_hash);
11323 freeSetupFileHash(setup_file_hash);
11327 Debug("setup", "using default setup values");
11331 static void LoadSetup_SpecialPostProcessing(void)
11333 char *player_name_new;
11335 // needed to work around problems with fixed length strings
11336 player_name_new = getFixedUserName(setup.player_name);
11337 free(setup.player_name);
11338 setup.player_name = player_name_new;
11340 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11341 if (setup.scroll_delay == FALSE)
11343 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11344 setup.scroll_delay = TRUE; // now always "on"
11347 // make sure that scroll delay value stays inside valid range
11348 setup.scroll_delay_value =
11349 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11352 void LoadSetup_Default(void)
11356 // always start with reliable default values
11357 setSetupInfoToDefaults(&setup);
11359 // try to load setup values from default setup file
11360 filename = getDefaultSetupFilename();
11362 if (fileExists(filename))
11363 LoadSetupFromFilename(filename);
11365 // try to load setup values from platform setup file
11366 filename = getPlatformSetupFilename();
11368 if (fileExists(filename))
11369 LoadSetupFromFilename(filename);
11371 // try to load setup values from user setup file
11372 filename = getSetupFilename();
11374 LoadSetupFromFilename(filename);
11376 LoadSetup_SpecialPostProcessing();
11379 void LoadSetup_AutoSetup(void)
11381 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11382 SetupFileHash *setup_file_hash = NULL;
11384 // always start with reliable default values
11385 setSetupInfoToDefaults_AutoSetup(&setup);
11387 setup_file_hash = loadSetupFileHash(filename);
11389 if (setup_file_hash)
11391 decodeSetupFileHash_AutoSetup(setup_file_hash);
11393 freeSetupFileHash(setup_file_hash);
11399 void LoadSetup_ServerSetup(void)
11401 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11402 SetupFileHash *setup_file_hash = NULL;
11404 // always start with reliable default values
11405 setSetupInfoToDefaults_ServerSetup(&setup);
11407 setup_file_hash = loadSetupFileHash(filename);
11409 if (setup_file_hash)
11411 decodeSetupFileHash_ServerSetup(setup_file_hash);
11413 freeSetupFileHash(setup_file_hash);
11418 if (setup.player_uuid == NULL)
11420 // player UUID does not yet exist in setup file
11421 setup.player_uuid = getStringCopy(getUUID());
11422 setup.player_version = 2;
11424 SaveSetup_ServerSetup();
11428 void LoadSetup_EditorCascade(void)
11430 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11431 SetupFileHash *setup_file_hash = NULL;
11433 // always start with reliable default values
11434 setSetupInfoToDefaults_EditorCascade(&setup);
11436 setup_file_hash = loadSetupFileHash(filename);
11438 if (setup_file_hash)
11440 decodeSetupFileHash_EditorCascade(setup_file_hash);
11442 freeSetupFileHash(setup_file_hash);
11448 void LoadSetup(void)
11450 LoadSetup_Default();
11451 LoadSetup_AutoSetup();
11452 LoadSetup_ServerSetup();
11453 LoadSetup_EditorCascade();
11456 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11457 char *mapping_line)
11459 char mapping_guid[MAX_LINE_LEN];
11460 char *mapping_start, *mapping_end;
11462 // get GUID from game controller mapping line: copy complete line
11463 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11464 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11466 // get GUID from game controller mapping line: cut after GUID part
11467 mapping_start = strchr(mapping_guid, ',');
11468 if (mapping_start != NULL)
11469 *mapping_start = '\0';
11471 // cut newline from game controller mapping line
11472 mapping_end = strchr(mapping_line, '\n');
11473 if (mapping_end != NULL)
11474 *mapping_end = '\0';
11476 // add mapping entry to game controller mappings hash
11477 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11480 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11485 if (!(file = fopen(filename, MODE_READ)))
11487 Warn("cannot read game controller mappings file '%s'", filename);
11492 while (!feof(file))
11494 char line[MAX_LINE_LEN];
11496 if (!fgets(line, MAX_LINE_LEN, file))
11499 addGameControllerMappingToHash(mappings_hash, line);
11505 void SaveSetup_Default(void)
11507 char *filename = getSetupFilename();
11511 InitUserDataDirectory();
11513 if (!(file = fopen(filename, MODE_WRITE)))
11515 Warn("cannot write setup file '%s'", filename);
11520 fprintFileHeader(file, SETUP_FILENAME);
11522 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11524 // just to make things nicer :)
11525 if (global_setup_tokens[i].value == &setup.multiple_users ||
11526 global_setup_tokens[i].value == &setup.sound ||
11527 global_setup_tokens[i].value == &setup.graphics_set ||
11528 global_setup_tokens[i].value == &setup.volume_simple ||
11529 global_setup_tokens[i].value == &setup.network_mode ||
11530 global_setup_tokens[i].value == &setup.touch.control_type ||
11531 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11532 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11533 fprintf(file, "\n");
11535 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11538 for (i = 0; i < 2; i++)
11540 int grid_xsize = setup.touch.grid_xsize[i];
11541 int grid_ysize = setup.touch.grid_ysize[i];
11544 fprintf(file, "\n");
11546 for (y = 0; y < grid_ysize; y++)
11548 char token_string[MAX_LINE_LEN];
11549 char value_string[MAX_LINE_LEN];
11551 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11553 for (x = 0; x < grid_xsize; x++)
11555 char c = setup.touch.grid_button[i][x][y];
11557 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11560 value_string[grid_xsize] = '\0';
11562 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11566 fprintf(file, "\n");
11567 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11568 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11570 fprintf(file, "\n");
11571 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11572 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11574 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11578 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11579 fprintf(file, "\n");
11581 setup_input = setup.input[pnr];
11582 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11583 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11586 fprintf(file, "\n");
11587 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11588 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11590 // (internal setup values not saved to user setup file)
11592 fprintf(file, "\n");
11593 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11594 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11595 setup.debug.xsn_mode != AUTO)
11596 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11598 fprintf(file, "\n");
11599 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11600 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11604 SetFilePermissions(filename, PERMS_PRIVATE);
11607 void SaveSetup_AutoSetup(void)
11609 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11613 InitUserDataDirectory();
11615 if (!(file = fopen(filename, MODE_WRITE)))
11617 Warn("cannot write auto setup file '%s'", filename);
11624 fprintFileHeader(file, AUTOSETUP_FILENAME);
11626 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11627 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11631 SetFilePermissions(filename, PERMS_PRIVATE);
11636 void SaveSetup_ServerSetup(void)
11638 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11642 InitUserDataDirectory();
11644 if (!(file = fopen(filename, MODE_WRITE)))
11646 Warn("cannot write server setup file '%s'", filename);
11653 fprintFileHeader(file, SERVERSETUP_FILENAME);
11655 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11657 // just to make things nicer :)
11658 if (server_setup_tokens[i].value == &setup.use_api_server)
11659 fprintf(file, "\n");
11661 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11666 SetFilePermissions(filename, PERMS_PRIVATE);
11671 void SaveSetup_EditorCascade(void)
11673 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11677 InitUserDataDirectory();
11679 if (!(file = fopen(filename, MODE_WRITE)))
11681 Warn("cannot write editor cascade state file '%s'", filename);
11688 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11690 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11691 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11695 SetFilePermissions(filename, PERMS_PRIVATE);
11700 void SaveSetup(void)
11702 SaveSetup_Default();
11703 SaveSetup_AutoSetup();
11704 SaveSetup_ServerSetup();
11705 SaveSetup_EditorCascade();
11708 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11713 if (!(file = fopen(filename, MODE_WRITE)))
11715 Warn("cannot write game controller mappings file '%s'", filename);
11720 BEGIN_HASH_ITERATION(mappings_hash, itr)
11722 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11724 END_HASH_ITERATION(mappings_hash, itr)
11729 void SaveSetup_AddGameControllerMapping(char *mapping)
11731 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11732 SetupFileHash *mappings_hash = newSetupFileHash();
11734 InitUserDataDirectory();
11736 // load existing personal game controller mappings
11737 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11739 // add new mapping to personal game controller mappings
11740 addGameControllerMappingToHash(mappings_hash, mapping);
11742 // save updated personal game controller mappings
11743 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11745 freeSetupFileHash(mappings_hash);
11749 void LoadCustomElementDescriptions(void)
11751 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11752 SetupFileHash *setup_file_hash;
11755 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11757 if (element_info[i].custom_description != NULL)
11759 free(element_info[i].custom_description);
11760 element_info[i].custom_description = NULL;
11764 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11767 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11769 char *token = getStringCat2(element_info[i].token_name, ".name");
11770 char *value = getHashEntry(setup_file_hash, token);
11773 element_info[i].custom_description = getStringCopy(value);
11778 freeSetupFileHash(setup_file_hash);
11781 static int getElementFromToken(char *token)
11783 char *value = getHashEntry(element_token_hash, token);
11786 return atoi(value);
11788 Warn("unknown element token '%s'", token);
11790 return EL_UNDEFINED;
11793 void FreeGlobalAnimEventInfo(void)
11795 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11797 if (gaei->event_list == NULL)
11802 for (i = 0; i < gaei->num_event_lists; i++)
11804 checked_free(gaei->event_list[i]->event_value);
11805 checked_free(gaei->event_list[i]);
11808 checked_free(gaei->event_list);
11810 gaei->event_list = NULL;
11811 gaei->num_event_lists = 0;
11814 static int AddGlobalAnimEventList(void)
11816 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11817 int list_pos = gaei->num_event_lists++;
11819 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11820 sizeof(struct GlobalAnimEventListInfo *));
11822 gaei->event_list[list_pos] =
11823 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11825 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11827 gaeli->event_value = NULL;
11828 gaeli->num_event_values = 0;
11833 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11835 // do not add empty global animation events
11836 if (event_value == ANIM_EVENT_NONE)
11839 // if list position is undefined, create new list
11840 if (list_pos == ANIM_EVENT_UNDEFINED)
11841 list_pos = AddGlobalAnimEventList();
11843 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11844 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11845 int value_pos = gaeli->num_event_values++;
11847 gaeli->event_value = checked_realloc(gaeli->event_value,
11848 gaeli->num_event_values * sizeof(int *));
11850 gaeli->event_value[value_pos] = event_value;
11855 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11857 if (list_pos == ANIM_EVENT_UNDEFINED)
11858 return ANIM_EVENT_NONE;
11860 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11861 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11863 return gaeli->event_value[value_pos];
11866 int GetGlobalAnimEventValueCount(int list_pos)
11868 if (list_pos == ANIM_EVENT_UNDEFINED)
11871 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11872 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11874 return gaeli->num_event_values;
11877 // This function checks if a string <s> of the format "string1, string2, ..."
11878 // exactly contains a string <s_contained>.
11880 static boolean string_has_parameter(char *s, char *s_contained)
11884 if (s == NULL || s_contained == NULL)
11887 if (strlen(s_contained) > strlen(s))
11890 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11892 char next_char = s[strlen(s_contained)];
11894 // check if next character is delimiter or whitespace
11895 if (next_char == ',' || next_char == '\0' ||
11896 next_char == ' ' || next_char == '\t')
11900 // check if string contains another parameter string after a comma
11901 substring = strchr(s, ',');
11902 if (substring == NULL) // string does not contain a comma
11905 // advance string pointer to next character after the comma
11908 // skip potential whitespaces after the comma
11909 while (*substring == ' ' || *substring == '\t')
11912 return string_has_parameter(substring, s_contained);
11915 static int get_anim_parameter_value_ce(char *s)
11918 char *pattern_1 = "ce_change:custom_";
11919 char *pattern_2 = ".page_";
11920 int pattern_1_len = strlen(pattern_1);
11921 char *matching_char = strstr(s_ptr, pattern_1);
11922 int result = ANIM_EVENT_NONE;
11924 if (matching_char == NULL)
11925 return ANIM_EVENT_NONE;
11927 result = ANIM_EVENT_CE_CHANGE;
11929 s_ptr = matching_char + pattern_1_len;
11931 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11932 if (*s_ptr >= '0' && *s_ptr <= '9')
11934 int gic_ce_nr = (*s_ptr++ - '0');
11936 if (*s_ptr >= '0' && *s_ptr <= '9')
11938 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11940 if (*s_ptr >= '0' && *s_ptr <= '9')
11941 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11944 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11945 return ANIM_EVENT_NONE;
11947 // custom element stored as 0 to 255
11950 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11954 // invalid custom element number specified
11956 return ANIM_EVENT_NONE;
11959 // check for change page number ("page_X" or "page_XX") (optional)
11960 if (strPrefix(s_ptr, pattern_2))
11962 s_ptr += strlen(pattern_2);
11964 if (*s_ptr >= '0' && *s_ptr <= '9')
11966 int gic_page_nr = (*s_ptr++ - '0');
11968 if (*s_ptr >= '0' && *s_ptr <= '9')
11969 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11971 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11972 return ANIM_EVENT_NONE;
11974 // change page stored as 1 to 32 (0 means "all change pages")
11976 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11980 // invalid animation part number specified
11982 return ANIM_EVENT_NONE;
11986 // discard result if next character is neither delimiter nor whitespace
11987 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11988 *s_ptr == ' ' || *s_ptr == '\t'))
11989 return ANIM_EVENT_NONE;
11994 static int get_anim_parameter_value(char *s)
11996 int event_value[] =
12004 char *pattern_1[] =
12012 char *pattern_2 = ".part_";
12013 char *matching_char = NULL;
12015 int pattern_1_len = 0;
12016 int result = ANIM_EVENT_NONE;
12019 result = get_anim_parameter_value_ce(s);
12021 if (result != ANIM_EVENT_NONE)
12024 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12026 matching_char = strstr(s_ptr, pattern_1[i]);
12027 pattern_1_len = strlen(pattern_1[i]);
12028 result = event_value[i];
12030 if (matching_char != NULL)
12034 if (matching_char == NULL)
12035 return ANIM_EVENT_NONE;
12037 s_ptr = matching_char + pattern_1_len;
12039 // check for main animation number ("anim_X" or "anim_XX")
12040 if (*s_ptr >= '0' && *s_ptr <= '9')
12042 int gic_anim_nr = (*s_ptr++ - '0');
12044 if (*s_ptr >= '0' && *s_ptr <= '9')
12045 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12047 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12048 return ANIM_EVENT_NONE;
12050 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12054 // invalid main animation number specified
12056 return ANIM_EVENT_NONE;
12059 // check for animation part number ("part_X" or "part_XX") (optional)
12060 if (strPrefix(s_ptr, pattern_2))
12062 s_ptr += strlen(pattern_2);
12064 if (*s_ptr >= '0' && *s_ptr <= '9')
12066 int gic_part_nr = (*s_ptr++ - '0');
12068 if (*s_ptr >= '0' && *s_ptr <= '9')
12069 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12071 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12072 return ANIM_EVENT_NONE;
12074 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12078 // invalid animation part number specified
12080 return ANIM_EVENT_NONE;
12084 // discard result if next character is neither delimiter nor whitespace
12085 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12086 *s_ptr == ' ' || *s_ptr == '\t'))
12087 return ANIM_EVENT_NONE;
12092 static int get_anim_parameter_values(char *s)
12094 int list_pos = ANIM_EVENT_UNDEFINED;
12095 int event_value = ANIM_EVENT_DEFAULT;
12097 if (string_has_parameter(s, "any"))
12098 event_value |= ANIM_EVENT_ANY;
12100 if (string_has_parameter(s, "click:self") ||
12101 string_has_parameter(s, "click") ||
12102 string_has_parameter(s, "self"))
12103 event_value |= ANIM_EVENT_SELF;
12105 if (string_has_parameter(s, "unclick:any"))
12106 event_value |= ANIM_EVENT_UNCLICK_ANY;
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);
12114 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12115 event_value = get_anim_parameter_value(s);
12117 // if animation event found, add it to global animation event list
12118 if (event_value != ANIM_EVENT_NONE)
12119 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12121 // continue with next part of the string, starting with next comma
12122 s = strchr(s + 1, ',');
12128 static int get_anim_action_parameter_value(char *token)
12130 // check most common default case first to massively speed things up
12131 if (strEqual(token, ARG_UNDEFINED))
12132 return ANIM_EVENT_ACTION_NONE;
12134 int result = getImageIDFromToken(token);
12138 char *gfx_token = getStringCat2("gfx.", token);
12140 result = getImageIDFromToken(gfx_token);
12142 checked_free(gfx_token);
12147 Key key = getKeyFromX11KeyName(token);
12149 if (key != KSYM_UNDEFINED)
12150 result = -(int)key;
12157 result = get_hash_from_string(token); // unsigned int => int
12158 result = ABS(result); // may be negative now
12159 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12161 setHashEntry(anim_url_hash, int2str(result, 0), token);
12166 result = ANIM_EVENT_ACTION_NONE;
12171 int get_parameter_value(char *value_raw, char *suffix, int type)
12173 char *value = getStringToLower(value_raw);
12174 int result = 0; // probably a save default value
12176 if (strEqual(suffix, ".direction"))
12178 result = (strEqual(value, "left") ? MV_LEFT :
12179 strEqual(value, "right") ? MV_RIGHT :
12180 strEqual(value, "up") ? MV_UP :
12181 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12183 else if (strEqual(suffix, ".position"))
12185 result = (strEqual(value, "left") ? POS_LEFT :
12186 strEqual(value, "right") ? POS_RIGHT :
12187 strEqual(value, "top") ? POS_TOP :
12188 strEqual(value, "upper") ? POS_UPPER :
12189 strEqual(value, "middle") ? POS_MIDDLE :
12190 strEqual(value, "lower") ? POS_LOWER :
12191 strEqual(value, "bottom") ? POS_BOTTOM :
12192 strEqual(value, "any") ? POS_ANY :
12193 strEqual(value, "ce") ? POS_CE :
12194 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12195 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12197 else if (strEqual(suffix, ".align"))
12199 result = (strEqual(value, "left") ? ALIGN_LEFT :
12200 strEqual(value, "right") ? ALIGN_RIGHT :
12201 strEqual(value, "center") ? ALIGN_CENTER :
12202 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12204 else if (strEqual(suffix, ".valign"))
12206 result = (strEqual(value, "top") ? VALIGN_TOP :
12207 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12208 strEqual(value, "middle") ? VALIGN_MIDDLE :
12209 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12211 else if (strEqual(suffix, ".anim_mode"))
12213 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12214 string_has_parameter(value, "loop") ? ANIM_LOOP :
12215 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12216 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12217 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12218 string_has_parameter(value, "random") ? ANIM_RANDOM :
12219 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12220 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12221 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12222 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12223 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12224 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12225 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12226 string_has_parameter(value, "all") ? ANIM_ALL :
12227 string_has_parameter(value, "tiled") ? ANIM_TILED :
12228 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12231 if (string_has_parameter(value, "once"))
12232 result |= ANIM_ONCE;
12234 if (string_has_parameter(value, "reverse"))
12235 result |= ANIM_REVERSE;
12237 if (string_has_parameter(value, "opaque_player"))
12238 result |= ANIM_OPAQUE_PLAYER;
12240 if (string_has_parameter(value, "static_panel"))
12241 result |= ANIM_STATIC_PANEL;
12243 else if (strEqual(suffix, ".init_event") ||
12244 strEqual(suffix, ".anim_event"))
12246 result = get_anim_parameter_values(value);
12248 else if (strEqual(suffix, ".init_delay_action") ||
12249 strEqual(suffix, ".anim_delay_action") ||
12250 strEqual(suffix, ".post_delay_action") ||
12251 strEqual(suffix, ".init_event_action") ||
12252 strEqual(suffix, ".anim_event_action"))
12254 result = get_anim_action_parameter_value(value_raw);
12256 else if (strEqual(suffix, ".class"))
12258 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12259 get_hash_from_string(value));
12261 else if (strEqual(suffix, ".style"))
12263 result = STYLE_DEFAULT;
12265 if (string_has_parameter(value, "accurate_borders"))
12266 result |= STYLE_ACCURATE_BORDERS;
12268 if (string_has_parameter(value, "inner_corners"))
12269 result |= STYLE_INNER_CORNERS;
12271 if (string_has_parameter(value, "reverse"))
12272 result |= STYLE_REVERSE;
12274 if (string_has_parameter(value, "leftmost_position"))
12275 result |= STYLE_LEFTMOST_POSITION;
12277 if (string_has_parameter(value, "block_clicks"))
12278 result |= STYLE_BLOCK;
12280 if (string_has_parameter(value, "passthrough_clicks"))
12281 result |= STYLE_PASSTHROUGH;
12283 if (string_has_parameter(value, "multiple_actions"))
12284 result |= STYLE_MULTIPLE_ACTIONS;
12286 if (string_has_parameter(value, "consume_ce_event"))
12287 result |= STYLE_CONSUME_CE_EVENT;
12289 else if (strEqual(suffix, ".fade_mode"))
12291 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12292 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12293 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12294 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12295 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12296 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12297 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12298 FADE_MODE_DEFAULT);
12300 else if (strEqual(suffix, ".auto_delay_unit"))
12302 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12303 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12304 AUTO_DELAY_UNIT_DEFAULT);
12306 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12308 result = gfx.get_font_from_token_function(value);
12310 else // generic parameter of type integer or boolean
12312 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12313 type == TYPE_INTEGER ? get_integer_from_string(value) :
12314 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12315 ARG_UNDEFINED_VALUE);
12323 static int get_token_parameter_value(char *token, char *value_raw)
12327 if (token == NULL || value_raw == NULL)
12328 return ARG_UNDEFINED_VALUE;
12330 suffix = strrchr(token, '.');
12331 if (suffix == NULL)
12334 if (strEqual(suffix, ".element"))
12335 return getElementFromToken(value_raw);
12337 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12338 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12341 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12342 boolean ignore_defaults)
12346 for (i = 0; image_config_vars[i].token != NULL; i++)
12348 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12350 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12351 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12355 *image_config_vars[i].value =
12356 get_token_parameter_value(image_config_vars[i].token, value);
12360 void InitMenuDesignSettings_Static(void)
12362 // always start with reliable default values from static default config
12363 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12366 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12370 // the following initializes hierarchical values from static configuration
12372 // special case: initialize "ARG_DEFAULT" values in static default config
12373 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12374 titlescreen_initial_first_default.fade_mode =
12375 title_initial_first_default.fade_mode;
12376 titlescreen_initial_first_default.fade_delay =
12377 title_initial_first_default.fade_delay;
12378 titlescreen_initial_first_default.post_delay =
12379 title_initial_first_default.post_delay;
12380 titlescreen_initial_first_default.auto_delay =
12381 title_initial_first_default.auto_delay;
12382 titlescreen_initial_first_default.auto_delay_unit =
12383 title_initial_first_default.auto_delay_unit;
12384 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12385 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12386 titlescreen_first_default.post_delay = title_first_default.post_delay;
12387 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12388 titlescreen_first_default.auto_delay_unit =
12389 title_first_default.auto_delay_unit;
12390 titlemessage_initial_first_default.fade_mode =
12391 title_initial_first_default.fade_mode;
12392 titlemessage_initial_first_default.fade_delay =
12393 title_initial_first_default.fade_delay;
12394 titlemessage_initial_first_default.post_delay =
12395 title_initial_first_default.post_delay;
12396 titlemessage_initial_first_default.auto_delay =
12397 title_initial_first_default.auto_delay;
12398 titlemessage_initial_first_default.auto_delay_unit =
12399 title_initial_first_default.auto_delay_unit;
12400 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12401 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12402 titlemessage_first_default.post_delay = title_first_default.post_delay;
12403 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12404 titlemessage_first_default.auto_delay_unit =
12405 title_first_default.auto_delay_unit;
12407 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12408 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12409 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12410 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12411 titlescreen_initial_default.auto_delay_unit =
12412 title_initial_default.auto_delay_unit;
12413 titlescreen_default.fade_mode = title_default.fade_mode;
12414 titlescreen_default.fade_delay = title_default.fade_delay;
12415 titlescreen_default.post_delay = title_default.post_delay;
12416 titlescreen_default.auto_delay = title_default.auto_delay;
12417 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12418 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12419 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12420 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12421 titlemessage_initial_default.auto_delay_unit =
12422 title_initial_default.auto_delay_unit;
12423 titlemessage_default.fade_mode = title_default.fade_mode;
12424 titlemessage_default.fade_delay = title_default.fade_delay;
12425 titlemessage_default.post_delay = title_default.post_delay;
12426 titlemessage_default.auto_delay = title_default.auto_delay;
12427 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12429 // special case: initialize "ARG_DEFAULT" values in static default config
12430 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12431 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12433 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12434 titlescreen_first[i] = titlescreen_first_default;
12435 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12436 titlemessage_first[i] = titlemessage_first_default;
12438 titlescreen_initial[i] = titlescreen_initial_default;
12439 titlescreen[i] = titlescreen_default;
12440 titlemessage_initial[i] = titlemessage_initial_default;
12441 titlemessage[i] = titlemessage_default;
12444 // special case: initialize "ARG_DEFAULT" values in static default config
12445 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12446 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12448 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12451 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12452 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12453 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12456 // special case: initialize "ARG_DEFAULT" values in static default config
12457 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12458 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12460 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12461 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12462 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12464 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12467 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12471 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12475 struct XY *dst, *src;
12477 game_buttons_xy[] =
12479 { &game.button.save, &game.button.stop },
12480 { &game.button.pause2, &game.button.pause },
12481 { &game.button.load, &game.button.play },
12482 { &game.button.undo, &game.button.stop },
12483 { &game.button.redo, &game.button.play },
12489 // special case: initialize later added SETUP list size from LEVELS value
12490 if (menu.list_size[GAME_MODE_SETUP] == -1)
12491 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12493 // set default position for snapshot buttons to stop/pause/play buttons
12494 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12495 if ((*game_buttons_xy[i].dst).x == -1 &&
12496 (*game_buttons_xy[i].dst).y == -1)
12497 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12499 // --------------------------------------------------------------------------
12500 // dynamic viewports (including playfield margins, borders and alignments)
12501 // --------------------------------------------------------------------------
12503 // dynamic viewports currently only supported for landscape mode
12504 int display_width = MAX(video.display_width, video.display_height);
12505 int display_height = MIN(video.display_width, video.display_height);
12507 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12509 struct RectWithBorder *vp_window = &viewport.window[i];
12510 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12511 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12512 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12513 boolean dynamic_window_width = (vp_window->min_width != -1);
12514 boolean dynamic_window_height = (vp_window->min_height != -1);
12515 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12516 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12518 // adjust window size if min/max width/height is specified
12520 if (vp_window->min_width != -1)
12522 int window_width = display_width;
12524 // when using static window height, use aspect ratio of display
12525 if (vp_window->min_height == -1)
12526 window_width = vp_window->height * display_width / display_height;
12528 vp_window->width = MAX(vp_window->min_width, window_width);
12531 if (vp_window->min_height != -1)
12533 int window_height = display_height;
12535 // when using static window width, use aspect ratio of display
12536 if (vp_window->min_width == -1)
12537 window_height = vp_window->width * display_height / display_width;
12539 vp_window->height = MAX(vp_window->min_height, window_height);
12542 if (vp_window->max_width != -1)
12543 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12545 if (vp_window->max_height != -1)
12546 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12548 int playfield_width = vp_window->width;
12549 int playfield_height = vp_window->height;
12551 // adjust playfield size and position according to specified margins
12553 playfield_width -= vp_playfield->margin_left;
12554 playfield_width -= vp_playfield->margin_right;
12556 playfield_height -= vp_playfield->margin_top;
12557 playfield_height -= vp_playfield->margin_bottom;
12559 // adjust playfield size if min/max width/height is specified
12561 if (vp_playfield->min_width != -1)
12562 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12564 if (vp_playfield->min_height != -1)
12565 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12567 if (vp_playfield->max_width != -1)
12568 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12570 if (vp_playfield->max_height != -1)
12571 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12573 // adjust playfield position according to specified alignment
12575 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12576 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12577 else if (vp_playfield->align == ALIGN_CENTER)
12578 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12579 else if (vp_playfield->align == ALIGN_RIGHT)
12580 vp_playfield->x += playfield_width - vp_playfield->width;
12582 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12583 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12584 else if (vp_playfield->valign == VALIGN_MIDDLE)
12585 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12586 else if (vp_playfield->valign == VALIGN_BOTTOM)
12587 vp_playfield->y += playfield_height - vp_playfield->height;
12589 vp_playfield->x += vp_playfield->margin_left;
12590 vp_playfield->y += vp_playfield->margin_top;
12592 // adjust individual playfield borders if only default border is specified
12594 if (vp_playfield->border_left == -1)
12595 vp_playfield->border_left = vp_playfield->border_size;
12596 if (vp_playfield->border_right == -1)
12597 vp_playfield->border_right = vp_playfield->border_size;
12598 if (vp_playfield->border_top == -1)
12599 vp_playfield->border_top = vp_playfield->border_size;
12600 if (vp_playfield->border_bottom == -1)
12601 vp_playfield->border_bottom = vp_playfield->border_size;
12603 // set dynamic playfield borders if borders are specified as undefined
12604 // (but only if window size was dynamic and playfield size was static)
12606 if (dynamic_window_width && !dynamic_playfield_width)
12608 if (vp_playfield->border_left == -1)
12610 vp_playfield->border_left = (vp_playfield->x -
12611 vp_playfield->margin_left);
12612 vp_playfield->x -= vp_playfield->border_left;
12613 vp_playfield->width += vp_playfield->border_left;
12616 if (vp_playfield->border_right == -1)
12618 vp_playfield->border_right = (vp_window->width -
12620 vp_playfield->width -
12621 vp_playfield->margin_right);
12622 vp_playfield->width += vp_playfield->border_right;
12626 if (dynamic_window_height && !dynamic_playfield_height)
12628 if (vp_playfield->border_top == -1)
12630 vp_playfield->border_top = (vp_playfield->y -
12631 vp_playfield->margin_top);
12632 vp_playfield->y -= vp_playfield->border_top;
12633 vp_playfield->height += vp_playfield->border_top;
12636 if (vp_playfield->border_bottom == -1)
12638 vp_playfield->border_bottom = (vp_window->height -
12640 vp_playfield->height -
12641 vp_playfield->margin_bottom);
12642 vp_playfield->height += vp_playfield->border_bottom;
12646 // adjust playfield size to be a multiple of a defined alignment tile size
12648 int align_size = vp_playfield->align_size;
12649 int playfield_xtiles = vp_playfield->width / align_size;
12650 int playfield_ytiles = vp_playfield->height / align_size;
12651 int playfield_width_corrected = playfield_xtiles * align_size;
12652 int playfield_height_corrected = playfield_ytiles * align_size;
12653 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12654 i == GFX_SPECIAL_ARG_EDITOR);
12656 if (is_playfield_mode &&
12657 dynamic_playfield_width &&
12658 vp_playfield->width != playfield_width_corrected)
12660 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12662 vp_playfield->width = playfield_width_corrected;
12664 if (vp_playfield->align == ALIGN_LEFT)
12666 vp_playfield->border_left += playfield_xdiff;
12668 else if (vp_playfield->align == ALIGN_RIGHT)
12670 vp_playfield->border_right += playfield_xdiff;
12672 else if (vp_playfield->align == ALIGN_CENTER)
12674 int border_left_diff = playfield_xdiff / 2;
12675 int border_right_diff = playfield_xdiff - border_left_diff;
12677 vp_playfield->border_left += border_left_diff;
12678 vp_playfield->border_right += border_right_diff;
12682 if (is_playfield_mode &&
12683 dynamic_playfield_height &&
12684 vp_playfield->height != playfield_height_corrected)
12686 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12688 vp_playfield->height = playfield_height_corrected;
12690 if (vp_playfield->valign == VALIGN_TOP)
12692 vp_playfield->border_top += playfield_ydiff;
12694 else if (vp_playfield->align == VALIGN_BOTTOM)
12696 vp_playfield->border_right += playfield_ydiff;
12698 else if (vp_playfield->align == VALIGN_MIDDLE)
12700 int border_top_diff = playfield_ydiff / 2;
12701 int border_bottom_diff = playfield_ydiff - border_top_diff;
12703 vp_playfield->border_top += border_top_diff;
12704 vp_playfield->border_bottom += border_bottom_diff;
12708 // adjust door positions according to specified alignment
12710 for (j = 0; j < 2; j++)
12712 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12714 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12715 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12716 else if (vp_door->align == ALIGN_CENTER)
12717 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12718 else if (vp_door->align == ALIGN_RIGHT)
12719 vp_door->x += vp_window->width - vp_door->width;
12721 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12722 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12723 else if (vp_door->valign == VALIGN_MIDDLE)
12724 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12725 else if (vp_door->valign == VALIGN_BOTTOM)
12726 vp_door->y += vp_window->height - vp_door->height;
12731 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12735 struct XYTileSize *dst, *src;
12738 editor_buttons_xy[] =
12741 &editor.button.element_left, &editor.palette.element_left,
12742 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12745 &editor.button.element_middle, &editor.palette.element_middle,
12746 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12749 &editor.button.element_right, &editor.palette.element_right,
12750 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12757 // set default position for element buttons to element graphics
12758 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12760 if ((*editor_buttons_xy[i].dst).x == -1 &&
12761 (*editor_buttons_xy[i].dst).y == -1)
12763 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12765 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12767 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12771 // adjust editor palette rows and columns if specified to be dynamic
12773 if (editor.palette.cols == -1)
12775 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12776 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12777 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12779 editor.palette.cols = (vp_width - sc_width) / bt_width;
12781 if (editor.palette.x == -1)
12783 int palette_width = editor.palette.cols * bt_width + sc_width;
12785 editor.palette.x = (vp_width - palette_width) / 2;
12789 if (editor.palette.rows == -1)
12791 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12792 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12793 int tx_height = getFontHeight(FONT_TEXT_2);
12795 editor.palette.rows = (vp_height - tx_height) / bt_height;
12797 if (editor.palette.y == -1)
12799 int palette_height = editor.palette.rows * bt_height + tx_height;
12801 editor.palette.y = (vp_height - palette_height) / 2;
12806 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12807 boolean initialize)
12809 // special case: check if network and preview player positions are redefined,
12810 // to compare this later against the main menu level preview being redefined
12811 struct TokenIntPtrInfo menu_config_players[] =
12813 { "main.network_players.x", &menu.main.network_players.redefined },
12814 { "main.network_players.y", &menu.main.network_players.redefined },
12815 { "main.preview_players.x", &menu.main.preview_players.redefined },
12816 { "main.preview_players.y", &menu.main.preview_players.redefined },
12817 { "preview.x", &preview.redefined },
12818 { "preview.y", &preview.redefined }
12824 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12825 *menu_config_players[i].value = FALSE;
12829 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12830 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12831 *menu_config_players[i].value = TRUE;
12835 static void InitMenuDesignSettings_PreviewPlayers(void)
12837 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12840 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12842 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12845 static void LoadMenuDesignSettingsFromFilename(char *filename)
12847 static struct TitleFadingInfo tfi;
12848 static struct TitleMessageInfo tmi;
12849 static struct TokenInfo title_tokens[] =
12851 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12852 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12853 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12854 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12855 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12859 static struct TokenInfo titlemessage_tokens[] =
12861 { TYPE_INTEGER, &tmi.x, ".x" },
12862 { TYPE_INTEGER, &tmi.y, ".y" },
12863 { TYPE_INTEGER, &tmi.width, ".width" },
12864 { TYPE_INTEGER, &tmi.height, ".height" },
12865 { TYPE_INTEGER, &tmi.chars, ".chars" },
12866 { TYPE_INTEGER, &tmi.lines, ".lines" },
12867 { TYPE_INTEGER, &tmi.align, ".align" },
12868 { TYPE_INTEGER, &tmi.valign, ".valign" },
12869 { TYPE_INTEGER, &tmi.font, ".font" },
12870 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12871 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12872 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12873 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12874 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12875 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12876 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12877 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12878 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12884 struct TitleFadingInfo *info;
12889 // initialize first titles from "enter screen" definitions, if defined
12890 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12891 { &title_first_default, "menu.enter_screen.TITLE" },
12893 // initialize title screens from "next screen" definitions, if defined
12894 { &title_initial_default, "menu.next_screen.TITLE" },
12895 { &title_default, "menu.next_screen.TITLE" },
12901 struct TitleMessageInfo *array;
12904 titlemessage_arrays[] =
12906 // initialize first titles from "enter screen" definitions, if defined
12907 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12908 { titlescreen_first, "menu.enter_screen.TITLE" },
12909 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12910 { titlemessage_first, "menu.enter_screen.TITLE" },
12912 // initialize titles from "next screen" definitions, if defined
12913 { titlescreen_initial, "menu.next_screen.TITLE" },
12914 { titlescreen, "menu.next_screen.TITLE" },
12915 { titlemessage_initial, "menu.next_screen.TITLE" },
12916 { titlemessage, "menu.next_screen.TITLE" },
12918 // overwrite titles with title definitions, if defined
12919 { titlescreen_initial_first, "[title_initial]" },
12920 { titlescreen_first, "[title]" },
12921 { titlemessage_initial_first, "[title_initial]" },
12922 { titlemessage_first, "[title]" },
12924 { titlescreen_initial, "[title_initial]" },
12925 { titlescreen, "[title]" },
12926 { titlemessage_initial, "[title_initial]" },
12927 { titlemessage, "[title]" },
12929 // overwrite titles with title screen/message definitions, if defined
12930 { titlescreen_initial_first, "[titlescreen_initial]" },
12931 { titlescreen_first, "[titlescreen]" },
12932 { titlemessage_initial_first, "[titlemessage_initial]" },
12933 { titlemessage_first, "[titlemessage]" },
12935 { titlescreen_initial, "[titlescreen_initial]" },
12936 { titlescreen, "[titlescreen]" },
12937 { titlemessage_initial, "[titlemessage_initial]" },
12938 { titlemessage, "[titlemessage]" },
12942 SetupFileHash *setup_file_hash;
12945 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12948 // the following initializes hierarchical values from dynamic configuration
12950 // special case: initialize with default values that may be overwritten
12951 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12952 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12954 struct TokenIntPtrInfo menu_config[] =
12956 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12957 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12958 { "menu.list_size", &menu.list_size[i] }
12961 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12963 char *token = menu_config[j].token;
12964 char *value = getHashEntry(setup_file_hash, token);
12967 *menu_config[j].value = get_integer_from_string(value);
12971 // special case: initialize with default values that may be overwritten
12972 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12973 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12975 struct TokenIntPtrInfo menu_config[] =
12977 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12978 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12979 { "menu.list_size.INFO", &menu.list_size_info[i] },
12980 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12981 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12984 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12986 char *token = menu_config[j].token;
12987 char *value = getHashEntry(setup_file_hash, token);
12990 *menu_config[j].value = get_integer_from_string(value);
12994 // special case: initialize with default values that may be overwritten
12995 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12996 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12998 struct TokenIntPtrInfo menu_config[] =
13000 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13001 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13004 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13006 char *token = menu_config[j].token;
13007 char *value = getHashEntry(setup_file_hash, token);
13010 *menu_config[j].value = get_integer_from_string(value);
13014 // special case: initialize with default values that may be overwritten
13015 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13016 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13018 struct TokenIntPtrInfo menu_config[] =
13020 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13021 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13022 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13023 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13024 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13025 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13026 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13027 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13028 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13029 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13032 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13034 char *token = menu_config[j].token;
13035 char *value = getHashEntry(setup_file_hash, token);
13038 *menu_config[j].value = get_integer_from_string(value);
13042 // special case: initialize with default values that may be overwritten
13043 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13044 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13046 struct TokenIntPtrInfo menu_config[] =
13048 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13049 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13050 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13051 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13052 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13053 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13054 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13055 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13056 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13059 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13061 char *token = menu_config[j].token;
13062 char *value = getHashEntry(setup_file_hash, token);
13065 *menu_config[j].value = get_token_parameter_value(token, value);
13069 // special case: initialize with default values that may be overwritten
13070 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13071 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13075 char *token_prefix;
13076 struct RectWithBorder *struct_ptr;
13080 { "viewport.window", &viewport.window[i] },
13081 { "viewport.playfield", &viewport.playfield[i] },
13082 { "viewport.door_1", &viewport.door_1[i] },
13083 { "viewport.door_2", &viewport.door_2[i] }
13086 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13088 struct TokenIntPtrInfo vp_config[] =
13090 { ".x", &vp_struct[j].struct_ptr->x },
13091 { ".y", &vp_struct[j].struct_ptr->y },
13092 { ".width", &vp_struct[j].struct_ptr->width },
13093 { ".height", &vp_struct[j].struct_ptr->height },
13094 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13095 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13096 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13097 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13098 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13099 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13100 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13101 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13102 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13103 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13104 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13105 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13106 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13107 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13108 { ".align", &vp_struct[j].struct_ptr->align },
13109 { ".valign", &vp_struct[j].struct_ptr->valign }
13112 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13114 char *token = getStringCat2(vp_struct[j].token_prefix,
13115 vp_config[k].token);
13116 char *value = getHashEntry(setup_file_hash, token);
13119 *vp_config[k].value = get_token_parameter_value(token, value);
13126 // special case: initialize with default values that may be overwritten
13127 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13128 for (i = 0; title_info[i].info != NULL; i++)
13130 struct TitleFadingInfo *info = title_info[i].info;
13131 char *base_token = title_info[i].text;
13133 for (j = 0; title_tokens[j].type != -1; j++)
13135 char *token = getStringCat2(base_token, title_tokens[j].text);
13136 char *value = getHashEntry(setup_file_hash, token);
13140 int parameter_value = get_token_parameter_value(token, value);
13144 *(int *)title_tokens[j].value = (int)parameter_value;
13153 // special case: initialize with default values that may be overwritten
13154 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13155 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13157 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13158 char *base_token = titlemessage_arrays[i].text;
13160 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13162 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13163 char *value = getHashEntry(setup_file_hash, token);
13167 int parameter_value = get_token_parameter_value(token, value);
13169 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13173 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13174 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13176 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13186 // read (and overwrite with) values that may be specified in config file
13187 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13189 // special case: check if network and preview player positions are redefined
13190 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13192 freeSetupFileHash(setup_file_hash);
13195 void LoadMenuDesignSettings(void)
13197 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13199 InitMenuDesignSettings_Static();
13200 InitMenuDesignSettings_SpecialPreProcessing();
13201 InitMenuDesignSettings_PreviewPlayers();
13203 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13205 // first look for special settings configured in level series config
13206 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13208 if (fileExists(filename_base))
13209 LoadMenuDesignSettingsFromFilename(filename_base);
13212 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13214 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13215 LoadMenuDesignSettingsFromFilename(filename_local);
13217 InitMenuDesignSettings_SpecialPostProcessing();
13220 void LoadMenuDesignSettings_AfterGraphics(void)
13222 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13225 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13226 boolean ignore_defaults)
13230 for (i = 0; sound_config_vars[i].token != NULL; i++)
13232 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13234 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13235 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13239 *sound_config_vars[i].value =
13240 get_token_parameter_value(sound_config_vars[i].token, value);
13244 void InitSoundSettings_Static(void)
13246 // always start with reliable default values from static default config
13247 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13250 static void LoadSoundSettingsFromFilename(char *filename)
13252 SetupFileHash *setup_file_hash;
13254 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13257 // read (and overwrite with) values that may be specified in config file
13258 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13260 freeSetupFileHash(setup_file_hash);
13263 void LoadSoundSettings(void)
13265 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13267 InitSoundSettings_Static();
13269 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13271 // first look for special settings configured in level series config
13272 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13274 if (fileExists(filename_base))
13275 LoadSoundSettingsFromFilename(filename_base);
13278 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13280 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13281 LoadSoundSettingsFromFilename(filename_local);
13284 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13286 char *filename = getEditorSetupFilename();
13287 SetupFileList *setup_file_list, *list;
13288 SetupFileHash *element_hash;
13289 int num_unknown_tokens = 0;
13292 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13295 element_hash = newSetupFileHash();
13297 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13298 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13300 // determined size may be larger than needed (due to unknown elements)
13302 for (list = setup_file_list; list != NULL; list = list->next)
13305 // add space for up to 3 more elements for padding that may be needed
13306 *num_elements += 3;
13308 // free memory for old list of elements, if needed
13309 checked_free(*elements);
13311 // allocate memory for new list of elements
13312 *elements = checked_malloc(*num_elements * sizeof(int));
13315 for (list = setup_file_list; list != NULL; list = list->next)
13317 char *value = getHashEntry(element_hash, list->token);
13319 if (value == NULL) // try to find obsolete token mapping
13321 char *mapped_token = get_mapped_token(list->token);
13323 if (mapped_token != NULL)
13325 value = getHashEntry(element_hash, mapped_token);
13327 free(mapped_token);
13333 (*elements)[(*num_elements)++] = atoi(value);
13337 if (num_unknown_tokens == 0)
13340 Warn("unknown token(s) found in config file:");
13341 Warn("- config file: '%s'", filename);
13343 num_unknown_tokens++;
13346 Warn("- token: '%s'", list->token);
13350 if (num_unknown_tokens > 0)
13353 while (*num_elements % 4) // pad with empty elements, if needed
13354 (*elements)[(*num_elements)++] = EL_EMPTY;
13356 freeSetupFileList(setup_file_list);
13357 freeSetupFileHash(element_hash);
13360 for (i = 0; i < *num_elements; i++)
13361 Debug("editor", "element '%s' [%d]\n",
13362 element_info[(*elements)[i]].token_name, (*elements)[i]);
13366 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13369 SetupFileHash *setup_file_hash = NULL;
13370 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13371 char *filename_music, *filename_prefix, *filename_info;
13377 token_to_value_ptr[] =
13379 { "title_header", &tmp_music_file_info.title_header },
13380 { "artist_header", &tmp_music_file_info.artist_header },
13381 { "album_header", &tmp_music_file_info.album_header },
13382 { "year_header", &tmp_music_file_info.year_header },
13383 { "played_header", &tmp_music_file_info.played_header },
13385 { "title", &tmp_music_file_info.title },
13386 { "artist", &tmp_music_file_info.artist },
13387 { "album", &tmp_music_file_info.album },
13388 { "year", &tmp_music_file_info.year },
13389 { "played", &tmp_music_file_info.played },
13395 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13396 getCustomMusicFilename(basename));
13398 if (filename_music == NULL)
13401 // ---------- try to replace file extension ----------
13403 filename_prefix = getStringCopy(filename_music);
13404 if (strrchr(filename_prefix, '.') != NULL)
13405 *strrchr(filename_prefix, '.') = '\0';
13406 filename_info = getStringCat2(filename_prefix, ".txt");
13408 if (fileExists(filename_info))
13409 setup_file_hash = loadSetupFileHash(filename_info);
13411 free(filename_prefix);
13412 free(filename_info);
13414 if (setup_file_hash == NULL)
13416 // ---------- try to add file extension ----------
13418 filename_prefix = getStringCopy(filename_music);
13419 filename_info = getStringCat2(filename_prefix, ".txt");
13421 if (fileExists(filename_info))
13422 setup_file_hash = loadSetupFileHash(filename_info);
13424 free(filename_prefix);
13425 free(filename_info);
13428 if (setup_file_hash == NULL)
13431 // ---------- music file info found ----------
13433 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13435 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13437 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13439 *token_to_value_ptr[i].value_ptr =
13440 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13443 tmp_music_file_info.basename = getStringCopy(basename);
13444 tmp_music_file_info.music = music;
13445 tmp_music_file_info.is_sound = is_sound;
13447 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13448 *new_music_file_info = tmp_music_file_info;
13450 return new_music_file_info;
13453 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13455 return get_music_file_info_ext(basename, music, FALSE);
13458 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13460 return get_music_file_info_ext(basename, sound, TRUE);
13463 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13464 char *basename, boolean is_sound)
13466 for (; list != NULL; list = list->next)
13467 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13473 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13475 return music_info_listed_ext(list, basename, FALSE);
13478 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13480 return music_info_listed_ext(list, basename, TRUE);
13483 void LoadMusicInfo(void)
13485 int num_music_noconf = getMusicListSize_NoConf();
13486 int num_music = getMusicListSize();
13487 int num_sounds = getSoundListSize();
13488 struct FileInfo *music, *sound;
13489 struct MusicFileInfo *next, **new;
13493 while (music_file_info != NULL)
13495 next = music_file_info->next;
13497 checked_free(music_file_info->basename);
13499 checked_free(music_file_info->title_header);
13500 checked_free(music_file_info->artist_header);
13501 checked_free(music_file_info->album_header);
13502 checked_free(music_file_info->year_header);
13503 checked_free(music_file_info->played_header);
13505 checked_free(music_file_info->title);
13506 checked_free(music_file_info->artist);
13507 checked_free(music_file_info->album);
13508 checked_free(music_file_info->year);
13509 checked_free(music_file_info->played);
13511 free(music_file_info);
13513 music_file_info = next;
13516 new = &music_file_info;
13518 // get (configured or unconfigured) music file info for all levels
13519 for (i = leveldir_current->first_level;
13520 i <= leveldir_current->last_level; i++)
13524 if (levelset.music[i] != MUS_UNDEFINED)
13526 // get music file info for configured level music
13527 music_nr = levelset.music[i];
13529 else if (num_music_noconf > 0)
13531 // get music file info for unconfigured level music
13532 int level_pos = i - leveldir_current->first_level;
13534 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13541 char *basename = getMusicInfoEntryFilename(music_nr);
13543 if (basename == NULL)
13546 if (!music_info_listed(music_file_info, basename))
13548 *new = get_music_file_info(basename, music_nr);
13551 new = &(*new)->next;
13555 // get music file info for all remaining configured music files
13556 for (i = 0; i < num_music; i++)
13558 music = getMusicListEntry(i);
13560 if (music->filename == NULL)
13563 if (strEqual(music->filename, UNDEFINED_FILENAME))
13566 // a configured file may be not recognized as music
13567 if (!FileIsMusic(music->filename))
13570 if (!music_info_listed(music_file_info, music->filename))
13572 *new = get_music_file_info(music->filename, i);
13575 new = &(*new)->next;
13579 // get sound file info for all configured sound files
13580 for (i = 0; i < num_sounds; i++)
13582 sound = getSoundListEntry(i);
13584 if (sound->filename == NULL)
13587 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13590 // a configured file may be not recognized as sound
13591 if (!FileIsSound(sound->filename))
13594 if (!sound_info_listed(music_file_info, sound->filename))
13596 *new = get_sound_file_info(sound->filename, i);
13598 new = &(*new)->next;
13602 // add pointers to previous list nodes
13604 struct MusicFileInfo *node = music_file_info;
13606 while (node != NULL)
13609 node->next->prev = node;
13615 static void add_helpanim_entry(int element, int action, int direction,
13616 int delay, int *num_list_entries)
13618 struct HelpAnimInfo *new_list_entry;
13619 (*num_list_entries)++;
13622 checked_realloc(helpanim_info,
13623 *num_list_entries * sizeof(struct HelpAnimInfo));
13624 new_list_entry = &helpanim_info[*num_list_entries - 1];
13626 new_list_entry->element = element;
13627 new_list_entry->action = action;
13628 new_list_entry->direction = direction;
13629 new_list_entry->delay = delay;
13632 static void print_unknown_token(char *filename, char *token, int token_nr)
13637 Warn("unknown token(s) found in config file:");
13638 Warn("- config file: '%s'", filename);
13641 Warn("- token: '%s'", token);
13644 static void print_unknown_token_end(int token_nr)
13650 void LoadHelpAnimInfo(void)
13652 char *filename = getHelpAnimFilename();
13653 SetupFileList *setup_file_list = NULL, *list;
13654 SetupFileHash *element_hash, *action_hash, *direction_hash;
13655 int num_list_entries = 0;
13656 int num_unknown_tokens = 0;
13659 if (fileExists(filename))
13660 setup_file_list = loadSetupFileList(filename);
13662 if (setup_file_list == NULL)
13664 // use reliable default values from static configuration
13665 SetupFileList *insert_ptr;
13667 insert_ptr = setup_file_list =
13668 newSetupFileList(helpanim_config[0].token,
13669 helpanim_config[0].value);
13671 for (i = 1; helpanim_config[i].token; i++)
13672 insert_ptr = addListEntry(insert_ptr,
13673 helpanim_config[i].token,
13674 helpanim_config[i].value);
13677 element_hash = newSetupFileHash();
13678 action_hash = newSetupFileHash();
13679 direction_hash = newSetupFileHash();
13681 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13682 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13684 for (i = 0; i < NUM_ACTIONS; i++)
13685 setHashEntry(action_hash, element_action_info[i].suffix,
13686 i_to_a(element_action_info[i].value));
13688 // do not store direction index (bit) here, but direction value!
13689 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13690 setHashEntry(direction_hash, element_direction_info[i].suffix,
13691 i_to_a(1 << element_direction_info[i].value));
13693 for (list = setup_file_list; list != NULL; list = list->next)
13695 char *element_token, *action_token, *direction_token;
13696 char *element_value, *action_value, *direction_value;
13697 int delay = atoi(list->value);
13699 if (strEqual(list->token, "end"))
13701 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13706 /* first try to break element into element/action/direction parts;
13707 if this does not work, also accept combined "element[.act][.dir]"
13708 elements (like "dynamite.active"), which are unique elements */
13710 if (strchr(list->token, '.') == NULL) // token contains no '.'
13712 element_value = getHashEntry(element_hash, list->token);
13713 if (element_value != NULL) // element found
13714 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13715 &num_list_entries);
13718 // no further suffixes found -- this is not an element
13719 print_unknown_token(filename, list->token, num_unknown_tokens++);
13725 // token has format "<prefix>.<something>"
13727 action_token = strchr(list->token, '.'); // suffix may be action ...
13728 direction_token = action_token; // ... or direction
13730 element_token = getStringCopy(list->token);
13731 *strchr(element_token, '.') = '\0';
13733 element_value = getHashEntry(element_hash, element_token);
13735 if (element_value == NULL) // this is no element
13737 element_value = getHashEntry(element_hash, list->token);
13738 if (element_value != NULL) // combined element found
13739 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13740 &num_list_entries);
13742 print_unknown_token(filename, list->token, num_unknown_tokens++);
13744 free(element_token);
13749 action_value = getHashEntry(action_hash, action_token);
13751 if (action_value != NULL) // action found
13753 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13754 &num_list_entries);
13756 free(element_token);
13761 direction_value = getHashEntry(direction_hash, direction_token);
13763 if (direction_value != NULL) // direction found
13765 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13766 &num_list_entries);
13768 free(element_token);
13773 if (strchr(action_token + 1, '.') == NULL)
13775 // no further suffixes found -- this is not an action nor direction
13777 element_value = getHashEntry(element_hash, list->token);
13778 if (element_value != NULL) // combined element found
13779 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13780 &num_list_entries);
13782 print_unknown_token(filename, list->token, num_unknown_tokens++);
13784 free(element_token);
13789 // token has format "<prefix>.<suffix>.<something>"
13791 direction_token = strchr(action_token + 1, '.');
13793 action_token = getStringCopy(action_token);
13794 *strchr(action_token + 1, '.') = '\0';
13796 action_value = getHashEntry(action_hash, action_token);
13798 if (action_value == NULL) // this is no action
13800 element_value = getHashEntry(element_hash, list->token);
13801 if (element_value != NULL) // combined element found
13802 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13803 &num_list_entries);
13805 print_unknown_token(filename, list->token, num_unknown_tokens++);
13807 free(element_token);
13808 free(action_token);
13813 direction_value = getHashEntry(direction_hash, direction_token);
13815 if (direction_value != NULL) // direction found
13817 add_helpanim_entry(atoi(element_value), atoi(action_value),
13818 atoi(direction_value), delay, &num_list_entries);
13820 free(element_token);
13821 free(action_token);
13826 // this is no direction
13828 element_value = getHashEntry(element_hash, list->token);
13829 if (element_value != NULL) // combined element found
13830 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13831 &num_list_entries);
13833 print_unknown_token(filename, list->token, num_unknown_tokens++);
13835 free(element_token);
13836 free(action_token);
13839 print_unknown_token_end(num_unknown_tokens);
13841 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13842 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13844 freeSetupFileList(setup_file_list);
13845 freeSetupFileHash(element_hash);
13846 freeSetupFileHash(action_hash);
13847 freeSetupFileHash(direction_hash);
13850 for (i = 0; i < num_list_entries; i++)
13851 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13852 EL_NAME(helpanim_info[i].element),
13853 helpanim_info[i].element,
13854 helpanim_info[i].action,
13855 helpanim_info[i].direction,
13856 helpanim_info[i].delay);
13860 void LoadHelpTextInfo(void)
13862 char *filename = getHelpTextFilename();
13865 if (helptext_info != NULL)
13867 freeSetupFileHash(helptext_info);
13868 helptext_info = NULL;
13871 if (fileExists(filename))
13872 helptext_info = loadSetupFileHash(filename);
13874 if (helptext_info == NULL)
13876 // use reliable default values from static configuration
13877 helptext_info = newSetupFileHash();
13879 for (i = 0; helptext_config[i].token; i++)
13880 setHashEntry(helptext_info,
13881 helptext_config[i].token,
13882 helptext_config[i].value);
13886 BEGIN_HASH_ITERATION(helptext_info, itr)
13888 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13889 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13891 END_HASH_ITERATION(hash, itr)
13896 // ----------------------------------------------------------------------------
13898 // ----------------------------------------------------------------------------
13900 #define MAX_NUM_CONVERT_LEVELS 1000
13902 void ConvertLevels(void)
13904 static LevelDirTree *convert_leveldir = NULL;
13905 static int convert_level_nr = -1;
13906 static int num_levels_handled = 0;
13907 static int num_levels_converted = 0;
13908 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13911 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13912 global.convert_leveldir);
13914 if (convert_leveldir == NULL)
13915 Fail("no such level identifier: '%s'", global.convert_leveldir);
13917 leveldir_current = convert_leveldir;
13919 if (global.convert_level_nr != -1)
13921 convert_leveldir->first_level = global.convert_level_nr;
13922 convert_leveldir->last_level = global.convert_level_nr;
13925 convert_level_nr = convert_leveldir->first_level;
13927 PrintLine("=", 79);
13928 Print("Converting levels\n");
13929 PrintLine("-", 79);
13930 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13931 Print("Level series name: '%s'\n", convert_leveldir->name);
13932 Print("Level series author: '%s'\n", convert_leveldir->author);
13933 Print("Number of levels: %d\n", convert_leveldir->levels);
13934 PrintLine("=", 79);
13937 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13938 levels_failed[i] = FALSE;
13940 while (convert_level_nr <= convert_leveldir->last_level)
13942 char *level_filename;
13945 level_nr = convert_level_nr++;
13947 Print("Level %03d: ", level_nr);
13949 LoadLevel(level_nr);
13950 if (level.no_level_file || level.no_valid_file)
13952 Print("(no level)\n");
13956 Print("converting level ... ");
13959 // special case: conversion of some EMC levels as requested by ACME
13960 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13963 level_filename = getDefaultLevelFilename(level_nr);
13964 new_level = !fileExists(level_filename);
13968 SaveLevel(level_nr);
13970 num_levels_converted++;
13972 Print("converted.\n");
13976 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13977 levels_failed[level_nr] = TRUE;
13979 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13982 num_levels_handled++;
13986 PrintLine("=", 79);
13987 Print("Number of levels handled: %d\n", num_levels_handled);
13988 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13989 (num_levels_handled ?
13990 num_levels_converted * 100 / num_levels_handled : 0));
13991 PrintLine("-", 79);
13992 Print("Summary (for automatic parsing by scripts):\n");
13993 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13994 convert_leveldir->identifier, num_levels_converted,
13995 num_levels_handled,
13996 (num_levels_handled ?
13997 num_levels_converted * 100 / num_levels_handled : 0));
13999 if (num_levels_handled != num_levels_converted)
14001 Print(", FAILED:");
14002 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14003 if (levels_failed[i])
14008 PrintLine("=", 79);
14010 CloseAllAndExit(0);
14014 // ----------------------------------------------------------------------------
14015 // create and save images for use in level sketches (raw BMP format)
14016 // ----------------------------------------------------------------------------
14018 void CreateLevelSketchImages(void)
14024 InitElementPropertiesGfxElement();
14026 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14027 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14029 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14031 int element = getMappedElement(i);
14032 char basename1[16];
14033 char basename2[16];
14037 sprintf(basename1, "%04d.bmp", i);
14038 sprintf(basename2, "%04ds.bmp", i);
14040 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14041 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14043 DrawSizedElement(0, 0, element, TILESIZE);
14044 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14046 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14047 Fail("cannot save level sketch image file '%s'", filename1);
14049 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14050 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14052 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14053 Fail("cannot save level sketch image file '%s'", filename2);
14058 // create corresponding SQL statements (for normal and small images)
14061 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14062 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14065 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14066 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14068 // optional: create content for forum level sketch demonstration post
14070 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14073 FreeBitmap(bitmap1);
14074 FreeBitmap(bitmap2);
14077 fprintf(stderr, "\n");
14079 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14081 CloseAllAndExit(0);
14085 // ----------------------------------------------------------------------------
14086 // create and save images for element collecting animations (raw BMP format)
14087 // ----------------------------------------------------------------------------
14089 static boolean createCollectImage(int element)
14091 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14094 void CreateCollectElementImages(void)
14098 int anim_frames = num_steps - 1;
14099 int tile_size = TILESIZE;
14100 int anim_width = tile_size * anim_frames;
14101 int anim_height = tile_size;
14102 int num_collect_images = 0;
14103 int pos_collect_images = 0;
14105 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14106 if (createCollectImage(i))
14107 num_collect_images++;
14109 Info("Creating %d element collecting animation images ...",
14110 num_collect_images);
14112 int dst_width = anim_width * 2;
14113 int dst_height = anim_height * num_collect_images / 2;
14114 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14115 char *basename_bmp = "RocksCollect.bmp";
14116 char *basename_png = "RocksCollect.png";
14117 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14118 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14119 int len_filename_bmp = strlen(filename_bmp);
14120 int len_filename_png = strlen(filename_png);
14121 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14122 char cmd_convert[max_command_len];
14124 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14128 // force using RGBA surface for destination bitmap
14129 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14130 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14132 dst_bitmap->surface =
14133 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14135 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14137 if (!createCollectImage(i))
14140 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14141 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14142 int graphic = el2img(i);
14143 char *token_name = element_info[i].token_name;
14144 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14145 Bitmap *src_bitmap;
14148 Info("- creating collecting image for '%s' ...", token_name);
14150 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14152 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14153 tile_size, tile_size, 0, 0);
14155 // force using RGBA surface for temporary bitmap (using transparent black)
14156 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14157 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14159 tmp_bitmap->surface =
14160 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14162 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14164 for (j = 0; j < anim_frames; j++)
14166 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14167 int frame_size = frame_size_final * num_steps;
14168 int offset = (tile_size - frame_size_final) / 2;
14169 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14171 while (frame_size > frame_size_final)
14175 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14177 FreeBitmap(frame_bitmap);
14179 frame_bitmap = half_bitmap;
14182 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14183 frame_size_final, frame_size_final,
14184 dst_x + j * tile_size + offset, dst_y + offset);
14186 FreeBitmap(frame_bitmap);
14189 tmp_bitmap->surface_masked = NULL;
14191 FreeBitmap(tmp_bitmap);
14193 pos_collect_images++;
14196 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14197 Fail("cannot save element collecting image file '%s'", filename_bmp);
14199 FreeBitmap(dst_bitmap);
14201 Info("Converting image file from BMP to PNG ...");
14203 if (system(cmd_convert) != 0)
14204 Fail("converting image file failed");
14206 unlink(filename_bmp);
14210 CloseAllAndExit(0);
14214 // ----------------------------------------------------------------------------
14215 // create and save images for custom and group elements (raw BMP format)
14216 // ----------------------------------------------------------------------------
14218 void CreateCustomElementImages(char *directory)
14220 char *src_basename = "RocksCE-template.ilbm";
14221 char *dst_basename = "RocksCE.bmp";
14222 char *src_filename = getPath2(directory, src_basename);
14223 char *dst_filename = getPath2(directory, dst_basename);
14224 Bitmap *src_bitmap;
14226 int yoffset_ce = 0;
14227 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14230 InitVideoDefaults();
14232 ReCreateBitmap(&backbuffer, video.width, video.height);
14234 src_bitmap = LoadImage(src_filename);
14236 bitmap = CreateBitmap(TILEX * 16 * 2,
14237 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14240 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14247 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14248 TILEX * x, TILEY * y + yoffset_ce);
14250 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14252 TILEX * x + TILEX * 16,
14253 TILEY * y + yoffset_ce);
14255 for (j = 2; j >= 0; j--)
14259 BlitBitmap(src_bitmap, bitmap,
14260 TILEX + c * 7, 0, 6, 10,
14261 TILEX * x + 6 + j * 7,
14262 TILEY * y + 11 + yoffset_ce);
14264 BlitBitmap(src_bitmap, bitmap,
14265 TILEX + c * 8, TILEY, 6, 10,
14266 TILEX * 16 + TILEX * x + 6 + j * 8,
14267 TILEY * y + 10 + yoffset_ce);
14273 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14280 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14281 TILEX * x, TILEY * y + yoffset_ge);
14283 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14285 TILEX * x + TILEX * 16,
14286 TILEY * y + yoffset_ge);
14288 for (j = 1; j >= 0; j--)
14292 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14293 TILEX * x + 6 + j * 10,
14294 TILEY * y + 11 + yoffset_ge);
14296 BlitBitmap(src_bitmap, bitmap,
14297 TILEX + c * 8, TILEY + 12, 6, 10,
14298 TILEX * 16 + TILEX * x + 10 + j * 8,
14299 TILEY * y + 10 + yoffset_ge);
14305 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14306 Fail("cannot save CE graphics file '%s'", dst_filename);
14308 FreeBitmap(bitmap);
14310 CloseAllAndExit(0);