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)
605 // (some values for BD style amoeba following below)
608 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
609 &li.bd_diagonal_movements, FALSE
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
614 &li.bd_topmost_player_active, TRUE
618 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
619 &li.bd_pushing_prob, 25
623 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
624 &li.bd_pushing_prob_with_sweet, 100
628 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
629 &li.bd_push_mega_rock_with_sweet, FALSE
634 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
635 &li.score[SC_DIAMOND_EXTRA], 20
639 EL_BD_MAGIC_WALL, -1,
640 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
641 &li.bd_magic_wall_wait_hatching, FALSE
644 EL_BD_MAGIC_WALL, -1,
645 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
646 &li.bd_magic_wall_stops_amoeba, TRUE
649 // (the following values are related to various game elements)
653 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
654 &li.score[SC_EMERALD], 10
659 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
660 &li.score[SC_DIAMOND], 10
665 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
666 &li.score[SC_BUG], 10
671 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
672 &li.score[SC_SPACESHIP], 10
677 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
678 &li.score[SC_PACMAN], 10
683 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
684 &li.score[SC_NUT], 10
689 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
690 &li.score[SC_DYNAMITE], 10
695 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
696 &li.score[SC_KEY], 10
701 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
702 &li.score[SC_PEARL], 10
707 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
708 &li.score[SC_CRYSTAL], 10
711 // (amoeba values used by R'n'D game engine only)
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.amoeba_content, EL_DIAMOND
719 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
724 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
725 &li.grow_into_diggable, TRUE
727 // (amoeba values used by BD game engine only)
730 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
731 &li.bd_amoeba_wait_for_hatching, FALSE
735 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
736 &li.bd_amoeba_start_immediately, TRUE
740 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
741 &li.bd_amoeba_2_explode_by_amoeba, TRUE
745 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
746 &li.bd_amoeba_threshold_too_big, 200
750 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
751 &li.bd_amoeba_slow_growth_time, 200
755 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
756 &li.bd_amoeba_slow_growth_rate, 3
760 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
761 &li.bd_amoeba_fast_growth_rate, 25
765 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
766 &li.bd_amoeba_content_too_big, EL_BD_ROCK
770 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
771 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
776 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
777 &li.bd_amoeba_2_threshold_too_big, 200
781 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
782 &li.bd_amoeba_2_slow_growth_time, 200
786 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
787 &li.bd_amoeba_2_slow_growth_rate, 3
791 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
792 &li.bd_amoeba_2_fast_growth_rate, 25
796 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
797 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
801 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
802 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
806 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
807 &li.bd_amoeba_2_content_exploding, EL_EMPTY
811 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
812 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
817 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
818 &li.yamyam_content, EL_ROCK, NULL,
819 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
823 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
824 &li.score[SC_YAMYAM], 10
829 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
830 &li.score[SC_ROBOT], 10
834 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
840 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
846 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
847 &li.time_magic_wall, 10
852 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
853 &li.game_of_life[0], 2
857 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
858 &li.game_of_life[1], 3
862 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
863 &li.game_of_life[2], 3
867 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
868 &li.game_of_life[3], 3
872 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
873 &li.use_life_bugs, FALSE
878 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
883 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
888 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
893 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
898 EL_TIMEGATE_SWITCH, -1,
899 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
900 &li.time_timegate, 10
904 EL_LIGHT_SWITCH_ACTIVE, -1,
905 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
910 EL_SHIELD_NORMAL, -1,
911 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
912 &li.shield_normal_time, 10
915 EL_SHIELD_NORMAL, -1,
916 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
917 &li.score[SC_SHIELD], 10
921 EL_SHIELD_DEADLY, -1,
922 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
923 &li.shield_deadly_time, 10
926 EL_SHIELD_DEADLY, -1,
927 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
928 &li.score[SC_SHIELD], 10
933 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
938 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
939 &li.extra_time_score, 10
943 EL_TIME_ORB_FULL, -1,
944 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
945 &li.time_orb_time, 10
948 EL_TIME_ORB_FULL, -1,
949 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
950 &li.use_time_orb_bug, FALSE
955 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
956 &li.use_spring_bug, FALSE
961 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
962 &li.android_move_time, 10
966 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
967 &li.android_clone_time, 10
970 EL_EMC_ANDROID, SAVE_CONF_NEVER,
971 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
972 &li.android_clone_element[0], EL_EMPTY, NULL,
973 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
977 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
978 &li.android_clone_element[0], EL_EMPTY, NULL,
979 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
984 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
989 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
994 EL_EMC_MAGNIFIER, -1,
995 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
996 &li.magnify_score, 10
999 EL_EMC_MAGNIFIER, -1,
1000 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1001 &li.magnify_time, 10
1005 EL_EMC_MAGIC_BALL, -1,
1006 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1010 EL_EMC_MAGIC_BALL, -1,
1011 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1012 &li.ball_random, FALSE
1015 EL_EMC_MAGIC_BALL, -1,
1016 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1017 &li.ball_active_initial, FALSE
1020 EL_EMC_MAGIC_BALL, -1,
1021 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1022 &li.ball_content, EL_EMPTY, NULL,
1023 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1027 EL_SOKOBAN_FIELD_EMPTY, -1,
1028 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1029 &li.sb_fields_needed, TRUE
1033 EL_SOKOBAN_OBJECT, -1,
1034 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1035 &li.sb_objects_needed, TRUE
1040 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1041 &li.mm_laser_red, FALSE
1045 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1046 &li.mm_laser_green, FALSE
1050 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1051 &li.mm_laser_blue, TRUE
1056 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1057 &li.df_laser_red, TRUE
1061 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1062 &li.df_laser_green, TRUE
1066 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1067 &li.df_laser_blue, FALSE
1071 EL_MM_FUSE_ACTIVE, -1,
1072 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1073 &li.mm_time_fuse, 25
1077 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1078 &li.mm_time_bomb, 75
1082 EL_MM_GRAY_BALL, -1,
1083 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1084 &li.mm_time_ball, 75
1087 EL_MM_GRAY_BALL, -1,
1088 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1089 &li.mm_ball_choice_mode, ANIM_RANDOM
1092 EL_MM_GRAY_BALL, -1,
1093 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1094 &li.mm_ball_content, EL_EMPTY, NULL,
1095 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1098 EL_MM_GRAY_BALL, -1,
1099 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1100 &li.rotate_mm_ball_content, TRUE
1103 EL_MM_GRAY_BALL, -1,
1104 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1105 &li.explode_mm_ball, FALSE
1109 EL_MM_STEEL_BLOCK, -1,
1110 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1111 &li.mm_time_block, 75
1114 EL_MM_LIGHTBALL, -1,
1115 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1116 &li.score[SC_ELEM_BONUS], 10
1126 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1130 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1131 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1135 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1136 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1141 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1142 &xx_envelope.autowrap, FALSE
1146 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1147 &xx_envelope.centered, FALSE
1152 TYPE_STRING, CONF_VALUE_BYTES(1),
1153 &xx_envelope.text, -1, NULL,
1154 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1155 &xx_default_string_empty[0]
1165 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1169 TYPE_STRING, CONF_VALUE_BYTES(1),
1170 &xx_ei.description[0], -1,
1171 &yy_ei.description[0],
1172 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1173 &xx_default_description[0]
1178 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1179 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1180 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1182 #if ENABLE_RESERVED_CODE
1183 // (reserved for later use)
1186 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1187 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1188 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1194 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1195 &xx_ei.use_gfx_element, FALSE,
1196 &yy_ei.use_gfx_element
1200 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1201 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1202 &yy_ei.gfx_element_initial
1207 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1208 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1209 &yy_ei.access_direction
1214 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1215 &xx_ei.collect_score_initial, 10,
1216 &yy_ei.collect_score_initial
1220 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1221 &xx_ei.collect_count_initial, 1,
1222 &yy_ei.collect_count_initial
1227 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1228 &xx_ei.ce_value_fixed_initial, 0,
1229 &yy_ei.ce_value_fixed_initial
1233 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1234 &xx_ei.ce_value_random_initial, 0,
1235 &yy_ei.ce_value_random_initial
1239 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1240 &xx_ei.use_last_ce_value, FALSE,
1241 &yy_ei.use_last_ce_value
1246 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1247 &xx_ei.push_delay_fixed, 8,
1248 &yy_ei.push_delay_fixed
1252 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1253 &xx_ei.push_delay_random, 8,
1254 &yy_ei.push_delay_random
1258 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1259 &xx_ei.drop_delay_fixed, 0,
1260 &yy_ei.drop_delay_fixed
1264 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1265 &xx_ei.drop_delay_random, 0,
1266 &yy_ei.drop_delay_random
1270 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1271 &xx_ei.move_delay_fixed, 0,
1272 &yy_ei.move_delay_fixed
1276 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1277 &xx_ei.move_delay_random, 0,
1278 &yy_ei.move_delay_random
1282 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1283 &xx_ei.step_delay_fixed, 0,
1284 &yy_ei.step_delay_fixed
1288 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1289 &xx_ei.step_delay_random, 0,
1290 &yy_ei.step_delay_random
1295 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1296 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1301 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1302 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1303 &yy_ei.move_direction_initial
1307 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1308 &xx_ei.move_stepsize, TILEX / 8,
1309 &yy_ei.move_stepsize
1314 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1315 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1316 &yy_ei.move_enter_element
1320 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1321 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1322 &yy_ei.move_leave_element
1326 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1327 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1328 &yy_ei.move_leave_type
1333 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1334 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1335 &yy_ei.slippery_type
1340 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1341 &xx_ei.explosion_type, EXPLODES_3X3,
1342 &yy_ei.explosion_type
1346 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1347 &xx_ei.explosion_delay, 16,
1348 &yy_ei.explosion_delay
1352 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1353 &xx_ei.ignition_delay, 8,
1354 &yy_ei.ignition_delay
1359 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1360 &xx_ei.content, EL_EMPTY_SPACE,
1362 &xx_num_contents, 1, 1
1365 // ---------- "num_change_pages" must be the last entry ---------------------
1368 -1, SAVE_CONF_ALWAYS,
1369 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1370 &xx_ei.num_change_pages, 1,
1371 &yy_ei.num_change_pages
1382 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1384 // ---------- "current_change_page" must be the first entry -----------------
1387 -1, SAVE_CONF_ALWAYS,
1388 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1389 &xx_current_change_page, -1
1392 // ---------- (the remaining entries can be in any order) -------------------
1396 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1397 &xx_change.can_change, FALSE
1402 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1403 &xx_event_bits[0], 0
1407 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1408 &xx_event_bits[1], 0
1413 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1414 &xx_change.trigger_player, CH_PLAYER_ANY
1418 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1419 &xx_change.trigger_side, CH_SIDE_ANY
1423 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1424 &xx_change.trigger_page, CH_PAGE_ANY
1429 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1430 &xx_change.target_element, EL_EMPTY_SPACE
1435 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1436 &xx_change.delay_fixed, 0
1440 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1441 &xx_change.delay_random, 0
1445 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1446 &xx_change.delay_frames, FRAMES_PER_SECOND
1451 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1452 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1457 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1458 &xx_change.explode, FALSE
1462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1463 &xx_change.use_target_content, FALSE
1467 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1468 &xx_change.only_if_complete, FALSE
1472 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1473 &xx_change.use_random_replace, FALSE
1477 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1478 &xx_change.random_percentage, 100
1482 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1483 &xx_change.replace_when, CP_WHEN_EMPTY
1488 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1489 &xx_change.has_action, FALSE
1493 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1494 &xx_change.action_type, CA_NO_ACTION
1498 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1499 &xx_change.action_mode, CA_MODE_UNDEFINED
1503 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1504 &xx_change.action_arg, CA_ARG_UNDEFINED
1509 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1510 &xx_change.action_element, EL_EMPTY_SPACE
1515 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1516 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1517 &xx_num_contents, 1, 1
1527 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1531 TYPE_STRING, CONF_VALUE_BYTES(1),
1532 &xx_ei.description[0], -1, NULL,
1533 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1534 &xx_default_description[0]
1539 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1540 &xx_ei.use_gfx_element, FALSE
1544 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1545 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1550 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1551 &xx_group.choice_mode, ANIM_RANDOM
1556 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1557 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1558 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1568 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1572 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1573 &xx_ei.use_gfx_element, FALSE
1577 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1578 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1588 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1592 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1593 &li.block_snap_field, TRUE
1597 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1598 &li.continuous_snapping, TRUE
1602 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1603 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1607 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1608 &li.use_start_element[0], FALSE
1612 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1613 &li.start_element[0], EL_PLAYER_1
1617 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1618 &li.use_artwork_element[0], FALSE
1622 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1623 &li.artwork_element[0], EL_PLAYER_1
1627 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1628 &li.use_explosion_element[0], FALSE
1632 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1633 &li.explosion_element[0], EL_PLAYER_1
1648 filetype_id_list[] =
1650 { LEVEL_FILE_TYPE_RND, "RND" },
1651 { LEVEL_FILE_TYPE_BD, "BD" },
1652 { LEVEL_FILE_TYPE_EM, "EM" },
1653 { LEVEL_FILE_TYPE_SP, "SP" },
1654 { LEVEL_FILE_TYPE_DX, "DX" },
1655 { LEVEL_FILE_TYPE_SB, "SB" },
1656 { LEVEL_FILE_TYPE_DC, "DC" },
1657 { LEVEL_FILE_TYPE_MM, "MM" },
1658 { LEVEL_FILE_TYPE_MM, "DF" },
1663 // ============================================================================
1664 // level file functions
1665 // ============================================================================
1667 static boolean check_special_flags(char *flag)
1669 if (strEqual(options.special_flags, flag) ||
1670 strEqual(leveldir_current->special_flags, flag))
1676 static struct DateInfo getCurrentDate(void)
1678 time_t epoch_seconds = time(NULL);
1679 struct tm *now = localtime(&epoch_seconds);
1680 struct DateInfo date;
1682 date.year = now->tm_year + 1900;
1683 date.month = now->tm_mon + 1;
1684 date.day = now->tm_mday;
1686 date.src = DATE_SRC_CLOCK;
1691 static void resetEventFlags(struct ElementChangeInfo *change)
1695 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1696 change->has_event[i] = FALSE;
1699 static void resetEventBits(void)
1703 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1704 xx_event_bits[i] = 0;
1707 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1711 /* important: only change event flag if corresponding event bit is set
1712 (this is because all xx_event_bits[] values are loaded separately,
1713 and all xx_event_bits[] values are set back to zero before loading
1714 another value xx_event_bits[x] (each value representing 32 flags)) */
1716 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1717 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1718 change->has_event[i] = TRUE;
1721 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1725 /* in contrast to the above function setEventFlagsFromEventBits(), it
1726 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1727 depending on the corresponding change->has_event[i] values here, as
1728 all xx_event_bits[] values are reset in resetEventBits() before */
1730 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1731 if (change->has_event[i])
1732 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1735 static char *getDefaultElementDescription(struct ElementInfo *ei)
1737 static char description[MAX_ELEMENT_NAME_LEN + 1];
1738 char *default_description = (ei->custom_description != NULL ?
1739 ei->custom_description :
1740 ei->editor_description);
1743 // always start with reliable default values
1744 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1745 description[i] = '\0';
1747 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1748 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1750 return &description[0];
1753 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1755 char *default_description = getDefaultElementDescription(ei);
1758 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1759 ei->description[i] = default_description[i];
1762 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1766 for (i = 0; conf[i].data_type != -1; i++)
1768 int default_value = conf[i].default_value;
1769 int data_type = conf[i].data_type;
1770 int conf_type = conf[i].conf_type;
1771 int byte_mask = conf_type & CONF_MASK_BYTES;
1773 if (byte_mask == CONF_MASK_MULTI_BYTES)
1775 int default_num_entities = conf[i].default_num_entities;
1776 int max_num_entities = conf[i].max_num_entities;
1778 *(int *)(conf[i].num_entities) = default_num_entities;
1780 if (data_type == TYPE_STRING)
1782 char *default_string = conf[i].default_string;
1783 char *string = (char *)(conf[i].value);
1785 strncpy(string, default_string, max_num_entities);
1787 else if (data_type == TYPE_ELEMENT_LIST)
1789 int *element_array = (int *)(conf[i].value);
1792 for (j = 0; j < max_num_entities; j++)
1793 element_array[j] = default_value;
1795 else if (data_type == TYPE_CONTENT_LIST)
1797 struct Content *content = (struct Content *)(conf[i].value);
1800 for (c = 0; c < max_num_entities; c++)
1801 for (y = 0; y < 3; y++)
1802 for (x = 0; x < 3; x++)
1803 content[c].e[x][y] = default_value;
1806 else // constant size configuration data (1, 2 or 4 bytes)
1808 if (data_type == TYPE_BOOLEAN)
1809 *(boolean *)(conf[i].value) = default_value;
1811 *(int *) (conf[i].value) = default_value;
1816 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1820 for (i = 0; conf[i].data_type != -1; i++)
1822 int data_type = conf[i].data_type;
1823 int conf_type = conf[i].conf_type;
1824 int byte_mask = conf_type & CONF_MASK_BYTES;
1826 if (byte_mask == CONF_MASK_MULTI_BYTES)
1828 int max_num_entities = conf[i].max_num_entities;
1830 if (data_type == TYPE_STRING)
1832 char *string = (char *)(conf[i].value);
1833 char *string_copy = (char *)(conf[i].value_copy);
1835 strncpy(string_copy, string, max_num_entities);
1837 else if (data_type == TYPE_ELEMENT_LIST)
1839 int *element_array = (int *)(conf[i].value);
1840 int *element_array_copy = (int *)(conf[i].value_copy);
1843 for (j = 0; j < max_num_entities; j++)
1844 element_array_copy[j] = element_array[j];
1846 else if (data_type == TYPE_CONTENT_LIST)
1848 struct Content *content = (struct Content *)(conf[i].value);
1849 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1852 for (c = 0; c < max_num_entities; c++)
1853 for (y = 0; y < 3; y++)
1854 for (x = 0; x < 3; x++)
1855 content_copy[c].e[x][y] = content[c].e[x][y];
1858 else // constant size configuration data (1, 2 or 4 bytes)
1860 if (data_type == TYPE_BOOLEAN)
1861 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1863 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1868 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1872 xx_ei = *ei_from; // copy element data into temporary buffer
1873 yy_ei = *ei_to; // copy element data into temporary buffer
1875 copyConfigFromConfigList(chunk_config_CUSX_base);
1880 // ---------- reinitialize and copy change pages ----------
1882 ei_to->num_change_pages = ei_from->num_change_pages;
1883 ei_to->current_change_page = ei_from->current_change_page;
1885 setElementChangePages(ei_to, ei_to->num_change_pages);
1887 for (i = 0; i < ei_to->num_change_pages; i++)
1888 ei_to->change_page[i] = ei_from->change_page[i];
1890 // ---------- copy group element info ----------
1891 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1892 *ei_to->group = *ei_from->group;
1894 // mark this custom element as modified
1895 ei_to->modified_settings = TRUE;
1898 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1900 int change_page_size = sizeof(struct ElementChangeInfo);
1902 ei->num_change_pages = MAX(1, change_pages);
1905 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1907 if (ei->current_change_page >= ei->num_change_pages)
1908 ei->current_change_page = ei->num_change_pages - 1;
1910 ei->change = &ei->change_page[ei->current_change_page];
1913 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1915 xx_change = *change; // copy change data into temporary buffer
1917 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1919 *change = xx_change;
1921 resetEventFlags(change);
1923 change->direct_action = 0;
1924 change->other_action = 0;
1926 change->pre_change_function = NULL;
1927 change->change_function = NULL;
1928 change->post_change_function = NULL;
1931 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1935 li = *level; // copy level data into temporary buffer
1936 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1937 *level = li; // copy temporary buffer back to level data
1939 setLevelInfoToDefaults_BD();
1940 setLevelInfoToDefaults_EM();
1941 setLevelInfoToDefaults_SP();
1942 setLevelInfoToDefaults_MM();
1944 level->native_bd_level = &native_bd_level;
1945 level->native_em_level = &native_em_level;
1946 level->native_sp_level = &native_sp_level;
1947 level->native_mm_level = &native_mm_level;
1949 level->file_version = FILE_VERSION_ACTUAL;
1950 level->game_version = GAME_VERSION_ACTUAL;
1952 level->creation_date = getCurrentDate();
1954 level->encoding_16bit_field = TRUE;
1955 level->encoding_16bit_yamyam = TRUE;
1956 level->encoding_16bit_amoeba = TRUE;
1958 // clear level name and level author string buffers
1959 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1960 level->name[i] = '\0';
1961 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1962 level->author[i] = '\0';
1964 // set level name and level author to default values
1965 strcpy(level->name, NAMELESS_LEVEL_NAME);
1966 strcpy(level->author, ANONYMOUS_NAME);
1968 // set level playfield to playable default level with player and exit
1969 for (x = 0; x < MAX_LEV_FIELDX; x++)
1970 for (y = 0; y < MAX_LEV_FIELDY; y++)
1971 level->field[x][y] = EL_SAND;
1973 level->field[0][0] = EL_PLAYER_1;
1974 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1976 BorderElement = EL_STEELWALL;
1978 // detect custom elements when loading them
1979 level->file_has_custom_elements = FALSE;
1981 // set all bug compatibility flags to "false" => do not emulate this bug
1982 level->use_action_after_change_bug = FALSE;
1984 if (leveldir_current)
1986 // try to determine better author name than 'anonymous'
1987 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1989 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1990 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1994 switch (LEVELCLASS(leveldir_current))
1996 case LEVELCLASS_TUTORIAL:
1997 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2000 case LEVELCLASS_CONTRIB:
2001 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2002 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2005 case LEVELCLASS_PRIVATE:
2006 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2007 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2011 // keep default value
2018 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2020 static boolean clipboard_elements_initialized = FALSE;
2023 InitElementPropertiesStatic();
2025 li = *level; // copy level data into temporary buffer
2026 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2027 *level = li; // copy temporary buffer back to level data
2029 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2032 struct ElementInfo *ei = &element_info[element];
2034 if (element == EL_MM_GRAY_BALL)
2036 struct LevelInfo_MM *level_mm = level->native_mm_level;
2039 for (j = 0; j < level->num_mm_ball_contents; j++)
2040 level->mm_ball_content[j] =
2041 map_element_MM_to_RND(level_mm->ball_content[j]);
2044 // never initialize clipboard elements after the very first time
2045 // (to be able to use clipboard elements between several levels)
2046 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2049 if (IS_ENVELOPE(element))
2051 int envelope_nr = element - EL_ENVELOPE_1;
2053 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2055 level->envelope[envelope_nr] = xx_envelope;
2058 if (IS_CUSTOM_ELEMENT(element) ||
2059 IS_GROUP_ELEMENT(element) ||
2060 IS_INTERNAL_ELEMENT(element))
2062 xx_ei = *ei; // copy element data into temporary buffer
2064 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2069 setElementChangePages(ei, 1);
2070 setElementChangeInfoToDefaults(ei->change);
2072 if (IS_CUSTOM_ELEMENT(element) ||
2073 IS_GROUP_ELEMENT(element))
2075 setElementDescriptionToDefault(ei);
2077 ei->modified_settings = FALSE;
2080 if (IS_CUSTOM_ELEMENT(element) ||
2081 IS_INTERNAL_ELEMENT(element))
2083 // internal values used in level editor
2085 ei->access_type = 0;
2086 ei->access_layer = 0;
2087 ei->access_protected = 0;
2088 ei->walk_to_action = 0;
2089 ei->smash_targets = 0;
2092 ei->can_explode_by_fire = FALSE;
2093 ei->can_explode_smashed = FALSE;
2094 ei->can_explode_impact = FALSE;
2096 ei->current_change_page = 0;
2099 if (IS_GROUP_ELEMENT(element) ||
2100 IS_INTERNAL_ELEMENT(element))
2102 struct ElementGroupInfo *group;
2104 // initialize memory for list of elements in group
2105 if (ei->group == NULL)
2106 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2110 xx_group = *group; // copy group data into temporary buffer
2112 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2117 if (IS_EMPTY_ELEMENT(element) ||
2118 IS_INTERNAL_ELEMENT(element))
2120 xx_ei = *ei; // copy element data into temporary buffer
2122 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2128 clipboard_elements_initialized = TRUE;
2131 static void setLevelInfoToDefaults(struct LevelInfo *level,
2132 boolean level_info_only,
2133 boolean reset_file_status)
2135 setLevelInfoToDefaults_Level(level);
2137 if (!level_info_only)
2138 setLevelInfoToDefaults_Elements(level);
2140 if (reset_file_status)
2142 level->no_valid_file = FALSE;
2143 level->no_level_file = FALSE;
2146 level->changed = FALSE;
2149 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2151 level_file_info->nr = 0;
2152 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2153 level_file_info->packed = FALSE;
2155 setString(&level_file_info->basename, NULL);
2156 setString(&level_file_info->filename, NULL);
2159 int getMappedElement_SB(int, boolean);
2161 static void ActivateLevelTemplate(void)
2165 if (check_special_flags("load_xsb_to_ces"))
2167 // fill smaller playfields with padding "beyond border wall" elements
2168 if (level.fieldx < level_template.fieldx ||
2169 level.fieldy < level_template.fieldy)
2171 short field[level.fieldx][level.fieldy];
2172 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2173 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2174 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2175 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2177 // copy old playfield (which is smaller than the visible area)
2178 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2179 field[x][y] = level.field[x][y];
2181 // fill new, larger playfield with "beyond border wall" elements
2182 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2183 level.field[x][y] = getMappedElement_SB('_', TRUE);
2185 // copy the old playfield to the middle of the new playfield
2186 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2187 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2189 level.fieldx = new_fieldx;
2190 level.fieldy = new_fieldy;
2194 // Currently there is no special action needed to activate the template
2195 // data, because 'element_info' property settings overwrite the original
2196 // level data, while all other variables do not change.
2198 // Exception: 'from_level_template' elements in the original level playfield
2199 // are overwritten with the corresponding elements at the same position in
2200 // playfield from the level template.
2202 for (x = 0; x < level.fieldx; x++)
2203 for (y = 0; y < level.fieldy; y++)
2204 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2205 level.field[x][y] = level_template.field[x][y];
2207 if (check_special_flags("load_xsb_to_ces"))
2209 struct LevelInfo level_backup = level;
2211 // overwrite all individual level settings from template level settings
2212 level = level_template;
2214 // restore level file info
2215 level.file_info = level_backup.file_info;
2217 // restore playfield size
2218 level.fieldx = level_backup.fieldx;
2219 level.fieldy = level_backup.fieldy;
2221 // restore playfield content
2222 for (x = 0; x < level.fieldx; x++)
2223 for (y = 0; y < level.fieldy; y++)
2224 level.field[x][y] = level_backup.field[x][y];
2226 // restore name and author from individual level
2227 strcpy(level.name, level_backup.name);
2228 strcpy(level.author, level_backup.author);
2230 // restore flag "use_custom_template"
2231 level.use_custom_template = level_backup.use_custom_template;
2235 static boolean checkForPackageFromBasename_BD(char *basename)
2237 // check for native BD level file extensions
2238 if (!strSuffixLower(basename, ".bd") &&
2239 !strSuffixLower(basename, ".bdr") &&
2240 !strSuffixLower(basename, ".brc") &&
2241 !strSuffixLower(basename, ".gds"))
2244 // check for standard single-level BD files (like "001.bd")
2245 if (strSuffixLower(basename, ".bd") &&
2246 strlen(basename) == 6 &&
2247 basename[0] >= '0' && basename[0] <= '9' &&
2248 basename[1] >= '0' && basename[1] <= '9' &&
2249 basename[2] >= '0' && basename[2] <= '9')
2252 // this is a level package in native BD file format
2256 static char *getLevelFilenameFromBasename(char *basename)
2258 static char *filename = NULL;
2260 checked_free(filename);
2262 filename = getPath2(getCurrentLevelDir(), basename);
2267 static int getFileTypeFromBasename(char *basename)
2269 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2271 static char *filename = NULL;
2272 struct stat file_status;
2274 // ---------- try to determine file type from filename ----------
2276 // check for typical filename of a Supaplex level package file
2277 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2278 return LEVEL_FILE_TYPE_SP;
2280 // check for typical filename of a Diamond Caves II level package file
2281 if (strSuffixLower(basename, ".dc") ||
2282 strSuffixLower(basename, ".dc2"))
2283 return LEVEL_FILE_TYPE_DC;
2285 // check for typical filename of a Sokoban level package file
2286 if (strSuffixLower(basename, ".xsb") &&
2287 strchr(basename, '%') == NULL)
2288 return LEVEL_FILE_TYPE_SB;
2290 // check for typical filename of a Boulder Dash (GDash) level package file
2291 if (checkForPackageFromBasename_BD(basename))
2292 return LEVEL_FILE_TYPE_BD;
2294 // ---------- try to determine file type from filesize ----------
2296 checked_free(filename);
2297 filename = getPath2(getCurrentLevelDir(), basename);
2299 if (stat(filename, &file_status) == 0)
2301 // check for typical filesize of a Supaplex level package file
2302 if (file_status.st_size == 170496)
2303 return LEVEL_FILE_TYPE_SP;
2306 return LEVEL_FILE_TYPE_UNKNOWN;
2309 static int getFileTypeFromMagicBytes(char *filename, int type)
2313 if ((file = openFile(filename, MODE_READ)))
2315 char chunk_name[CHUNK_ID_LEN + 1];
2317 getFileChunkBE(file, chunk_name, NULL);
2319 if (strEqual(chunk_name, "MMII") ||
2320 strEqual(chunk_name, "MIRR"))
2321 type = LEVEL_FILE_TYPE_MM;
2329 static boolean checkForPackageFromBasename(char *basename)
2331 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2332 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2334 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2337 static char *getSingleLevelBasenameExt(int nr, char *extension)
2339 static char basename[MAX_FILENAME_LEN];
2342 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2344 sprintf(basename, "%03d.%s", nr, extension);
2349 static char *getSingleLevelBasename(int nr)
2351 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2354 static char *getPackedLevelBasename(int type)
2356 static char basename[MAX_FILENAME_LEN];
2357 char *directory = getCurrentLevelDir();
2359 DirectoryEntry *dir_entry;
2361 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2363 if ((dir = openDirectory(directory)) == NULL)
2365 Warn("cannot read current level directory '%s'", directory);
2370 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2372 char *entry_basename = dir_entry->basename;
2373 int entry_type = getFileTypeFromBasename(entry_basename);
2375 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2377 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2380 strcpy(basename, entry_basename);
2387 closeDirectory(dir);
2392 static char *getSingleLevelFilename(int nr)
2394 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2397 #if ENABLE_UNUSED_CODE
2398 static char *getPackedLevelFilename(int type)
2400 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2404 char *getDefaultLevelFilename(int nr)
2406 return getSingleLevelFilename(nr);
2409 #if ENABLE_UNUSED_CODE
2410 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2414 lfi->packed = FALSE;
2416 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2417 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2421 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2422 int type, char *format, ...)
2424 static char basename[MAX_FILENAME_LEN];
2427 va_start(ap, format);
2428 vsprintf(basename, format, ap);
2432 lfi->packed = FALSE;
2434 setString(&lfi->basename, basename);
2435 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2438 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2444 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2445 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2448 static int getFiletypeFromID(char *filetype_id)
2450 char *filetype_id_lower;
2451 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2454 if (filetype_id == NULL)
2455 return LEVEL_FILE_TYPE_UNKNOWN;
2457 filetype_id_lower = getStringToLower(filetype_id);
2459 for (i = 0; filetype_id_list[i].id != NULL; i++)
2461 char *id_lower = getStringToLower(filetype_id_list[i].id);
2463 if (strEqual(filetype_id_lower, id_lower))
2464 filetype = filetype_id_list[i].filetype;
2468 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2472 free(filetype_id_lower);
2477 char *getLocalLevelTemplateFilename(void)
2479 return getDefaultLevelFilename(-1);
2482 char *getGlobalLevelTemplateFilename(void)
2484 // global variable "leveldir_current" must be modified in the loop below
2485 LevelDirTree *leveldir_current_last = leveldir_current;
2486 char *filename = NULL;
2488 // check for template level in path from current to topmost tree node
2490 while (leveldir_current != NULL)
2492 filename = getDefaultLevelFilename(-1);
2494 if (fileExists(filename))
2497 leveldir_current = leveldir_current->node_parent;
2500 // restore global variable "leveldir_current" modified in above loop
2501 leveldir_current = leveldir_current_last;
2506 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2510 // special case: level number is negative => check for level template file
2513 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2514 getSingleLevelBasename(-1));
2516 // replace local level template filename with global template filename
2517 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2519 // no fallback if template file not existing
2523 // special case: check for file name/pattern specified in "levelinfo.conf"
2524 if (leveldir_current->level_filename != NULL)
2526 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2528 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2529 leveldir_current->level_filename, nr);
2531 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2533 if (fileExists(lfi->filename))
2536 else if (leveldir_current->level_filetype != NULL)
2538 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2540 // check for specified native level file with standard file name
2541 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2542 "%03d.%s", nr, LEVELFILE_EXTENSION);
2543 if (fileExists(lfi->filename))
2547 // check for native Rocks'n'Diamonds level file
2548 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2549 "%03d.%s", nr, LEVELFILE_EXTENSION);
2550 if (fileExists(lfi->filename))
2553 // check for native Boulder Dash level file
2554 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2555 if (fileExists(lfi->filename))
2558 // check for Emerald Mine level file (V1)
2559 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2560 'a' + (nr / 10) % 26, '0' + nr % 10);
2561 if (fileExists(lfi->filename))
2563 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2564 'A' + (nr / 10) % 26, '0' + nr % 10);
2565 if (fileExists(lfi->filename))
2568 // check for Emerald Mine level file (V2 to V5)
2569 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2570 if (fileExists(lfi->filename))
2573 // check for Emerald Mine level file (V6 / single mode)
2574 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2575 if (fileExists(lfi->filename))
2577 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2578 if (fileExists(lfi->filename))
2581 // check for Emerald Mine level file (V6 / teamwork mode)
2582 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2583 if (fileExists(lfi->filename))
2585 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2586 if (fileExists(lfi->filename))
2589 // check for various packed level file formats
2590 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2591 if (fileExists(lfi->filename))
2594 // no known level file found -- use default values (and fail later)
2595 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2596 "%03d.%s", nr, LEVELFILE_EXTENSION);
2599 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2601 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2602 lfi->type = getFileTypeFromBasename(lfi->basename);
2604 if (lfi->type == LEVEL_FILE_TYPE_RND)
2605 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2608 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2610 // always start with reliable default values
2611 setFileInfoToDefaults(level_file_info);
2613 level_file_info->nr = nr; // set requested level number
2615 determineLevelFileInfo_Filename(level_file_info);
2616 determineLevelFileInfo_Filetype(level_file_info);
2619 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2620 struct LevelFileInfo *lfi_to)
2622 lfi_to->nr = lfi_from->nr;
2623 lfi_to->type = lfi_from->type;
2624 lfi_to->packed = lfi_from->packed;
2626 setString(&lfi_to->basename, lfi_from->basename);
2627 setString(&lfi_to->filename, lfi_from->filename);
2630 // ----------------------------------------------------------------------------
2631 // functions for loading R'n'D level
2632 // ----------------------------------------------------------------------------
2634 int getMappedElement(int element)
2636 // remap some (historic, now obsolete) elements
2640 case EL_PLAYER_OBSOLETE:
2641 element = EL_PLAYER_1;
2644 case EL_KEY_OBSOLETE:
2648 case EL_EM_KEY_1_FILE_OBSOLETE:
2649 element = EL_EM_KEY_1;
2652 case EL_EM_KEY_2_FILE_OBSOLETE:
2653 element = EL_EM_KEY_2;
2656 case EL_EM_KEY_3_FILE_OBSOLETE:
2657 element = EL_EM_KEY_3;
2660 case EL_EM_KEY_4_FILE_OBSOLETE:
2661 element = EL_EM_KEY_4;
2664 case EL_ENVELOPE_OBSOLETE:
2665 element = EL_ENVELOPE_1;
2673 if (element >= NUM_FILE_ELEMENTS)
2675 Warn("invalid level element %d", element);
2677 element = EL_UNKNOWN;
2685 static int getMappedElementByVersion(int element, int game_version)
2687 // remap some elements due to certain game version
2689 if (game_version <= VERSION_IDENT(2,2,0,0))
2691 // map game font elements
2692 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2693 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2694 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2695 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2698 if (game_version < VERSION_IDENT(3,0,0,0))
2700 // map Supaplex gravity tube elements
2701 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2702 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2703 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2704 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2711 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2713 level->file_version = getFileVersion(file);
2714 level->game_version = getFileVersion(file);
2719 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2721 level->creation_date.year = getFile16BitBE(file);
2722 level->creation_date.month = getFile8Bit(file);
2723 level->creation_date.day = getFile8Bit(file);
2725 level->creation_date.src = DATE_SRC_LEVELFILE;
2730 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2732 int initial_player_stepsize;
2733 int initial_player_gravity;
2736 level->fieldx = getFile8Bit(file);
2737 level->fieldy = getFile8Bit(file);
2739 level->time = getFile16BitBE(file);
2740 level->gems_needed = getFile16BitBE(file);
2742 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2743 level->name[i] = getFile8Bit(file);
2744 level->name[MAX_LEVEL_NAME_LEN] = 0;
2746 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2747 level->score[i] = getFile8Bit(file);
2749 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2750 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2751 for (y = 0; y < 3; y++)
2752 for (x = 0; x < 3; x++)
2753 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2755 level->amoeba_speed = getFile8Bit(file);
2756 level->time_magic_wall = getFile8Bit(file);
2757 level->time_wheel = getFile8Bit(file);
2758 level->amoeba_content = getMappedElement(getFile8Bit(file));
2760 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2763 for (i = 0; i < MAX_PLAYERS; i++)
2764 level->initial_player_stepsize[i] = initial_player_stepsize;
2766 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2768 for (i = 0; i < MAX_PLAYERS; i++)
2769 level->initial_player_gravity[i] = initial_player_gravity;
2771 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2772 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2774 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2776 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2777 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2778 level->can_move_into_acid_bits = getFile32BitBE(file);
2779 level->dont_collide_with_bits = getFile8Bit(file);
2781 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2782 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2784 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2785 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2786 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2788 level->game_engine_type = getFile8Bit(file);
2790 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2795 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2799 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2800 level->name[i] = getFile8Bit(file);
2801 level->name[MAX_LEVEL_NAME_LEN] = 0;
2806 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2810 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2811 level->author[i] = getFile8Bit(file);
2812 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2817 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2820 int chunk_size_expected = level->fieldx * level->fieldy;
2822 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2823 stored with 16-bit encoding (and should be twice as big then).
2824 Even worse, playfield data was stored 16-bit when only yamyam content
2825 contained 16-bit elements and vice versa. */
2827 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2828 chunk_size_expected *= 2;
2830 if (chunk_size_expected != chunk_size)
2832 ReadUnusedBytesFromFile(file, chunk_size);
2833 return chunk_size_expected;
2836 for (y = 0; y < level->fieldy; y++)
2837 for (x = 0; x < level->fieldx; x++)
2838 level->field[x][y] =
2839 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2844 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2847 int header_size = 4;
2848 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2849 int chunk_size_expected = header_size + content_size;
2851 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2852 stored with 16-bit encoding (and should be twice as big then).
2853 Even worse, playfield data was stored 16-bit when only yamyam content
2854 contained 16-bit elements and vice versa. */
2856 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2857 chunk_size_expected += content_size;
2859 if (chunk_size_expected != chunk_size)
2861 ReadUnusedBytesFromFile(file, chunk_size);
2862 return chunk_size_expected;
2866 level->num_yamyam_contents = getFile8Bit(file);
2870 // correct invalid number of content fields -- should never happen
2871 if (level->num_yamyam_contents < 1 ||
2872 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2873 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2875 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2876 for (y = 0; y < 3; y++)
2877 for (x = 0; x < 3; x++)
2878 level->yamyam_content[i].e[x][y] =
2879 getMappedElement(level->encoding_16bit_field ?
2880 getFile16BitBE(file) : getFile8Bit(file));
2884 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2889 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2891 element = getMappedElement(getFile16BitBE(file));
2892 num_contents = getFile8Bit(file);
2894 getFile8Bit(file); // content x size (unused)
2895 getFile8Bit(file); // content y size (unused)
2897 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2899 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2900 for (y = 0; y < 3; y++)
2901 for (x = 0; x < 3; x++)
2902 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2904 // correct invalid number of content fields -- should never happen
2905 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2906 num_contents = STD_ELEMENT_CONTENTS;
2908 if (element == EL_YAMYAM)
2910 level->num_yamyam_contents = num_contents;
2912 for (i = 0; i < num_contents; i++)
2913 for (y = 0; y < 3; y++)
2914 for (x = 0; x < 3; x++)
2915 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2917 else if (element == EL_BD_AMOEBA)
2919 level->amoeba_content = content_array[0][0][0];
2923 Warn("cannot load content for element '%d'", element);
2929 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2935 int chunk_size_expected;
2937 element = getMappedElement(getFile16BitBE(file));
2938 if (!IS_ENVELOPE(element))
2939 element = EL_ENVELOPE_1;
2941 envelope_nr = element - EL_ENVELOPE_1;
2943 envelope_len = getFile16BitBE(file);
2945 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2946 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2948 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2950 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2951 if (chunk_size_expected != chunk_size)
2953 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2954 return chunk_size_expected;
2957 for (i = 0; i < envelope_len; i++)
2958 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2963 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2965 int num_changed_custom_elements = getFile16BitBE(file);
2966 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2969 if (chunk_size_expected != chunk_size)
2971 ReadUnusedBytesFromFile(file, chunk_size - 2);
2972 return chunk_size_expected;
2975 for (i = 0; i < num_changed_custom_elements; i++)
2977 int element = getMappedElement(getFile16BitBE(file));
2978 int properties = getFile32BitBE(file);
2980 if (IS_CUSTOM_ELEMENT(element))
2981 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2983 Warn("invalid custom element number %d", element);
2985 // older game versions that wrote level files with CUS1 chunks used
2986 // different default push delay values (not yet stored in level file)
2987 element_info[element].push_delay_fixed = 2;
2988 element_info[element].push_delay_random = 8;
2991 level->file_has_custom_elements = TRUE;
2996 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2998 int num_changed_custom_elements = getFile16BitBE(file);
2999 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3002 if (chunk_size_expected != chunk_size)
3004 ReadUnusedBytesFromFile(file, chunk_size - 2);
3005 return chunk_size_expected;
3008 for (i = 0; i < num_changed_custom_elements; i++)
3010 int element = getMappedElement(getFile16BitBE(file));
3011 int custom_target_element = getMappedElement(getFile16BitBE(file));
3013 if (IS_CUSTOM_ELEMENT(element))
3014 element_info[element].change->target_element = custom_target_element;
3016 Warn("invalid custom element number %d", element);
3019 level->file_has_custom_elements = TRUE;
3024 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3026 int num_changed_custom_elements = getFile16BitBE(file);
3027 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3030 if (chunk_size_expected != chunk_size)
3032 ReadUnusedBytesFromFile(file, chunk_size - 2);
3033 return chunk_size_expected;
3036 for (i = 0; i < num_changed_custom_elements; i++)
3038 int element = getMappedElement(getFile16BitBE(file));
3039 struct ElementInfo *ei = &element_info[element];
3040 unsigned int event_bits;
3042 if (!IS_CUSTOM_ELEMENT(element))
3044 Warn("invalid custom element number %d", element);
3046 element = EL_INTERNAL_DUMMY;
3049 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3050 ei->description[j] = getFile8Bit(file);
3051 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3053 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3055 // some free bytes for future properties and padding
3056 ReadUnusedBytesFromFile(file, 7);
3058 ei->use_gfx_element = getFile8Bit(file);
3059 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3061 ei->collect_score_initial = getFile8Bit(file);
3062 ei->collect_count_initial = getFile8Bit(file);
3064 ei->push_delay_fixed = getFile16BitBE(file);
3065 ei->push_delay_random = getFile16BitBE(file);
3066 ei->move_delay_fixed = getFile16BitBE(file);
3067 ei->move_delay_random = getFile16BitBE(file);
3069 ei->move_pattern = getFile16BitBE(file);
3070 ei->move_direction_initial = getFile8Bit(file);
3071 ei->move_stepsize = getFile8Bit(file);
3073 for (y = 0; y < 3; y++)
3074 for (x = 0; x < 3; x++)
3075 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3077 // bits 0 - 31 of "has_event[]"
3078 event_bits = getFile32BitBE(file);
3079 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3080 if (event_bits & (1u << j))
3081 ei->change->has_event[j] = TRUE;
3083 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3085 ei->change->delay_fixed = getFile16BitBE(file);
3086 ei->change->delay_random = getFile16BitBE(file);
3087 ei->change->delay_frames = getFile16BitBE(file);
3089 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3091 ei->change->explode = getFile8Bit(file);
3092 ei->change->use_target_content = getFile8Bit(file);
3093 ei->change->only_if_complete = getFile8Bit(file);
3094 ei->change->use_random_replace = getFile8Bit(file);
3096 ei->change->random_percentage = getFile8Bit(file);
3097 ei->change->replace_when = getFile8Bit(file);
3099 for (y = 0; y < 3; y++)
3100 for (x = 0; x < 3; x++)
3101 ei->change->target_content.e[x][y] =
3102 getMappedElement(getFile16BitBE(file));
3104 ei->slippery_type = getFile8Bit(file);
3106 // some free bytes for future properties and padding
3107 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3109 // mark that this custom element has been modified
3110 ei->modified_settings = TRUE;
3113 level->file_has_custom_elements = TRUE;
3118 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3120 struct ElementInfo *ei;
3121 int chunk_size_expected;
3125 // ---------- custom element base property values (96 bytes) ----------------
3127 element = getMappedElement(getFile16BitBE(file));
3129 if (!IS_CUSTOM_ELEMENT(element))
3131 Warn("invalid custom element number %d", element);
3133 ReadUnusedBytesFromFile(file, chunk_size - 2);
3138 ei = &element_info[element];
3140 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3141 ei->description[i] = getFile8Bit(file);
3142 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3144 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3146 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3148 ei->num_change_pages = getFile8Bit(file);
3150 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3151 if (chunk_size_expected != chunk_size)
3153 ReadUnusedBytesFromFile(file, chunk_size - 43);
3154 return chunk_size_expected;
3157 ei->ce_value_fixed_initial = getFile16BitBE(file);
3158 ei->ce_value_random_initial = getFile16BitBE(file);
3159 ei->use_last_ce_value = getFile8Bit(file);
3161 ei->use_gfx_element = getFile8Bit(file);
3162 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3164 ei->collect_score_initial = getFile8Bit(file);
3165 ei->collect_count_initial = getFile8Bit(file);
3167 ei->drop_delay_fixed = getFile8Bit(file);
3168 ei->push_delay_fixed = getFile8Bit(file);
3169 ei->drop_delay_random = getFile8Bit(file);
3170 ei->push_delay_random = getFile8Bit(file);
3171 ei->move_delay_fixed = getFile16BitBE(file);
3172 ei->move_delay_random = getFile16BitBE(file);
3174 // bits 0 - 15 of "move_pattern" ...
3175 ei->move_pattern = getFile16BitBE(file);
3176 ei->move_direction_initial = getFile8Bit(file);
3177 ei->move_stepsize = getFile8Bit(file);
3179 ei->slippery_type = getFile8Bit(file);
3181 for (y = 0; y < 3; y++)
3182 for (x = 0; x < 3; x++)
3183 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3185 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3186 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3187 ei->move_leave_type = getFile8Bit(file);
3189 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3190 ei->move_pattern |= (getFile16BitBE(file) << 16);
3192 ei->access_direction = getFile8Bit(file);
3194 ei->explosion_delay = getFile8Bit(file);
3195 ei->ignition_delay = getFile8Bit(file);
3196 ei->explosion_type = getFile8Bit(file);
3198 // some free bytes for future custom property values and padding
3199 ReadUnusedBytesFromFile(file, 1);
3201 // ---------- change page property values (48 bytes) ------------------------
3203 setElementChangePages(ei, ei->num_change_pages);
3205 for (i = 0; i < ei->num_change_pages; i++)
3207 struct ElementChangeInfo *change = &ei->change_page[i];
3208 unsigned int event_bits;
3210 // always start with reliable default values
3211 setElementChangeInfoToDefaults(change);
3213 // bits 0 - 31 of "has_event[]" ...
3214 event_bits = getFile32BitBE(file);
3215 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3216 if (event_bits & (1u << j))
3217 change->has_event[j] = TRUE;
3219 change->target_element = getMappedElement(getFile16BitBE(file));
3221 change->delay_fixed = getFile16BitBE(file);
3222 change->delay_random = getFile16BitBE(file);
3223 change->delay_frames = getFile16BitBE(file);
3225 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3227 change->explode = getFile8Bit(file);
3228 change->use_target_content = getFile8Bit(file);
3229 change->only_if_complete = getFile8Bit(file);
3230 change->use_random_replace = getFile8Bit(file);
3232 change->random_percentage = getFile8Bit(file);
3233 change->replace_when = getFile8Bit(file);
3235 for (y = 0; y < 3; y++)
3236 for (x = 0; x < 3; x++)
3237 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3239 change->can_change = getFile8Bit(file);
3241 change->trigger_side = getFile8Bit(file);
3243 change->trigger_player = getFile8Bit(file);
3244 change->trigger_page = getFile8Bit(file);
3246 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3247 CH_PAGE_ANY : (1 << change->trigger_page));
3249 change->has_action = getFile8Bit(file);
3250 change->action_type = getFile8Bit(file);
3251 change->action_mode = getFile8Bit(file);
3252 change->action_arg = getFile16BitBE(file);
3254 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3255 event_bits = getFile8Bit(file);
3256 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3257 if (event_bits & (1u << (j - 32)))
3258 change->has_event[j] = TRUE;
3261 // mark this custom element as modified
3262 ei->modified_settings = TRUE;
3264 level->file_has_custom_elements = TRUE;
3269 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3271 struct ElementInfo *ei;
3272 struct ElementGroupInfo *group;
3276 element = getMappedElement(getFile16BitBE(file));
3278 if (!IS_GROUP_ELEMENT(element))
3280 Warn("invalid group element number %d", element);
3282 ReadUnusedBytesFromFile(file, chunk_size - 2);
3287 ei = &element_info[element];
3289 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3290 ei->description[i] = getFile8Bit(file);
3291 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3293 group = element_info[element].group;
3295 group->num_elements = getFile8Bit(file);
3297 ei->use_gfx_element = getFile8Bit(file);
3298 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3300 group->choice_mode = getFile8Bit(file);
3302 // some free bytes for future values and padding
3303 ReadUnusedBytesFromFile(file, 3);
3305 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3306 group->element[i] = getMappedElement(getFile16BitBE(file));
3308 // mark this group element as modified
3309 element_info[element].modified_settings = TRUE;
3311 level->file_has_custom_elements = TRUE;
3316 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3317 int element, int real_element)
3319 int micro_chunk_size = 0;
3320 int conf_type = getFile8Bit(file);
3321 int byte_mask = conf_type & CONF_MASK_BYTES;
3322 boolean element_found = FALSE;
3325 micro_chunk_size += 1;
3327 if (byte_mask == CONF_MASK_MULTI_BYTES)
3329 int num_bytes = getFile16BitBE(file);
3330 byte *buffer = checked_malloc(num_bytes);
3332 ReadBytesFromFile(file, buffer, num_bytes);
3334 for (i = 0; conf[i].data_type != -1; i++)
3336 if (conf[i].element == element &&
3337 conf[i].conf_type == conf_type)
3339 int data_type = conf[i].data_type;
3340 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3341 int max_num_entities = conf[i].max_num_entities;
3343 if (num_entities > max_num_entities)
3345 Warn("truncating number of entities for element %d from %d to %d",
3346 element, num_entities, max_num_entities);
3348 num_entities = max_num_entities;
3351 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3352 data_type == TYPE_CONTENT_LIST))
3354 // for element and content lists, zero entities are not allowed
3355 Warn("found empty list of entities for element %d", element);
3357 // do not set "num_entities" here to prevent reading behind buffer
3359 *(int *)(conf[i].num_entities) = 1; // at least one is required
3363 *(int *)(conf[i].num_entities) = num_entities;
3366 element_found = TRUE;
3368 if (data_type == TYPE_STRING)
3370 char *string = (char *)(conf[i].value);
3373 for (j = 0; j < max_num_entities; j++)
3374 string[j] = (j < num_entities ? buffer[j] : '\0');
3376 else if (data_type == TYPE_ELEMENT_LIST)
3378 int *element_array = (int *)(conf[i].value);
3381 for (j = 0; j < num_entities; j++)
3383 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3385 else if (data_type == TYPE_CONTENT_LIST)
3387 struct Content *content= (struct Content *)(conf[i].value);
3390 for (c = 0; c < num_entities; c++)
3391 for (y = 0; y < 3; y++)
3392 for (x = 0; x < 3; x++)
3393 content[c].e[x][y] =
3394 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3397 element_found = FALSE;
3403 checked_free(buffer);
3405 micro_chunk_size += 2 + num_bytes;
3407 else // constant size configuration data (1, 2 or 4 bytes)
3409 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3410 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3411 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3413 for (i = 0; conf[i].data_type != -1; i++)
3415 if (conf[i].element == element &&
3416 conf[i].conf_type == conf_type)
3418 int data_type = conf[i].data_type;
3420 if (data_type == TYPE_ELEMENT)
3421 value = getMappedElement(value);
3423 if (data_type == TYPE_BOOLEAN)
3424 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3426 *(int *) (conf[i].value) = value;
3428 element_found = TRUE;
3434 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3439 char *error_conf_chunk_bytes =
3440 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3441 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3442 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3443 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3444 int error_element = real_element;
3446 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3447 error_conf_chunk_bytes, error_conf_chunk_token,
3448 error_element, EL_NAME(error_element));
3451 return micro_chunk_size;
3454 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3456 int real_chunk_size = 0;
3458 li = *level; // copy level data into temporary buffer
3460 while (!checkEndOfFile(file))
3462 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3464 if (real_chunk_size >= chunk_size)
3468 *level = li; // copy temporary buffer back to level data
3470 return real_chunk_size;
3473 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3475 int real_chunk_size = 0;
3477 li = *level; // copy level data into temporary buffer
3479 while (!checkEndOfFile(file))
3481 int element = getMappedElement(getFile16BitBE(file));
3483 real_chunk_size += 2;
3484 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3486 if (real_chunk_size >= chunk_size)
3490 *level = li; // copy temporary buffer back to level data
3492 return real_chunk_size;
3495 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3497 int real_chunk_size = 0;
3499 li = *level; // copy level data into temporary buffer
3501 while (!checkEndOfFile(file))
3503 int element = getMappedElement(getFile16BitBE(file));
3505 real_chunk_size += 2;
3506 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3508 if (real_chunk_size >= chunk_size)
3512 *level = li; // copy temporary buffer back to level data
3514 return real_chunk_size;
3517 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3519 int element = getMappedElement(getFile16BitBE(file));
3520 int envelope_nr = element - EL_ENVELOPE_1;
3521 int real_chunk_size = 2;
3523 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3525 while (!checkEndOfFile(file))
3527 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3530 if (real_chunk_size >= chunk_size)
3534 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3536 return real_chunk_size;
3539 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3541 int element = getMappedElement(getFile16BitBE(file));
3542 int real_chunk_size = 2;
3543 struct ElementInfo *ei = &element_info[element];
3546 xx_ei = *ei; // copy element data into temporary buffer
3548 xx_ei.num_change_pages = -1;
3550 while (!checkEndOfFile(file))
3552 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3554 if (xx_ei.num_change_pages != -1)
3557 if (real_chunk_size >= chunk_size)
3563 if (ei->num_change_pages == -1)
3565 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3568 ei->num_change_pages = 1;
3570 setElementChangePages(ei, 1);
3571 setElementChangeInfoToDefaults(ei->change);
3573 return real_chunk_size;
3576 // initialize number of change pages stored for this custom element
3577 setElementChangePages(ei, ei->num_change_pages);
3578 for (i = 0; i < ei->num_change_pages; i++)
3579 setElementChangeInfoToDefaults(&ei->change_page[i]);
3581 // start with reading properties for the first change page
3582 xx_current_change_page = 0;
3584 while (!checkEndOfFile(file))
3586 // level file might contain invalid change page number
3587 if (xx_current_change_page >= ei->num_change_pages)
3590 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3592 xx_change = *change; // copy change data into temporary buffer
3594 resetEventBits(); // reset bits; change page might have changed
3596 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3599 *change = xx_change;
3601 setEventFlagsFromEventBits(change);
3603 if (real_chunk_size >= chunk_size)
3607 level->file_has_custom_elements = TRUE;
3609 return real_chunk_size;
3612 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3614 int element = getMappedElement(getFile16BitBE(file));
3615 int real_chunk_size = 2;
3616 struct ElementInfo *ei = &element_info[element];
3617 struct ElementGroupInfo *group = ei->group;
3622 xx_ei = *ei; // copy element data into temporary buffer
3623 xx_group = *group; // copy group data into temporary buffer
3625 while (!checkEndOfFile(file))
3627 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3630 if (real_chunk_size >= chunk_size)
3637 level->file_has_custom_elements = TRUE;
3639 return real_chunk_size;
3642 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3644 int element = getMappedElement(getFile16BitBE(file));
3645 int real_chunk_size = 2;
3646 struct ElementInfo *ei = &element_info[element];
3648 xx_ei = *ei; // copy element data into temporary buffer
3650 while (!checkEndOfFile(file))
3652 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3655 if (real_chunk_size >= chunk_size)
3661 level->file_has_custom_elements = TRUE;
3663 return real_chunk_size;
3666 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3667 struct LevelFileInfo *level_file_info,
3668 boolean level_info_only)
3670 char *filename = level_file_info->filename;
3671 char cookie[MAX_LINE_LEN];
3672 char chunk_name[CHUNK_ID_LEN + 1];
3676 if (!(file = openFile(filename, MODE_READ)))
3678 level->no_valid_file = TRUE;
3679 level->no_level_file = TRUE;
3681 if (level_info_only)
3684 Warn("cannot read level '%s' -- using empty level", filename);
3686 if (!setup.editor.use_template_for_new_levels)
3689 // if level file not found, try to initialize level data from template
3690 filename = getGlobalLevelTemplateFilename();
3692 if (!(file = openFile(filename, MODE_READ)))
3695 // default: for empty levels, use level template for custom elements
3696 level->use_custom_template = TRUE;
3698 level->no_valid_file = FALSE;
3701 getFileChunkBE(file, chunk_name, NULL);
3702 if (strEqual(chunk_name, "RND1"))
3704 getFile32BitBE(file); // not used
3706 getFileChunkBE(file, chunk_name, NULL);
3707 if (!strEqual(chunk_name, "CAVE"))
3709 level->no_valid_file = TRUE;
3711 Warn("unknown format of level file '%s'", filename);
3718 else // check for pre-2.0 file format with cookie string
3720 strcpy(cookie, chunk_name);
3721 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3723 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3724 cookie[strlen(cookie) - 1] = '\0';
3726 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3728 level->no_valid_file = TRUE;
3730 Warn("unknown format of level file '%s'", filename);
3737 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3739 level->no_valid_file = TRUE;
3741 Warn("unsupported version of level file '%s'", filename);
3748 // pre-2.0 level files have no game version, so use file version here
3749 level->game_version = level->file_version;
3752 if (level->file_version < FILE_VERSION_1_2)
3754 // level files from versions before 1.2.0 without chunk structure
3755 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3756 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3764 int (*loader)(File *, int, struct LevelInfo *);
3768 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3769 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3770 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3771 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3772 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3773 { "INFO", -1, LoadLevel_INFO },
3774 { "BODY", -1, LoadLevel_BODY },
3775 { "CONT", -1, LoadLevel_CONT },
3776 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3777 { "CNT3", -1, LoadLevel_CNT3 },
3778 { "CUS1", -1, LoadLevel_CUS1 },
3779 { "CUS2", -1, LoadLevel_CUS2 },
3780 { "CUS3", -1, LoadLevel_CUS3 },
3781 { "CUS4", -1, LoadLevel_CUS4 },
3782 { "GRP1", -1, LoadLevel_GRP1 },
3783 { "CONF", -1, LoadLevel_CONF },
3784 { "ELEM", -1, LoadLevel_ELEM },
3785 { "NOTE", -1, LoadLevel_NOTE },
3786 { "CUSX", -1, LoadLevel_CUSX },
3787 { "GRPX", -1, LoadLevel_GRPX },
3788 { "EMPX", -1, LoadLevel_EMPX },
3793 while (getFileChunkBE(file, chunk_name, &chunk_size))
3797 while (chunk_info[i].name != NULL &&
3798 !strEqual(chunk_name, chunk_info[i].name))
3801 if (chunk_info[i].name == NULL)
3803 Warn("unknown chunk '%s' in level file '%s'",
3804 chunk_name, filename);
3806 ReadUnusedBytesFromFile(file, chunk_size);
3808 else if (chunk_info[i].size != -1 &&
3809 chunk_info[i].size != chunk_size)
3811 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3812 chunk_size, chunk_name, filename);
3814 ReadUnusedBytesFromFile(file, chunk_size);
3818 // call function to load this level chunk
3819 int chunk_size_expected =
3820 (chunk_info[i].loader)(file, chunk_size, level);
3822 if (chunk_size_expected < 0)
3824 Warn("error reading chunk '%s' in level file '%s'",
3825 chunk_name, filename);
3830 // the size of some chunks cannot be checked before reading other
3831 // chunks first (like "HEAD" and "BODY") that contain some header
3832 // information, so check them here
3833 if (chunk_size_expected != chunk_size)
3835 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3836 chunk_size, chunk_name, filename);
3848 // ----------------------------------------------------------------------------
3849 // functions for loading BD level
3850 // ----------------------------------------------------------------------------
3852 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3854 struct LevelInfo_BD *level_bd = level->native_bd_level;
3855 GdCave *cave = NULL; // will be changed below
3856 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3857 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3860 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3862 // cave and map newly allocated when set to defaults above
3863 cave = level_bd->cave;
3866 cave->intermission = level->bd_intermission;
3869 cave->level_time[0] = level->time;
3870 cave->level_diamonds[0] = level->gems_needed;
3873 cave->scheduling = level->bd_scheduling_type;
3874 cave->pal_timing = level->bd_pal_timing;
3875 cave->level_speed[0] = level->bd_cycle_delay_ms;
3876 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3877 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3878 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3881 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3882 cave->diamond_value = level->score[SC_EMERALD];
3883 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3885 // compatibility settings
3886 cave->lineshift = level->bd_line_shifting_borders;
3887 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3888 cave->short_explosions = level->bd_short_explosions;
3889 cave->gravity_affects_all = level->bd_gravity_affects_all;
3891 // player properties
3892 cave->diagonal_movements = level->bd_diagonal_movements;
3893 cave->active_is_first_found = level->bd_topmost_player_active;
3894 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3895 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3896 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3898 // element properties
3899 cave->level_magic_wall_time[0] = level->time_magic_wall;
3900 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
3901 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
3902 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
3903 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
3904 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
3905 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
3906 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
3907 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
3908 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
3909 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
3910 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
3911 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
3912 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
3914 cave->amoeba_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
3915 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
3916 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
3917 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
3918 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
3919 cave->amoeba_2_looks_like = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
3922 strncpy(cave->name, level->name, sizeof(GdString));
3923 cave->name[sizeof(GdString) - 1] = '\0';
3925 // playfield elements
3926 for (x = 0; x < cave->w; x++)
3927 for (y = 0; y < cave->h; y++)
3928 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3931 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3933 struct LevelInfo_BD *level_bd = level->native_bd_level;
3934 GdCave *cave = level_bd->cave;
3935 int bd_level_nr = level_bd->level_nr;
3938 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3939 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3942 level->bd_intermission = cave->intermission;
3945 level->time = cave->level_time[bd_level_nr];
3946 level->gems_needed = cave->level_diamonds[bd_level_nr];
3949 level->bd_scheduling_type = cave->scheduling;
3950 level->bd_pal_timing = cave->pal_timing;
3951 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3952 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3953 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3954 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3957 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3958 level->score[SC_EMERALD] = cave->diamond_value;
3959 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3961 // compatibility settings
3962 level->bd_line_shifting_borders = cave->lineshift;
3963 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
3964 level->bd_short_explosions = cave->short_explosions;
3965 level->bd_gravity_affects_all = cave->gravity_affects_all;
3967 // player properties
3968 level->bd_diagonal_movements = cave->diagonal_movements;
3969 level->bd_topmost_player_active = cave->active_is_first_found;
3970 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
3971 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
3972 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
3974 // element properties
3975 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3976 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
3977 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
3978 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
3979 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
3980 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
3981 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[0];
3982 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[0];
3983 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
3984 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
3985 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[0];
3986 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[0];
3987 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
3988 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
3990 level->bd_amoeba_content_too_big = map_element_BD_to_RND(cave->amoeba_too_big_effect);
3991 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
3992 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
3993 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
3994 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
3995 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
3998 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4000 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4001 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4003 // playfield elements
4004 for (x = 0; x < level->fieldx; x++)
4005 for (y = 0; y < level->fieldy; y++)
4006 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4008 checked_free(cave_name);
4011 static void setTapeInfoToDefaults(void);
4013 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4015 struct LevelInfo_BD *level_bd = level->native_bd_level;
4016 GdCave *cave = level_bd->cave;
4017 GdReplay *replay = level_bd->replay;
4023 // always start with reliable default values
4024 setTapeInfoToDefaults();
4026 tape.level_nr = level_nr; // (currently not used)
4027 tape.random_seed = replay->seed;
4029 TapeSetDateFromIsoDateString(replay->date);
4032 tape.pos[tape.counter].delay = 0;
4034 tape.bd_replay = TRUE;
4036 // all time calculations only used to display approximate tape time
4037 int cave_speed = cave->speed;
4038 int milliseconds_game = 0;
4039 int milliseconds_elapsed = 20;
4041 for (i = 0; i < replay->movements->len; i++)
4043 int replay_action = replay->movements->data[i];
4044 int tape_action = map_action_BD_to_RND(replay_action);
4045 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4046 boolean success = 0;
4050 success = TapeAddAction(action);
4052 milliseconds_game += milliseconds_elapsed;
4054 if (milliseconds_game >= cave_speed)
4056 milliseconds_game -= cave_speed;
4063 tape.pos[tape.counter].delay = 0;
4064 tape.pos[tape.counter].action[0] = 0;
4068 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4074 TapeHaltRecording();
4078 // ----------------------------------------------------------------------------
4079 // functions for loading EM level
4080 // ----------------------------------------------------------------------------
4082 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4084 static int ball_xy[8][2] =
4095 struct LevelInfo_EM *level_em = level->native_em_level;
4096 struct CAVE *cav = level_em->cav;
4099 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4100 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4102 cav->time_seconds = level->time;
4103 cav->gems_needed = level->gems_needed;
4105 cav->emerald_score = level->score[SC_EMERALD];
4106 cav->diamond_score = level->score[SC_DIAMOND];
4107 cav->alien_score = level->score[SC_ROBOT];
4108 cav->tank_score = level->score[SC_SPACESHIP];
4109 cav->bug_score = level->score[SC_BUG];
4110 cav->eater_score = level->score[SC_YAMYAM];
4111 cav->nut_score = level->score[SC_NUT];
4112 cav->dynamite_score = level->score[SC_DYNAMITE];
4113 cav->key_score = level->score[SC_KEY];
4114 cav->exit_score = level->score[SC_TIME_BONUS];
4116 cav->num_eater_arrays = level->num_yamyam_contents;
4118 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4119 for (y = 0; y < 3; y++)
4120 for (x = 0; x < 3; x++)
4121 cav->eater_array[i][y * 3 + x] =
4122 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4124 cav->amoeba_time = level->amoeba_speed;
4125 cav->wonderwall_time = level->time_magic_wall;
4126 cav->wheel_time = level->time_wheel;
4128 cav->android_move_time = level->android_move_time;
4129 cav->android_clone_time = level->android_clone_time;
4130 cav->ball_random = level->ball_random;
4131 cav->ball_active = level->ball_active_initial;
4132 cav->ball_time = level->ball_time;
4133 cav->num_ball_arrays = level->num_ball_contents;
4135 cav->lenses_score = level->lenses_score;
4136 cav->magnify_score = level->magnify_score;
4137 cav->slurp_score = level->slurp_score;
4139 cav->lenses_time = level->lenses_time;
4140 cav->magnify_time = level->magnify_time;
4142 cav->wind_time = 9999;
4143 cav->wind_direction =
4144 map_direction_RND_to_EM(level->wind_direction_initial);
4146 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4147 for (j = 0; j < 8; j++)
4148 cav->ball_array[i][j] =
4149 map_element_RND_to_EM_cave(level->ball_content[i].
4150 e[ball_xy[j][0]][ball_xy[j][1]]);
4152 map_android_clone_elements_RND_to_EM(level);
4154 // first fill the complete playfield with the empty space element
4155 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4156 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4157 cav->cave[x][y] = Cblank;
4159 // then copy the real level contents from level file into the playfield
4160 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4162 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4164 if (level->field[x][y] == EL_AMOEBA_DEAD)
4165 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4167 cav->cave[x][y] = new_element;
4170 for (i = 0; i < MAX_PLAYERS; i++)
4172 cav->player_x[i] = -1;
4173 cav->player_y[i] = -1;
4176 // initialize player positions and delete players from the playfield
4177 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4179 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4181 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4183 cav->player_x[player_nr] = x;
4184 cav->player_y[player_nr] = y;
4186 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4191 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4193 static int ball_xy[8][2] =
4204 struct LevelInfo_EM *level_em = level->native_em_level;
4205 struct CAVE *cav = level_em->cav;
4208 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4209 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4211 level->time = cav->time_seconds;
4212 level->gems_needed = cav->gems_needed;
4214 sprintf(level->name, "Level %d", level->file_info.nr);
4216 level->score[SC_EMERALD] = cav->emerald_score;
4217 level->score[SC_DIAMOND] = cav->diamond_score;
4218 level->score[SC_ROBOT] = cav->alien_score;
4219 level->score[SC_SPACESHIP] = cav->tank_score;
4220 level->score[SC_BUG] = cav->bug_score;
4221 level->score[SC_YAMYAM] = cav->eater_score;
4222 level->score[SC_NUT] = cav->nut_score;
4223 level->score[SC_DYNAMITE] = cav->dynamite_score;
4224 level->score[SC_KEY] = cav->key_score;
4225 level->score[SC_TIME_BONUS] = cav->exit_score;
4227 level->num_yamyam_contents = cav->num_eater_arrays;
4229 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4230 for (y = 0; y < 3; y++)
4231 for (x = 0; x < 3; x++)
4232 level->yamyam_content[i].e[x][y] =
4233 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4235 level->amoeba_speed = cav->amoeba_time;
4236 level->time_magic_wall = cav->wonderwall_time;
4237 level->time_wheel = cav->wheel_time;
4239 level->android_move_time = cav->android_move_time;
4240 level->android_clone_time = cav->android_clone_time;
4241 level->ball_random = cav->ball_random;
4242 level->ball_active_initial = cav->ball_active;
4243 level->ball_time = cav->ball_time;
4244 level->num_ball_contents = cav->num_ball_arrays;
4246 level->lenses_score = cav->lenses_score;
4247 level->magnify_score = cav->magnify_score;
4248 level->slurp_score = cav->slurp_score;
4250 level->lenses_time = cav->lenses_time;
4251 level->magnify_time = cav->magnify_time;
4253 level->wind_direction_initial =
4254 map_direction_EM_to_RND(cav->wind_direction);
4256 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4257 for (j = 0; j < 8; j++)
4258 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4259 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4261 map_android_clone_elements_EM_to_RND(level);
4263 // convert the playfield (some elements need special treatment)
4264 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4266 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4268 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4269 new_element = EL_AMOEBA_DEAD;
4271 level->field[x][y] = new_element;
4274 for (i = 0; i < MAX_PLAYERS; i++)
4276 // in case of all players set to the same field, use the first player
4277 int nr = MAX_PLAYERS - i - 1;
4278 int jx = cav->player_x[nr];
4279 int jy = cav->player_y[nr];
4281 if (jx != -1 && jy != -1)
4282 level->field[jx][jy] = EL_PLAYER_1 + nr;
4285 // time score is counted for each 10 seconds left in Emerald Mine levels
4286 level->time_score_base = 10;
4290 // ----------------------------------------------------------------------------
4291 // functions for loading SP level
4292 // ----------------------------------------------------------------------------
4294 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4296 struct LevelInfo_SP *level_sp = level->native_sp_level;
4297 LevelInfoType *header = &level_sp->header;
4300 level_sp->width = level->fieldx;
4301 level_sp->height = level->fieldy;
4303 for (x = 0; x < level->fieldx; x++)
4304 for (y = 0; y < level->fieldy; y++)
4305 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4307 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4309 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4310 header->LevelTitle[i] = level->name[i];
4311 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4313 header->InfotronsNeeded = level->gems_needed;
4315 header->SpecialPortCount = 0;
4317 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4319 boolean gravity_port_found = FALSE;
4320 boolean gravity_port_valid = FALSE;
4321 int gravity_port_flag;
4322 int gravity_port_base_element;
4323 int element = level->field[x][y];
4325 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4326 element <= EL_SP_GRAVITY_ON_PORT_UP)
4328 gravity_port_found = TRUE;
4329 gravity_port_valid = TRUE;
4330 gravity_port_flag = 1;
4331 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4333 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4334 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4336 gravity_port_found = TRUE;
4337 gravity_port_valid = TRUE;
4338 gravity_port_flag = 0;
4339 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4341 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4342 element <= EL_SP_GRAVITY_PORT_UP)
4344 // change R'n'D style gravity inverting special port to normal port
4345 // (there are no gravity inverting ports in native Supaplex engine)
4347 gravity_port_found = TRUE;
4348 gravity_port_valid = FALSE;
4349 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4352 if (gravity_port_found)
4354 if (gravity_port_valid &&
4355 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4357 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4359 port->PortLocation = (y * level->fieldx + x) * 2;
4360 port->Gravity = gravity_port_flag;
4362 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4364 header->SpecialPortCount++;
4368 // change special gravity port to normal port
4370 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4373 level_sp->playfield[x][y] = element - EL_SP_START;
4378 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4380 struct LevelInfo_SP *level_sp = level->native_sp_level;
4381 LevelInfoType *header = &level_sp->header;
4382 boolean num_invalid_elements = 0;
4385 level->fieldx = level_sp->width;
4386 level->fieldy = level_sp->height;
4388 for (x = 0; x < level->fieldx; x++)
4390 for (y = 0; y < level->fieldy; y++)
4392 int element_old = level_sp->playfield[x][y];
4393 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4395 if (element_new == EL_UNKNOWN)
4397 num_invalid_elements++;
4399 Debug("level:native:SP", "invalid element %d at position %d, %d",
4403 level->field[x][y] = element_new;
4407 if (num_invalid_elements > 0)
4408 Warn("found %d invalid elements%s", num_invalid_elements,
4409 (!options.debug ? " (use '--debug' for more details)" : ""));
4411 for (i = 0; i < MAX_PLAYERS; i++)
4412 level->initial_player_gravity[i] =
4413 (header->InitialGravity == 1 ? TRUE : FALSE);
4415 // skip leading spaces
4416 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4417 if (header->LevelTitle[i] != ' ')
4421 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4422 level->name[j] = header->LevelTitle[i];
4423 level->name[j] = '\0';
4425 // cut trailing spaces
4427 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4428 level->name[j - 1] = '\0';
4430 level->gems_needed = header->InfotronsNeeded;
4432 for (i = 0; i < header->SpecialPortCount; i++)
4434 SpecialPortType *port = &header->SpecialPort[i];
4435 int port_location = port->PortLocation;
4436 int gravity = port->Gravity;
4437 int port_x, port_y, port_element;
4439 port_x = (port_location / 2) % level->fieldx;
4440 port_y = (port_location / 2) / level->fieldx;
4442 if (port_x < 0 || port_x >= level->fieldx ||
4443 port_y < 0 || port_y >= level->fieldy)
4445 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4450 port_element = level->field[port_x][port_y];
4452 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4453 port_element > EL_SP_GRAVITY_PORT_UP)
4455 Warn("no special port at position (%d, %d)", port_x, port_y);
4460 // change previous (wrong) gravity inverting special port to either
4461 // gravity enabling special port or gravity disabling special port
4462 level->field[port_x][port_y] +=
4463 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4464 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4467 // change special gravity ports without database entries to normal ports
4468 for (x = 0; x < level->fieldx; x++)
4469 for (y = 0; y < level->fieldy; y++)
4470 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4471 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4472 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4474 level->time = 0; // no time limit
4475 level->amoeba_speed = 0;
4476 level->time_magic_wall = 0;
4477 level->time_wheel = 0;
4478 level->amoeba_content = EL_EMPTY;
4480 // original Supaplex does not use score values -- rate by playing time
4481 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4482 level->score[i] = 0;
4484 level->rate_time_over_score = TRUE;
4486 // there are no yamyams in supaplex levels
4487 for (i = 0; i < level->num_yamyam_contents; i++)
4488 for (x = 0; x < 3; x++)
4489 for (y = 0; y < 3; y++)
4490 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4493 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4495 struct LevelInfo_SP *level_sp = level->native_sp_level;
4496 struct DemoInfo_SP *demo = &level_sp->demo;
4499 // always start with reliable default values
4500 demo->is_available = FALSE;
4503 if (TAPE_IS_EMPTY(tape))
4506 demo->level_nr = tape.level_nr; // (currently not used)
4508 level_sp->header.DemoRandomSeed = tape.random_seed;
4512 for (i = 0; i < tape.length; i++)
4514 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4515 int demo_repeat = tape.pos[i].delay;
4516 int demo_entries = (demo_repeat + 15) / 16;
4518 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4520 Warn("tape truncated: size exceeds maximum SP demo size %d",
4526 for (j = 0; j < demo_repeat / 16; j++)
4527 demo->data[demo->length++] = 0xf0 | demo_action;
4529 if (demo_repeat % 16)
4530 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4533 demo->is_available = TRUE;
4536 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4538 struct LevelInfo_SP *level_sp = level->native_sp_level;
4539 struct DemoInfo_SP *demo = &level_sp->demo;
4540 char *filename = level->file_info.filename;
4543 // always start with reliable default values
4544 setTapeInfoToDefaults();
4546 if (!demo->is_available)
4549 tape.level_nr = demo->level_nr; // (currently not used)
4550 tape.random_seed = level_sp->header.DemoRandomSeed;
4552 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4555 tape.pos[tape.counter].delay = 0;
4557 for (i = 0; i < demo->length; i++)
4559 int demo_action = demo->data[i] & 0x0f;
4560 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4561 int tape_action = map_key_SP_to_RND(demo_action);
4562 int tape_repeat = demo_repeat + 1;
4563 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4564 boolean success = 0;
4567 for (j = 0; j < tape_repeat; j++)
4568 success = TapeAddAction(action);
4572 Warn("SP demo truncated: size exceeds maximum tape size %d",
4579 TapeHaltRecording();
4583 // ----------------------------------------------------------------------------
4584 // functions for loading MM level
4585 // ----------------------------------------------------------------------------
4587 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4589 struct LevelInfo_MM *level_mm = level->native_mm_level;
4592 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4593 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4595 level_mm->time = level->time;
4596 level_mm->kettles_needed = level->gems_needed;
4597 level_mm->auto_count_kettles = level->auto_count_gems;
4599 level_mm->mm_laser_red = level->mm_laser_red;
4600 level_mm->mm_laser_green = level->mm_laser_green;
4601 level_mm->mm_laser_blue = level->mm_laser_blue;
4603 level_mm->df_laser_red = level->df_laser_red;
4604 level_mm->df_laser_green = level->df_laser_green;
4605 level_mm->df_laser_blue = level->df_laser_blue;
4607 strcpy(level_mm->name, level->name);
4608 strcpy(level_mm->author, level->author);
4610 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4611 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4612 level_mm->score[SC_KEY] = level->score[SC_KEY];
4613 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4614 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4616 level_mm->amoeba_speed = level->amoeba_speed;
4617 level_mm->time_fuse = level->mm_time_fuse;
4618 level_mm->time_bomb = level->mm_time_bomb;
4619 level_mm->time_ball = level->mm_time_ball;
4620 level_mm->time_block = level->mm_time_block;
4622 level_mm->num_ball_contents = level->num_mm_ball_contents;
4623 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4624 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4625 level_mm->explode_ball = level->explode_mm_ball;
4627 for (i = 0; i < level->num_mm_ball_contents; i++)
4628 level_mm->ball_content[i] =
4629 map_element_RND_to_MM(level->mm_ball_content[i]);
4631 for (x = 0; x < level->fieldx; x++)
4632 for (y = 0; y < level->fieldy; y++)
4634 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4637 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4639 struct LevelInfo_MM *level_mm = level->native_mm_level;
4642 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4643 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4645 level->time = level_mm->time;
4646 level->gems_needed = level_mm->kettles_needed;
4647 level->auto_count_gems = level_mm->auto_count_kettles;
4649 level->mm_laser_red = level_mm->mm_laser_red;
4650 level->mm_laser_green = level_mm->mm_laser_green;
4651 level->mm_laser_blue = level_mm->mm_laser_blue;
4653 level->df_laser_red = level_mm->df_laser_red;
4654 level->df_laser_green = level_mm->df_laser_green;
4655 level->df_laser_blue = level_mm->df_laser_blue;
4657 strcpy(level->name, level_mm->name);
4659 // only overwrite author from 'levelinfo.conf' if author defined in level
4660 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4661 strcpy(level->author, level_mm->author);
4663 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4664 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4665 level->score[SC_KEY] = level_mm->score[SC_KEY];
4666 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4667 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4669 level->amoeba_speed = level_mm->amoeba_speed;
4670 level->mm_time_fuse = level_mm->time_fuse;
4671 level->mm_time_bomb = level_mm->time_bomb;
4672 level->mm_time_ball = level_mm->time_ball;
4673 level->mm_time_block = level_mm->time_block;
4675 level->num_mm_ball_contents = level_mm->num_ball_contents;
4676 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4677 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4678 level->explode_mm_ball = level_mm->explode_ball;
4680 for (i = 0; i < level->num_mm_ball_contents; i++)
4681 level->mm_ball_content[i] =
4682 map_element_MM_to_RND(level_mm->ball_content[i]);
4684 for (x = 0; x < level->fieldx; x++)
4685 for (y = 0; y < level->fieldy; y++)
4686 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4690 // ----------------------------------------------------------------------------
4691 // functions for loading DC level
4692 // ----------------------------------------------------------------------------
4694 #define DC_LEVEL_HEADER_SIZE 344
4696 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4699 static int last_data_encoded;
4703 int diff_hi, diff_lo;
4704 int data_hi, data_lo;
4705 unsigned short data_decoded;
4709 last_data_encoded = 0;
4716 diff = data_encoded - last_data_encoded;
4717 diff_hi = diff & ~0xff;
4718 diff_lo = diff & 0xff;
4722 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4723 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4724 data_hi = data_hi & 0xff00;
4726 data_decoded = data_hi | data_lo;
4728 last_data_encoded = data_encoded;
4730 offset1 = (offset1 + 1) % 31;
4731 offset2 = offset2 & 0xff;
4733 return data_decoded;
4736 static int getMappedElement_DC(int element)
4744 // 0x0117 - 0x036e: (?)
4747 // 0x042d - 0x0684: (?)
4763 element = EL_CRYSTAL;
4766 case 0x0e77: // quicksand (boulder)
4767 element = EL_QUICKSAND_FAST_FULL;
4770 case 0x0e99: // slow quicksand (boulder)
4771 element = EL_QUICKSAND_FULL;
4775 element = EL_EM_EXIT_OPEN;
4779 element = EL_EM_EXIT_CLOSED;
4783 element = EL_EM_STEEL_EXIT_OPEN;
4787 element = EL_EM_STEEL_EXIT_CLOSED;
4790 case 0x0f4f: // dynamite (lit 1)
4791 element = EL_EM_DYNAMITE_ACTIVE;
4794 case 0x0f57: // dynamite (lit 2)
4795 element = EL_EM_DYNAMITE_ACTIVE;
4798 case 0x0f5f: // dynamite (lit 3)
4799 element = EL_EM_DYNAMITE_ACTIVE;
4802 case 0x0f67: // dynamite (lit 4)
4803 element = EL_EM_DYNAMITE_ACTIVE;
4810 element = EL_AMOEBA_WET;
4814 element = EL_AMOEBA_DROP;
4818 element = EL_DC_MAGIC_WALL;
4822 element = EL_SPACESHIP_UP;
4826 element = EL_SPACESHIP_DOWN;
4830 element = EL_SPACESHIP_LEFT;
4834 element = EL_SPACESHIP_RIGHT;
4838 element = EL_BUG_UP;
4842 element = EL_BUG_DOWN;
4846 element = EL_BUG_LEFT;
4850 element = EL_BUG_RIGHT;
4854 element = EL_MOLE_UP;
4858 element = EL_MOLE_DOWN;
4862 element = EL_MOLE_LEFT;
4866 element = EL_MOLE_RIGHT;
4874 element = EL_YAMYAM_UP;
4878 element = EL_SWITCHGATE_OPEN;
4882 element = EL_SWITCHGATE_CLOSED;
4886 element = EL_DC_SWITCHGATE_SWITCH_UP;
4890 element = EL_TIMEGATE_CLOSED;
4893 case 0x144c: // conveyor belt switch (green)
4894 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4897 case 0x144f: // conveyor belt switch (red)
4898 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4901 case 0x1452: // conveyor belt switch (blue)
4902 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4906 element = EL_CONVEYOR_BELT_3_MIDDLE;
4910 element = EL_CONVEYOR_BELT_3_LEFT;
4914 element = EL_CONVEYOR_BELT_3_RIGHT;
4918 element = EL_CONVEYOR_BELT_1_MIDDLE;
4922 element = EL_CONVEYOR_BELT_1_LEFT;
4926 element = EL_CONVEYOR_BELT_1_RIGHT;
4930 element = EL_CONVEYOR_BELT_4_MIDDLE;
4934 element = EL_CONVEYOR_BELT_4_LEFT;
4938 element = EL_CONVEYOR_BELT_4_RIGHT;
4942 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4946 element = EL_EXPANDABLE_WALL_VERTICAL;
4950 element = EL_EXPANDABLE_WALL_ANY;
4953 case 0x14ce: // growing steel wall (left/right)
4954 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4957 case 0x14df: // growing steel wall (up/down)
4958 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4961 case 0x14e8: // growing steel wall (up/down/left/right)
4962 element = EL_EXPANDABLE_STEELWALL_ANY;
4966 element = EL_SHIELD_DEADLY;
4970 element = EL_EXTRA_TIME;
4978 element = EL_EMPTY_SPACE;
4981 case 0x1578: // quicksand (empty)
4982 element = EL_QUICKSAND_FAST_EMPTY;
4985 case 0x1579: // slow quicksand (empty)
4986 element = EL_QUICKSAND_EMPTY;
4996 element = EL_EM_DYNAMITE;
4999 case 0x15a1: // key (red)
5000 element = EL_EM_KEY_1;
5003 case 0x15a2: // key (yellow)
5004 element = EL_EM_KEY_2;
5007 case 0x15a3: // key (blue)
5008 element = EL_EM_KEY_4;
5011 case 0x15a4: // key (green)
5012 element = EL_EM_KEY_3;
5015 case 0x15a5: // key (white)
5016 element = EL_DC_KEY_WHITE;
5020 element = EL_WALL_SLIPPERY;
5027 case 0x15a8: // wall (not round)
5031 case 0x15a9: // (blue)
5032 element = EL_CHAR_A;
5035 case 0x15aa: // (blue)
5036 element = EL_CHAR_B;
5039 case 0x15ab: // (blue)
5040 element = EL_CHAR_C;
5043 case 0x15ac: // (blue)
5044 element = EL_CHAR_D;
5047 case 0x15ad: // (blue)
5048 element = EL_CHAR_E;
5051 case 0x15ae: // (blue)
5052 element = EL_CHAR_F;
5055 case 0x15af: // (blue)
5056 element = EL_CHAR_G;
5059 case 0x15b0: // (blue)
5060 element = EL_CHAR_H;
5063 case 0x15b1: // (blue)
5064 element = EL_CHAR_I;
5067 case 0x15b2: // (blue)
5068 element = EL_CHAR_J;
5071 case 0x15b3: // (blue)
5072 element = EL_CHAR_K;
5075 case 0x15b4: // (blue)
5076 element = EL_CHAR_L;
5079 case 0x15b5: // (blue)
5080 element = EL_CHAR_M;
5083 case 0x15b6: // (blue)
5084 element = EL_CHAR_N;
5087 case 0x15b7: // (blue)
5088 element = EL_CHAR_O;
5091 case 0x15b8: // (blue)
5092 element = EL_CHAR_P;
5095 case 0x15b9: // (blue)
5096 element = EL_CHAR_Q;
5099 case 0x15ba: // (blue)
5100 element = EL_CHAR_R;
5103 case 0x15bb: // (blue)
5104 element = EL_CHAR_S;
5107 case 0x15bc: // (blue)
5108 element = EL_CHAR_T;
5111 case 0x15bd: // (blue)
5112 element = EL_CHAR_U;
5115 case 0x15be: // (blue)
5116 element = EL_CHAR_V;
5119 case 0x15bf: // (blue)
5120 element = EL_CHAR_W;
5123 case 0x15c0: // (blue)
5124 element = EL_CHAR_X;
5127 case 0x15c1: // (blue)
5128 element = EL_CHAR_Y;
5131 case 0x15c2: // (blue)
5132 element = EL_CHAR_Z;
5135 case 0x15c3: // (blue)
5136 element = EL_CHAR_AUMLAUT;
5139 case 0x15c4: // (blue)
5140 element = EL_CHAR_OUMLAUT;
5143 case 0x15c5: // (blue)
5144 element = EL_CHAR_UUMLAUT;
5147 case 0x15c6: // (blue)
5148 element = EL_CHAR_0;
5151 case 0x15c7: // (blue)
5152 element = EL_CHAR_1;
5155 case 0x15c8: // (blue)
5156 element = EL_CHAR_2;
5159 case 0x15c9: // (blue)
5160 element = EL_CHAR_3;
5163 case 0x15ca: // (blue)
5164 element = EL_CHAR_4;
5167 case 0x15cb: // (blue)
5168 element = EL_CHAR_5;
5171 case 0x15cc: // (blue)
5172 element = EL_CHAR_6;
5175 case 0x15cd: // (blue)
5176 element = EL_CHAR_7;
5179 case 0x15ce: // (blue)
5180 element = EL_CHAR_8;
5183 case 0x15cf: // (blue)
5184 element = EL_CHAR_9;
5187 case 0x15d0: // (blue)
5188 element = EL_CHAR_PERIOD;
5191 case 0x15d1: // (blue)
5192 element = EL_CHAR_EXCLAM;
5195 case 0x15d2: // (blue)
5196 element = EL_CHAR_COLON;
5199 case 0x15d3: // (blue)
5200 element = EL_CHAR_LESS;
5203 case 0x15d4: // (blue)
5204 element = EL_CHAR_GREATER;
5207 case 0x15d5: // (blue)
5208 element = EL_CHAR_QUESTION;
5211 case 0x15d6: // (blue)
5212 element = EL_CHAR_COPYRIGHT;
5215 case 0x15d7: // (blue)
5216 element = EL_CHAR_UP;
5219 case 0x15d8: // (blue)
5220 element = EL_CHAR_DOWN;
5223 case 0x15d9: // (blue)
5224 element = EL_CHAR_BUTTON;
5227 case 0x15da: // (blue)
5228 element = EL_CHAR_PLUS;
5231 case 0x15db: // (blue)
5232 element = EL_CHAR_MINUS;
5235 case 0x15dc: // (blue)
5236 element = EL_CHAR_APOSTROPHE;
5239 case 0x15dd: // (blue)
5240 element = EL_CHAR_PARENLEFT;
5243 case 0x15de: // (blue)
5244 element = EL_CHAR_PARENRIGHT;
5247 case 0x15df: // (green)
5248 element = EL_CHAR_A;
5251 case 0x15e0: // (green)
5252 element = EL_CHAR_B;
5255 case 0x15e1: // (green)
5256 element = EL_CHAR_C;
5259 case 0x15e2: // (green)
5260 element = EL_CHAR_D;
5263 case 0x15e3: // (green)
5264 element = EL_CHAR_E;
5267 case 0x15e4: // (green)
5268 element = EL_CHAR_F;
5271 case 0x15e5: // (green)
5272 element = EL_CHAR_G;
5275 case 0x15e6: // (green)
5276 element = EL_CHAR_H;
5279 case 0x15e7: // (green)
5280 element = EL_CHAR_I;
5283 case 0x15e8: // (green)
5284 element = EL_CHAR_J;
5287 case 0x15e9: // (green)
5288 element = EL_CHAR_K;
5291 case 0x15ea: // (green)
5292 element = EL_CHAR_L;
5295 case 0x15eb: // (green)
5296 element = EL_CHAR_M;
5299 case 0x15ec: // (green)
5300 element = EL_CHAR_N;
5303 case 0x15ed: // (green)
5304 element = EL_CHAR_O;
5307 case 0x15ee: // (green)
5308 element = EL_CHAR_P;
5311 case 0x15ef: // (green)
5312 element = EL_CHAR_Q;
5315 case 0x15f0: // (green)
5316 element = EL_CHAR_R;
5319 case 0x15f1: // (green)
5320 element = EL_CHAR_S;
5323 case 0x15f2: // (green)
5324 element = EL_CHAR_T;
5327 case 0x15f3: // (green)
5328 element = EL_CHAR_U;
5331 case 0x15f4: // (green)
5332 element = EL_CHAR_V;
5335 case 0x15f5: // (green)
5336 element = EL_CHAR_W;
5339 case 0x15f6: // (green)
5340 element = EL_CHAR_X;
5343 case 0x15f7: // (green)
5344 element = EL_CHAR_Y;
5347 case 0x15f8: // (green)
5348 element = EL_CHAR_Z;
5351 case 0x15f9: // (green)
5352 element = EL_CHAR_AUMLAUT;
5355 case 0x15fa: // (green)
5356 element = EL_CHAR_OUMLAUT;
5359 case 0x15fb: // (green)
5360 element = EL_CHAR_UUMLAUT;
5363 case 0x15fc: // (green)
5364 element = EL_CHAR_0;
5367 case 0x15fd: // (green)
5368 element = EL_CHAR_1;
5371 case 0x15fe: // (green)
5372 element = EL_CHAR_2;
5375 case 0x15ff: // (green)
5376 element = EL_CHAR_3;
5379 case 0x1600: // (green)
5380 element = EL_CHAR_4;
5383 case 0x1601: // (green)
5384 element = EL_CHAR_5;
5387 case 0x1602: // (green)
5388 element = EL_CHAR_6;
5391 case 0x1603: // (green)
5392 element = EL_CHAR_7;
5395 case 0x1604: // (green)
5396 element = EL_CHAR_8;
5399 case 0x1605: // (green)
5400 element = EL_CHAR_9;
5403 case 0x1606: // (green)
5404 element = EL_CHAR_PERIOD;
5407 case 0x1607: // (green)
5408 element = EL_CHAR_EXCLAM;
5411 case 0x1608: // (green)
5412 element = EL_CHAR_COLON;
5415 case 0x1609: // (green)
5416 element = EL_CHAR_LESS;
5419 case 0x160a: // (green)
5420 element = EL_CHAR_GREATER;
5423 case 0x160b: // (green)
5424 element = EL_CHAR_QUESTION;
5427 case 0x160c: // (green)
5428 element = EL_CHAR_COPYRIGHT;
5431 case 0x160d: // (green)
5432 element = EL_CHAR_UP;
5435 case 0x160e: // (green)
5436 element = EL_CHAR_DOWN;
5439 case 0x160f: // (green)
5440 element = EL_CHAR_BUTTON;
5443 case 0x1610: // (green)
5444 element = EL_CHAR_PLUS;
5447 case 0x1611: // (green)
5448 element = EL_CHAR_MINUS;
5451 case 0x1612: // (green)
5452 element = EL_CHAR_APOSTROPHE;
5455 case 0x1613: // (green)
5456 element = EL_CHAR_PARENLEFT;
5459 case 0x1614: // (green)
5460 element = EL_CHAR_PARENRIGHT;
5463 case 0x1615: // (blue steel)
5464 element = EL_STEEL_CHAR_A;
5467 case 0x1616: // (blue steel)
5468 element = EL_STEEL_CHAR_B;
5471 case 0x1617: // (blue steel)
5472 element = EL_STEEL_CHAR_C;
5475 case 0x1618: // (blue steel)
5476 element = EL_STEEL_CHAR_D;
5479 case 0x1619: // (blue steel)
5480 element = EL_STEEL_CHAR_E;
5483 case 0x161a: // (blue steel)
5484 element = EL_STEEL_CHAR_F;
5487 case 0x161b: // (blue steel)
5488 element = EL_STEEL_CHAR_G;
5491 case 0x161c: // (blue steel)
5492 element = EL_STEEL_CHAR_H;
5495 case 0x161d: // (blue steel)
5496 element = EL_STEEL_CHAR_I;
5499 case 0x161e: // (blue steel)
5500 element = EL_STEEL_CHAR_J;
5503 case 0x161f: // (blue steel)
5504 element = EL_STEEL_CHAR_K;
5507 case 0x1620: // (blue steel)
5508 element = EL_STEEL_CHAR_L;
5511 case 0x1621: // (blue steel)
5512 element = EL_STEEL_CHAR_M;
5515 case 0x1622: // (blue steel)
5516 element = EL_STEEL_CHAR_N;
5519 case 0x1623: // (blue steel)
5520 element = EL_STEEL_CHAR_O;
5523 case 0x1624: // (blue steel)
5524 element = EL_STEEL_CHAR_P;
5527 case 0x1625: // (blue steel)
5528 element = EL_STEEL_CHAR_Q;
5531 case 0x1626: // (blue steel)
5532 element = EL_STEEL_CHAR_R;
5535 case 0x1627: // (blue steel)
5536 element = EL_STEEL_CHAR_S;
5539 case 0x1628: // (blue steel)
5540 element = EL_STEEL_CHAR_T;
5543 case 0x1629: // (blue steel)
5544 element = EL_STEEL_CHAR_U;
5547 case 0x162a: // (blue steel)
5548 element = EL_STEEL_CHAR_V;
5551 case 0x162b: // (blue steel)
5552 element = EL_STEEL_CHAR_W;
5555 case 0x162c: // (blue steel)
5556 element = EL_STEEL_CHAR_X;
5559 case 0x162d: // (blue steel)
5560 element = EL_STEEL_CHAR_Y;
5563 case 0x162e: // (blue steel)
5564 element = EL_STEEL_CHAR_Z;
5567 case 0x162f: // (blue steel)
5568 element = EL_STEEL_CHAR_AUMLAUT;
5571 case 0x1630: // (blue steel)
5572 element = EL_STEEL_CHAR_OUMLAUT;
5575 case 0x1631: // (blue steel)
5576 element = EL_STEEL_CHAR_UUMLAUT;
5579 case 0x1632: // (blue steel)
5580 element = EL_STEEL_CHAR_0;
5583 case 0x1633: // (blue steel)
5584 element = EL_STEEL_CHAR_1;
5587 case 0x1634: // (blue steel)
5588 element = EL_STEEL_CHAR_2;
5591 case 0x1635: // (blue steel)
5592 element = EL_STEEL_CHAR_3;
5595 case 0x1636: // (blue steel)
5596 element = EL_STEEL_CHAR_4;
5599 case 0x1637: // (blue steel)
5600 element = EL_STEEL_CHAR_5;
5603 case 0x1638: // (blue steel)
5604 element = EL_STEEL_CHAR_6;
5607 case 0x1639: // (blue steel)
5608 element = EL_STEEL_CHAR_7;
5611 case 0x163a: // (blue steel)
5612 element = EL_STEEL_CHAR_8;
5615 case 0x163b: // (blue steel)
5616 element = EL_STEEL_CHAR_9;
5619 case 0x163c: // (blue steel)
5620 element = EL_STEEL_CHAR_PERIOD;
5623 case 0x163d: // (blue steel)
5624 element = EL_STEEL_CHAR_EXCLAM;
5627 case 0x163e: // (blue steel)
5628 element = EL_STEEL_CHAR_COLON;
5631 case 0x163f: // (blue steel)
5632 element = EL_STEEL_CHAR_LESS;
5635 case 0x1640: // (blue steel)
5636 element = EL_STEEL_CHAR_GREATER;
5639 case 0x1641: // (blue steel)
5640 element = EL_STEEL_CHAR_QUESTION;
5643 case 0x1642: // (blue steel)
5644 element = EL_STEEL_CHAR_COPYRIGHT;
5647 case 0x1643: // (blue steel)
5648 element = EL_STEEL_CHAR_UP;
5651 case 0x1644: // (blue steel)
5652 element = EL_STEEL_CHAR_DOWN;
5655 case 0x1645: // (blue steel)
5656 element = EL_STEEL_CHAR_BUTTON;
5659 case 0x1646: // (blue steel)
5660 element = EL_STEEL_CHAR_PLUS;
5663 case 0x1647: // (blue steel)
5664 element = EL_STEEL_CHAR_MINUS;
5667 case 0x1648: // (blue steel)
5668 element = EL_STEEL_CHAR_APOSTROPHE;
5671 case 0x1649: // (blue steel)
5672 element = EL_STEEL_CHAR_PARENLEFT;
5675 case 0x164a: // (blue steel)
5676 element = EL_STEEL_CHAR_PARENRIGHT;
5679 case 0x164b: // (green steel)
5680 element = EL_STEEL_CHAR_A;
5683 case 0x164c: // (green steel)
5684 element = EL_STEEL_CHAR_B;
5687 case 0x164d: // (green steel)
5688 element = EL_STEEL_CHAR_C;
5691 case 0x164e: // (green steel)
5692 element = EL_STEEL_CHAR_D;
5695 case 0x164f: // (green steel)
5696 element = EL_STEEL_CHAR_E;
5699 case 0x1650: // (green steel)
5700 element = EL_STEEL_CHAR_F;
5703 case 0x1651: // (green steel)
5704 element = EL_STEEL_CHAR_G;
5707 case 0x1652: // (green steel)
5708 element = EL_STEEL_CHAR_H;
5711 case 0x1653: // (green steel)
5712 element = EL_STEEL_CHAR_I;
5715 case 0x1654: // (green steel)
5716 element = EL_STEEL_CHAR_J;
5719 case 0x1655: // (green steel)
5720 element = EL_STEEL_CHAR_K;
5723 case 0x1656: // (green steel)
5724 element = EL_STEEL_CHAR_L;
5727 case 0x1657: // (green steel)
5728 element = EL_STEEL_CHAR_M;
5731 case 0x1658: // (green steel)
5732 element = EL_STEEL_CHAR_N;
5735 case 0x1659: // (green steel)
5736 element = EL_STEEL_CHAR_O;
5739 case 0x165a: // (green steel)
5740 element = EL_STEEL_CHAR_P;
5743 case 0x165b: // (green steel)
5744 element = EL_STEEL_CHAR_Q;
5747 case 0x165c: // (green steel)
5748 element = EL_STEEL_CHAR_R;
5751 case 0x165d: // (green steel)
5752 element = EL_STEEL_CHAR_S;
5755 case 0x165e: // (green steel)
5756 element = EL_STEEL_CHAR_T;
5759 case 0x165f: // (green steel)
5760 element = EL_STEEL_CHAR_U;
5763 case 0x1660: // (green steel)
5764 element = EL_STEEL_CHAR_V;
5767 case 0x1661: // (green steel)
5768 element = EL_STEEL_CHAR_W;
5771 case 0x1662: // (green steel)
5772 element = EL_STEEL_CHAR_X;
5775 case 0x1663: // (green steel)
5776 element = EL_STEEL_CHAR_Y;
5779 case 0x1664: // (green steel)
5780 element = EL_STEEL_CHAR_Z;
5783 case 0x1665: // (green steel)
5784 element = EL_STEEL_CHAR_AUMLAUT;
5787 case 0x1666: // (green steel)
5788 element = EL_STEEL_CHAR_OUMLAUT;
5791 case 0x1667: // (green steel)
5792 element = EL_STEEL_CHAR_UUMLAUT;
5795 case 0x1668: // (green steel)
5796 element = EL_STEEL_CHAR_0;
5799 case 0x1669: // (green steel)
5800 element = EL_STEEL_CHAR_1;
5803 case 0x166a: // (green steel)
5804 element = EL_STEEL_CHAR_2;
5807 case 0x166b: // (green steel)
5808 element = EL_STEEL_CHAR_3;
5811 case 0x166c: // (green steel)
5812 element = EL_STEEL_CHAR_4;
5815 case 0x166d: // (green steel)
5816 element = EL_STEEL_CHAR_5;
5819 case 0x166e: // (green steel)
5820 element = EL_STEEL_CHAR_6;
5823 case 0x166f: // (green steel)
5824 element = EL_STEEL_CHAR_7;
5827 case 0x1670: // (green steel)
5828 element = EL_STEEL_CHAR_8;
5831 case 0x1671: // (green steel)
5832 element = EL_STEEL_CHAR_9;
5835 case 0x1672: // (green steel)
5836 element = EL_STEEL_CHAR_PERIOD;
5839 case 0x1673: // (green steel)
5840 element = EL_STEEL_CHAR_EXCLAM;
5843 case 0x1674: // (green steel)
5844 element = EL_STEEL_CHAR_COLON;
5847 case 0x1675: // (green steel)
5848 element = EL_STEEL_CHAR_LESS;
5851 case 0x1676: // (green steel)
5852 element = EL_STEEL_CHAR_GREATER;
5855 case 0x1677: // (green steel)
5856 element = EL_STEEL_CHAR_QUESTION;
5859 case 0x1678: // (green steel)
5860 element = EL_STEEL_CHAR_COPYRIGHT;
5863 case 0x1679: // (green steel)
5864 element = EL_STEEL_CHAR_UP;
5867 case 0x167a: // (green steel)
5868 element = EL_STEEL_CHAR_DOWN;
5871 case 0x167b: // (green steel)
5872 element = EL_STEEL_CHAR_BUTTON;
5875 case 0x167c: // (green steel)
5876 element = EL_STEEL_CHAR_PLUS;
5879 case 0x167d: // (green steel)
5880 element = EL_STEEL_CHAR_MINUS;
5883 case 0x167e: // (green steel)
5884 element = EL_STEEL_CHAR_APOSTROPHE;
5887 case 0x167f: // (green steel)
5888 element = EL_STEEL_CHAR_PARENLEFT;
5891 case 0x1680: // (green steel)
5892 element = EL_STEEL_CHAR_PARENRIGHT;
5895 case 0x1681: // gate (red)
5896 element = EL_EM_GATE_1;
5899 case 0x1682: // secret gate (red)
5900 element = EL_EM_GATE_1_GRAY;
5903 case 0x1683: // gate (yellow)
5904 element = EL_EM_GATE_2;
5907 case 0x1684: // secret gate (yellow)
5908 element = EL_EM_GATE_2_GRAY;
5911 case 0x1685: // gate (blue)
5912 element = EL_EM_GATE_4;
5915 case 0x1686: // secret gate (blue)
5916 element = EL_EM_GATE_4_GRAY;
5919 case 0x1687: // gate (green)
5920 element = EL_EM_GATE_3;
5923 case 0x1688: // secret gate (green)
5924 element = EL_EM_GATE_3_GRAY;
5927 case 0x1689: // gate (white)
5928 element = EL_DC_GATE_WHITE;
5931 case 0x168a: // secret gate (white)
5932 element = EL_DC_GATE_WHITE_GRAY;
5935 case 0x168b: // secret gate (no key)
5936 element = EL_DC_GATE_FAKE_GRAY;
5940 element = EL_ROBOT_WHEEL;
5944 element = EL_DC_TIMEGATE_SWITCH;
5948 element = EL_ACID_POOL_BOTTOM;
5952 element = EL_ACID_POOL_TOPLEFT;
5956 element = EL_ACID_POOL_TOPRIGHT;
5960 element = EL_ACID_POOL_BOTTOMLEFT;
5964 element = EL_ACID_POOL_BOTTOMRIGHT;
5968 element = EL_STEELWALL;
5972 element = EL_STEELWALL_SLIPPERY;
5975 case 0x1695: // steel wall (not round)
5976 element = EL_STEELWALL;
5979 case 0x1696: // steel wall (left)
5980 element = EL_DC_STEELWALL_1_LEFT;
5983 case 0x1697: // steel wall (bottom)
5984 element = EL_DC_STEELWALL_1_BOTTOM;
5987 case 0x1698: // steel wall (right)
5988 element = EL_DC_STEELWALL_1_RIGHT;
5991 case 0x1699: // steel wall (top)
5992 element = EL_DC_STEELWALL_1_TOP;
5995 case 0x169a: // steel wall (left/bottom)
5996 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5999 case 0x169b: // steel wall (right/bottom)
6000 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6003 case 0x169c: // steel wall (right/top)
6004 element = EL_DC_STEELWALL_1_TOPRIGHT;
6007 case 0x169d: // steel wall (left/top)
6008 element = EL_DC_STEELWALL_1_TOPLEFT;
6011 case 0x169e: // steel wall (right/bottom small)
6012 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6015 case 0x169f: // steel wall (left/bottom small)
6016 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6019 case 0x16a0: // steel wall (right/top small)
6020 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6023 case 0x16a1: // steel wall (left/top small)
6024 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6027 case 0x16a2: // steel wall (left/right)
6028 element = EL_DC_STEELWALL_1_VERTICAL;
6031 case 0x16a3: // steel wall (top/bottom)
6032 element = EL_DC_STEELWALL_1_HORIZONTAL;
6035 case 0x16a4: // steel wall 2 (left end)
6036 element = EL_DC_STEELWALL_2_LEFT;
6039 case 0x16a5: // steel wall 2 (right end)
6040 element = EL_DC_STEELWALL_2_RIGHT;
6043 case 0x16a6: // steel wall 2 (top end)
6044 element = EL_DC_STEELWALL_2_TOP;
6047 case 0x16a7: // steel wall 2 (bottom end)
6048 element = EL_DC_STEELWALL_2_BOTTOM;
6051 case 0x16a8: // steel wall 2 (left/right)
6052 element = EL_DC_STEELWALL_2_HORIZONTAL;
6055 case 0x16a9: // steel wall 2 (up/down)
6056 element = EL_DC_STEELWALL_2_VERTICAL;
6059 case 0x16aa: // steel wall 2 (mid)
6060 element = EL_DC_STEELWALL_2_MIDDLE;
6064 element = EL_SIGN_EXCLAMATION;
6068 element = EL_SIGN_RADIOACTIVITY;
6072 element = EL_SIGN_STOP;
6076 element = EL_SIGN_WHEELCHAIR;
6080 element = EL_SIGN_PARKING;
6084 element = EL_SIGN_NO_ENTRY;
6088 element = EL_SIGN_HEART;
6092 element = EL_SIGN_GIVE_WAY;
6096 element = EL_SIGN_ENTRY_FORBIDDEN;
6100 element = EL_SIGN_EMERGENCY_EXIT;
6104 element = EL_SIGN_YIN_YANG;
6108 element = EL_WALL_EMERALD;
6112 element = EL_WALL_DIAMOND;
6116 element = EL_WALL_PEARL;
6120 element = EL_WALL_CRYSTAL;
6124 element = EL_INVISIBLE_WALL;
6128 element = EL_INVISIBLE_STEELWALL;
6132 // EL_INVISIBLE_SAND
6135 element = EL_LIGHT_SWITCH;
6139 element = EL_ENVELOPE_1;
6143 if (element >= 0x0117 && element <= 0x036e) // (?)
6144 element = EL_DIAMOND;
6145 else if (element >= 0x042d && element <= 0x0684) // (?)
6146 element = EL_EMERALD;
6147 else if (element >= 0x157c && element <= 0x158b)
6149 else if (element >= 0x1590 && element <= 0x159f)
6150 element = EL_DC_LANDMINE;
6151 else if (element >= 0x16bc && element <= 0x16cb)
6152 element = EL_INVISIBLE_SAND;
6155 Warn("unknown Diamond Caves element 0x%04x", element);
6157 element = EL_UNKNOWN;
6162 return getMappedElement(element);
6165 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6167 byte header[DC_LEVEL_HEADER_SIZE];
6169 int envelope_header_pos = 62;
6170 int envelope_content_pos = 94;
6171 int level_name_pos = 251;
6172 int level_author_pos = 292;
6173 int envelope_header_len;
6174 int envelope_content_len;
6176 int level_author_len;
6178 int num_yamyam_contents;
6181 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6183 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6185 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6187 header[i * 2 + 0] = header_word >> 8;
6188 header[i * 2 + 1] = header_word & 0xff;
6191 // read some values from level header to check level decoding integrity
6192 fieldx = header[6] | (header[7] << 8);
6193 fieldy = header[8] | (header[9] << 8);
6194 num_yamyam_contents = header[60] | (header[61] << 8);
6196 // do some simple sanity checks to ensure that level was correctly decoded
6197 if (fieldx < 1 || fieldx > 256 ||
6198 fieldy < 1 || fieldy > 256 ||
6199 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6201 level->no_valid_file = TRUE;
6203 Warn("cannot decode level from stream -- using empty level");
6208 // maximum envelope header size is 31 bytes
6209 envelope_header_len = header[envelope_header_pos];
6210 // maximum envelope content size is 110 (156?) bytes
6211 envelope_content_len = header[envelope_content_pos];
6213 // maximum level title size is 40 bytes
6214 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6215 // maximum level author size is 30 (51?) bytes
6216 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6220 for (i = 0; i < envelope_header_len; i++)
6221 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6222 level->envelope[0].text[envelope_size++] =
6223 header[envelope_header_pos + 1 + i];
6225 if (envelope_header_len > 0 && envelope_content_len > 0)
6227 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6228 level->envelope[0].text[envelope_size++] = '\n';
6229 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6230 level->envelope[0].text[envelope_size++] = '\n';
6233 for (i = 0; i < envelope_content_len; i++)
6234 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6235 level->envelope[0].text[envelope_size++] =
6236 header[envelope_content_pos + 1 + i];
6238 level->envelope[0].text[envelope_size] = '\0';
6240 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6241 level->envelope[0].ysize = 10;
6242 level->envelope[0].autowrap = TRUE;
6243 level->envelope[0].centered = TRUE;
6245 for (i = 0; i < level_name_len; i++)
6246 level->name[i] = header[level_name_pos + 1 + i];
6247 level->name[level_name_len] = '\0';
6249 for (i = 0; i < level_author_len; i++)
6250 level->author[i] = header[level_author_pos + 1 + i];
6251 level->author[level_author_len] = '\0';
6253 num_yamyam_contents = header[60] | (header[61] << 8);
6254 level->num_yamyam_contents =
6255 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6257 for (i = 0; i < num_yamyam_contents; i++)
6259 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6261 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6262 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6264 if (i < MAX_ELEMENT_CONTENTS)
6265 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6269 fieldx = header[6] | (header[7] << 8);
6270 fieldy = header[8] | (header[9] << 8);
6271 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6272 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6274 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6276 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6277 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6279 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6280 level->field[x][y] = getMappedElement_DC(element_dc);
6283 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6284 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6285 level->field[x][y] = EL_PLAYER_1;
6287 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6288 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6289 level->field[x][y] = EL_PLAYER_2;
6291 level->gems_needed = header[18] | (header[19] << 8);
6293 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6294 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6295 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6296 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6297 level->score[SC_NUT] = header[28] | (header[29] << 8);
6298 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6299 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6300 level->score[SC_BUG] = header[34] | (header[35] << 8);
6301 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6302 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6303 level->score[SC_KEY] = header[40] | (header[41] << 8);
6304 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6306 level->time = header[44] | (header[45] << 8);
6308 level->amoeba_speed = header[46] | (header[47] << 8);
6309 level->time_light = header[48] | (header[49] << 8);
6310 level->time_timegate = header[50] | (header[51] << 8);
6311 level->time_wheel = header[52] | (header[53] << 8);
6312 level->time_magic_wall = header[54] | (header[55] << 8);
6313 level->extra_time = header[56] | (header[57] << 8);
6314 level->shield_normal_time = header[58] | (header[59] << 8);
6316 // shield and extra time elements do not have a score
6317 level->score[SC_SHIELD] = 0;
6318 level->extra_time_score = 0;
6320 // set time for normal and deadly shields to the same value
6321 level->shield_deadly_time = level->shield_normal_time;
6323 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6324 // can slip down from flat walls, like normal walls and steel walls
6325 level->em_slippery_gems = TRUE;
6327 // time score is counted for each 10 seconds left in Diamond Caves levels
6328 level->time_score_base = 10;
6331 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6332 struct LevelFileInfo *level_file_info,
6333 boolean level_info_only)
6335 char *filename = level_file_info->filename;
6337 int num_magic_bytes = 8;
6338 char magic_bytes[num_magic_bytes + 1];
6339 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6341 if (!(file = openFile(filename, MODE_READ)))
6343 level->no_valid_file = TRUE;
6345 if (!level_info_only)
6346 Warn("cannot read level '%s' -- using empty level", filename);
6351 // fseek(file, 0x0000, SEEK_SET);
6353 if (level_file_info->packed)
6355 // read "magic bytes" from start of file
6356 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6357 magic_bytes[0] = '\0';
6359 // check "magic bytes" for correct file format
6360 if (!strPrefix(magic_bytes, "DC2"))
6362 level->no_valid_file = TRUE;
6364 Warn("unknown DC level file '%s' -- using empty level", filename);
6369 if (strPrefix(magic_bytes, "DC2Win95") ||
6370 strPrefix(magic_bytes, "DC2Win98"))
6372 int position_first_level = 0x00fa;
6373 int extra_bytes = 4;
6376 // advance file stream to first level inside the level package
6377 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6379 // each block of level data is followed by block of non-level data
6380 num_levels_to_skip *= 2;
6382 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6383 while (num_levels_to_skip >= 0)
6385 // advance file stream to next level inside the level package
6386 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6388 level->no_valid_file = TRUE;
6390 Warn("cannot fseek in file '%s' -- using empty level", filename);
6395 // skip apparently unused extra bytes following each level
6396 ReadUnusedBytesFromFile(file, extra_bytes);
6398 // read size of next level in level package
6399 skip_bytes = getFile32BitLE(file);
6401 num_levels_to_skip--;
6406 level->no_valid_file = TRUE;
6408 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6414 LoadLevelFromFileStream_DC(file, level);
6420 // ----------------------------------------------------------------------------
6421 // functions for loading SB level
6422 // ----------------------------------------------------------------------------
6424 int getMappedElement_SB(int element_ascii, boolean use_ces)
6432 sb_element_mapping[] =
6434 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6435 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6436 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6437 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6438 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6439 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6440 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6441 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6448 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6449 if (element_ascii == sb_element_mapping[i].ascii)
6450 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6452 return EL_UNDEFINED;
6455 static void SetLevelSettings_SB(struct LevelInfo *level)
6459 level->use_step_counter = TRUE;
6462 level->score[SC_TIME_BONUS] = 0;
6463 level->time_score_base = 1;
6464 level->rate_time_over_score = TRUE;
6467 level->auto_exit_sokoban = TRUE;
6470 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6471 struct LevelFileInfo *level_file_info,
6472 boolean level_info_only)
6474 char *filename = level_file_info->filename;
6475 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6476 char last_comment[MAX_LINE_LEN];
6477 char level_name[MAX_LINE_LEN];
6480 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6481 boolean read_continued_line = FALSE;
6482 boolean reading_playfield = FALSE;
6483 boolean got_valid_playfield_line = FALSE;
6484 boolean invalid_playfield_char = FALSE;
6485 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6486 int file_level_nr = 0;
6487 int x = 0, y = 0; // initialized to make compilers happy
6489 last_comment[0] = '\0';
6490 level_name[0] = '\0';
6492 if (!(file = openFile(filename, MODE_READ)))
6494 level->no_valid_file = TRUE;
6496 if (!level_info_only)
6497 Warn("cannot read level '%s' -- using empty level", filename);
6502 while (!checkEndOfFile(file))
6504 // level successfully read, but next level may follow here
6505 if (!got_valid_playfield_line && reading_playfield)
6507 // read playfield from single level file -- skip remaining file
6508 if (!level_file_info->packed)
6511 if (file_level_nr >= num_levels_to_skip)
6516 last_comment[0] = '\0';
6517 level_name[0] = '\0';
6519 reading_playfield = FALSE;
6522 got_valid_playfield_line = FALSE;
6524 // read next line of input file
6525 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6528 // cut trailing line break (this can be newline and/or carriage return)
6529 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6530 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6533 // copy raw input line for later use (mainly debugging output)
6534 strcpy(line_raw, line);
6536 if (read_continued_line)
6538 // append new line to existing line, if there is enough space
6539 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6540 strcat(previous_line, line_ptr);
6542 strcpy(line, previous_line); // copy storage buffer to line
6544 read_continued_line = FALSE;
6547 // if the last character is '\', continue at next line
6548 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6550 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6551 strcpy(previous_line, line); // copy line to storage buffer
6553 read_continued_line = TRUE;
6559 if (line[0] == '\0')
6562 // extract comment text from comment line
6565 for (line_ptr = line; *line_ptr; line_ptr++)
6566 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6569 strcpy(last_comment, line_ptr);
6574 // extract level title text from line containing level title
6575 if (line[0] == '\'')
6577 strcpy(level_name, &line[1]);
6579 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6580 level_name[strlen(level_name) - 1] = '\0';
6585 // skip lines containing only spaces (or empty lines)
6586 for (line_ptr = line; *line_ptr; line_ptr++)
6587 if (*line_ptr != ' ')
6589 if (*line_ptr == '\0')
6592 // at this point, we have found a line containing part of a playfield
6594 got_valid_playfield_line = TRUE;
6596 if (!reading_playfield)
6598 reading_playfield = TRUE;
6599 invalid_playfield_char = FALSE;
6601 for (x = 0; x < MAX_LEV_FIELDX; x++)
6602 for (y = 0; y < MAX_LEV_FIELDY; y++)
6603 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6608 // start with topmost tile row
6612 // skip playfield line if larger row than allowed
6613 if (y >= MAX_LEV_FIELDY)
6616 // start with leftmost tile column
6619 // read playfield elements from line
6620 for (line_ptr = line; *line_ptr; line_ptr++)
6622 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6624 // stop parsing playfield line if larger column than allowed
6625 if (x >= MAX_LEV_FIELDX)
6628 if (mapped_sb_element == EL_UNDEFINED)
6630 invalid_playfield_char = TRUE;
6635 level->field[x][y] = mapped_sb_element;
6637 // continue with next tile column
6640 level->fieldx = MAX(x, level->fieldx);
6643 if (invalid_playfield_char)
6645 // if first playfield line, treat invalid lines as comment lines
6647 reading_playfield = FALSE;
6652 // continue with next tile row
6660 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6661 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6663 if (!reading_playfield)
6665 level->no_valid_file = TRUE;
6667 Warn("cannot read level '%s' -- using empty level", filename);
6672 if (*level_name != '\0')
6674 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6675 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6677 else if (*last_comment != '\0')
6679 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6680 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6684 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6687 // set all empty fields beyond the border walls to invisible steel wall
6688 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6690 if ((x == 0 || x == level->fieldx - 1 ||
6691 y == 0 || y == level->fieldy - 1) &&
6692 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6693 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6694 level->field, level->fieldx, level->fieldy);
6697 // set special level settings for Sokoban levels
6698 SetLevelSettings_SB(level);
6700 if (load_xsb_to_ces)
6702 // special global settings can now be set in level template
6703 level->use_custom_template = TRUE;
6708 // -------------------------------------------------------------------------
6709 // functions for handling native levels
6710 // -------------------------------------------------------------------------
6712 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6713 struct LevelFileInfo *level_file_info,
6714 boolean level_info_only)
6718 // determine position of requested level inside level package
6719 if (level_file_info->packed)
6720 pos = level_file_info->nr - leveldir_current->first_level;
6722 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6723 level->no_valid_file = TRUE;
6726 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6727 struct LevelFileInfo *level_file_info,
6728 boolean level_info_only)
6730 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6731 level->no_valid_file = TRUE;
6734 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6735 struct LevelFileInfo *level_file_info,
6736 boolean level_info_only)
6740 // determine position of requested level inside level package
6741 if (level_file_info->packed)
6742 pos = level_file_info->nr - leveldir_current->first_level;
6744 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6745 level->no_valid_file = TRUE;
6748 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6749 struct LevelFileInfo *level_file_info,
6750 boolean level_info_only)
6752 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6753 level->no_valid_file = TRUE;
6756 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6758 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6759 CopyNativeLevel_RND_to_BD(level);
6760 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6761 CopyNativeLevel_RND_to_EM(level);
6762 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6763 CopyNativeLevel_RND_to_SP(level);
6764 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6765 CopyNativeLevel_RND_to_MM(level);
6768 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6770 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6771 CopyNativeLevel_BD_to_RND(level);
6772 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6773 CopyNativeLevel_EM_to_RND(level);
6774 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6775 CopyNativeLevel_SP_to_RND(level);
6776 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6777 CopyNativeLevel_MM_to_RND(level);
6780 void SaveNativeLevel(struct LevelInfo *level)
6782 // saving native level files only supported for some game engines
6783 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6784 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6787 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6788 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6789 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6790 char *filename = getLevelFilenameFromBasename(basename);
6792 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6795 boolean success = FALSE;
6797 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6799 CopyNativeLevel_RND_to_BD(level);
6800 // CopyNativeTape_RND_to_BD(level);
6802 success = SaveNativeLevel_BD(filename);
6804 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6806 CopyNativeLevel_RND_to_SP(level);
6807 CopyNativeTape_RND_to_SP(level);
6809 success = SaveNativeLevel_SP(filename);
6813 Request("Native level file saved!", REQ_CONFIRM);
6815 Request("Failed to save native level file!", REQ_CONFIRM);
6819 // ----------------------------------------------------------------------------
6820 // functions for loading generic level
6821 // ----------------------------------------------------------------------------
6823 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6824 struct LevelFileInfo *level_file_info,
6825 boolean level_info_only)
6827 // always start with reliable default values
6828 setLevelInfoToDefaults(level, level_info_only, TRUE);
6830 switch (level_file_info->type)
6832 case LEVEL_FILE_TYPE_RND:
6833 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6836 case LEVEL_FILE_TYPE_BD:
6837 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6838 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6841 case LEVEL_FILE_TYPE_EM:
6842 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6843 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6846 case LEVEL_FILE_TYPE_SP:
6847 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6848 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6851 case LEVEL_FILE_TYPE_MM:
6852 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6853 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6856 case LEVEL_FILE_TYPE_DC:
6857 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6860 case LEVEL_FILE_TYPE_SB:
6861 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6865 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6869 // if level file is invalid, restore level structure to default values
6870 if (level->no_valid_file)
6871 setLevelInfoToDefaults(level, level_info_only, FALSE);
6873 if (check_special_flags("use_native_bd_game_engine"))
6874 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6876 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6877 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6879 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6880 CopyNativeLevel_Native_to_RND(level);
6883 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6885 static struct LevelFileInfo level_file_info;
6887 // always start with reliable default values
6888 setFileInfoToDefaults(&level_file_info);
6890 level_file_info.nr = 0; // unknown level number
6891 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6893 setString(&level_file_info.filename, filename);
6895 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6898 static void LoadLevel_InitVersion(struct LevelInfo *level)
6902 if (leveldir_current == NULL) // only when dumping level
6905 // all engine modifications also valid for levels which use latest engine
6906 if (level->game_version < VERSION_IDENT(3,2,0,5))
6908 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6909 level->time_score_base = 10;
6912 if (leveldir_current->latest_engine)
6914 // ---------- use latest game engine --------------------------------------
6916 /* For all levels which are forced to use the latest game engine version
6917 (normally all but user contributed, private and undefined levels), set
6918 the game engine version to the actual version; this allows for actual
6919 corrections in the game engine to take effect for existing, converted
6920 levels (from "classic" or other existing games) to make the emulation
6921 of the corresponding game more accurate, while (hopefully) not breaking
6922 existing levels created from other players. */
6924 level->game_version = GAME_VERSION_ACTUAL;
6926 /* Set special EM style gems behaviour: EM style gems slip down from
6927 normal, steel and growing wall. As this is a more fundamental change,
6928 it seems better to set the default behaviour to "off" (as it is more
6929 natural) and make it configurable in the level editor (as a property
6930 of gem style elements). Already existing converted levels (neither
6931 private nor contributed levels) are changed to the new behaviour. */
6933 if (level->file_version < FILE_VERSION_2_0)
6934 level->em_slippery_gems = TRUE;
6939 // ---------- use game engine the level was created with --------------------
6941 /* For all levels which are not forced to use the latest game engine
6942 version (normally user contributed, private and undefined levels),
6943 use the version of the game engine the levels were created for.
6945 Since 2.0.1, the game engine version is now directly stored
6946 in the level file (chunk "VERS"), so there is no need anymore
6947 to set the game version from the file version (except for old,
6948 pre-2.0 levels, where the game version is still taken from the
6949 file format version used to store the level -- see above). */
6951 // player was faster than enemies in 1.0.0 and before
6952 if (level->file_version == FILE_VERSION_1_0)
6953 for (i = 0; i < MAX_PLAYERS; i++)
6954 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6956 // default behaviour for EM style gems was "slippery" only in 2.0.1
6957 if (level->game_version == VERSION_IDENT(2,0,1,0))
6958 level->em_slippery_gems = TRUE;
6960 // springs could be pushed over pits before (pre-release version) 2.2.0
6961 if (level->game_version < VERSION_IDENT(2,2,0,0))
6962 level->use_spring_bug = TRUE;
6964 if (level->game_version < VERSION_IDENT(3,2,0,5))
6966 // time orb caused limited time in endless time levels before 3.2.0-5
6967 level->use_time_orb_bug = TRUE;
6969 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6970 level->block_snap_field = FALSE;
6972 // extra time score was same value as time left score before 3.2.0-5
6973 level->extra_time_score = level->score[SC_TIME_BONUS];
6976 if (level->game_version < VERSION_IDENT(3,2,0,7))
6978 // default behaviour for snapping was "not continuous" before 3.2.0-7
6979 level->continuous_snapping = FALSE;
6982 // only few elements were able to actively move into acid before 3.1.0
6983 // trigger settings did not exist before 3.1.0; set to default "any"
6984 if (level->game_version < VERSION_IDENT(3,1,0,0))
6986 // correct "can move into acid" settings (all zero in old levels)
6988 level->can_move_into_acid_bits = 0; // nothing can move into acid
6989 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6991 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6992 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6993 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6994 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6996 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6997 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6999 // correct trigger settings (stored as zero == "none" in old levels)
7001 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7003 int element = EL_CUSTOM_START + i;
7004 struct ElementInfo *ei = &element_info[element];
7006 for (j = 0; j < ei->num_change_pages; j++)
7008 struct ElementChangeInfo *change = &ei->change_page[j];
7010 change->trigger_player = CH_PLAYER_ANY;
7011 change->trigger_page = CH_PAGE_ANY;
7016 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7018 int element = EL_CUSTOM_256;
7019 struct ElementInfo *ei = &element_info[element];
7020 struct ElementChangeInfo *change = &ei->change_page[0];
7022 /* This is needed to fix a problem that was caused by a bugfix in function
7023 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7024 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7025 not replace walkable elements, but instead just placed the player on it,
7026 without placing the Sokoban field under the player). Unfortunately, this
7027 breaks "Snake Bite" style levels when the snake is halfway through a door
7028 that just closes (the snake head is still alive and can be moved in this
7029 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7030 player (without Sokoban element) which then gets killed as designed). */
7032 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7033 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7034 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7035 change->target_element = EL_PLAYER_1;
7038 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7039 if (level->game_version < VERSION_IDENT(3,2,5,0))
7041 /* This is needed to fix a problem that was caused by a bugfix in function
7042 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7043 corrects the behaviour when a custom element changes to another custom
7044 element with a higher element number that has change actions defined.
7045 Normally, only one change per frame is allowed for custom elements.
7046 Therefore, it is checked if a custom element already changed in the
7047 current frame; if it did, subsequent changes are suppressed.
7048 Unfortunately, this is only checked for element changes, but not for
7049 change actions, which are still executed. As the function above loops
7050 through all custom elements from lower to higher, an element change
7051 resulting in a lower CE number won't be checked again, while a target
7052 element with a higher number will also be checked, and potential change
7053 actions will get executed for this CE, too (which is wrong), while
7054 further changes are ignored (which is correct). As this bugfix breaks
7055 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7056 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7057 behaviour for existing levels and tapes that make use of this bug */
7059 level->use_action_after_change_bug = TRUE;
7062 // not centering level after relocating player was default only in 3.2.3
7063 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7064 level->shifted_relocation = TRUE;
7066 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7067 if (level->game_version < VERSION_IDENT(3,2,6,0))
7068 level->em_explodes_by_fire = TRUE;
7070 // levels were solved by the first player entering an exit up to 4.1.0.0
7071 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7072 level->solved_by_one_player = TRUE;
7074 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7075 if (level->game_version < VERSION_IDENT(4,1,1,1))
7076 level->use_life_bugs = TRUE;
7078 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7079 if (level->game_version < VERSION_IDENT(4,1,1,1))
7080 level->sb_objects_needed = FALSE;
7082 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7083 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7084 level->finish_dig_collect = FALSE;
7086 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7087 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7088 level->keep_walkable_ce = TRUE;
7091 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7093 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7096 // check if this level is (not) a Sokoban level
7097 for (y = 0; y < level->fieldy; y++)
7098 for (x = 0; x < level->fieldx; x++)
7099 if (!IS_SB_ELEMENT(Tile[x][y]))
7100 is_sokoban_level = FALSE;
7102 if (is_sokoban_level)
7104 // set special level settings for Sokoban levels
7105 SetLevelSettings_SB(level);
7109 static void LoadLevel_InitSettings(struct LevelInfo *level)
7111 // adjust level settings for (non-native) Sokoban-style levels
7112 LoadLevel_InitSettings_SB(level);
7114 // rename levels with title "nameless level" or if renaming is forced
7115 if (leveldir_current->empty_level_name != NULL &&
7116 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7117 leveldir_current->force_level_name))
7118 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7119 leveldir_current->empty_level_name, level_nr);
7122 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7126 // map elements that have changed in newer versions
7127 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7128 level->game_version);
7129 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7130 for (x = 0; x < 3; x++)
7131 for (y = 0; y < 3; y++)
7132 level->yamyam_content[i].e[x][y] =
7133 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7134 level->game_version);
7138 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7142 // map custom element change events that have changed in newer versions
7143 // (these following values were accidentally changed in version 3.0.1)
7144 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7145 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7147 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7149 int element = EL_CUSTOM_START + i;
7151 // order of checking and copying events to be mapped is important
7152 // (do not change the start and end value -- they are constant)
7153 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7155 if (HAS_CHANGE_EVENT(element, j - 2))
7157 SET_CHANGE_EVENT(element, j - 2, FALSE);
7158 SET_CHANGE_EVENT(element, j, TRUE);
7162 // order of checking and copying events to be mapped is important
7163 // (do not change the start and end value -- they are constant)
7164 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7166 if (HAS_CHANGE_EVENT(element, j - 1))
7168 SET_CHANGE_EVENT(element, j - 1, FALSE);
7169 SET_CHANGE_EVENT(element, j, TRUE);
7175 // initialize "can_change" field for old levels with only one change page
7176 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7178 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7180 int element = EL_CUSTOM_START + i;
7182 if (CAN_CHANGE(element))
7183 element_info[element].change->can_change = TRUE;
7187 // correct custom element values (for old levels without these options)
7188 if (level->game_version < VERSION_IDENT(3,1,1,0))
7190 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7192 int element = EL_CUSTOM_START + i;
7193 struct ElementInfo *ei = &element_info[element];
7195 if (ei->access_direction == MV_NO_DIRECTION)
7196 ei->access_direction = MV_ALL_DIRECTIONS;
7200 // correct custom element values (fix invalid values for all versions)
7203 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7205 int element = EL_CUSTOM_START + i;
7206 struct ElementInfo *ei = &element_info[element];
7208 for (j = 0; j < ei->num_change_pages; j++)
7210 struct ElementChangeInfo *change = &ei->change_page[j];
7212 if (change->trigger_player == CH_PLAYER_NONE)
7213 change->trigger_player = CH_PLAYER_ANY;
7215 if (change->trigger_side == CH_SIDE_NONE)
7216 change->trigger_side = CH_SIDE_ANY;
7221 // initialize "can_explode" field for old levels which did not store this
7222 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7223 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7225 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7227 int element = EL_CUSTOM_START + i;
7229 if (EXPLODES_1X1_OLD(element))
7230 element_info[element].explosion_type = EXPLODES_1X1;
7232 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7233 EXPLODES_SMASHED(element) ||
7234 EXPLODES_IMPACT(element)));
7238 // correct previously hard-coded move delay values for maze runner style
7239 if (level->game_version < VERSION_IDENT(3,1,1,0))
7241 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7243 int element = EL_CUSTOM_START + i;
7245 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7247 // previously hard-coded and therefore ignored
7248 element_info[element].move_delay_fixed = 9;
7249 element_info[element].move_delay_random = 0;
7254 // set some other uninitialized values of custom elements in older levels
7255 if (level->game_version < VERSION_IDENT(3,1,0,0))
7257 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7259 int element = EL_CUSTOM_START + i;
7261 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7263 element_info[element].explosion_delay = 17;
7264 element_info[element].ignition_delay = 8;
7268 // set mouse click change events to work for left/middle/right mouse button
7269 if (level->game_version < VERSION_IDENT(4,2,3,0))
7271 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7273 int element = EL_CUSTOM_START + i;
7274 struct ElementInfo *ei = &element_info[element];
7276 for (j = 0; j < ei->num_change_pages; j++)
7278 struct ElementChangeInfo *change = &ei->change_page[j];
7280 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7281 change->has_event[CE_PRESSED_BY_MOUSE] ||
7282 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7283 change->has_event[CE_MOUSE_PRESSED_ON_X])
7284 change->trigger_side = CH_SIDE_ANY;
7290 static void LoadLevel_InitElements(struct LevelInfo *level)
7292 LoadLevel_InitStandardElements(level);
7294 if (level->file_has_custom_elements)
7295 LoadLevel_InitCustomElements(level);
7297 // initialize element properties for level editor etc.
7298 InitElementPropertiesEngine(level->game_version);
7299 InitElementPropertiesGfxElement();
7302 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7306 // map elements that have changed in newer versions
7307 for (y = 0; y < level->fieldy; y++)
7308 for (x = 0; x < level->fieldx; x++)
7309 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7310 level->game_version);
7312 // clear unused playfield data (nicer if level gets resized in editor)
7313 for (x = 0; x < MAX_LEV_FIELDX; x++)
7314 for (y = 0; y < MAX_LEV_FIELDY; y++)
7315 if (x >= level->fieldx || y >= level->fieldy)
7316 level->field[x][y] = EL_EMPTY;
7318 // copy elements to runtime playfield array
7319 for (x = 0; x < MAX_LEV_FIELDX; x++)
7320 for (y = 0; y < MAX_LEV_FIELDY; y++)
7321 Tile[x][y] = level->field[x][y];
7323 // initialize level size variables for faster access
7324 lev_fieldx = level->fieldx;
7325 lev_fieldy = level->fieldy;
7327 // determine border element for this level
7328 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7329 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7334 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7336 struct LevelFileInfo *level_file_info = &level->file_info;
7338 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7339 CopyNativeLevel_RND_to_Native(level);
7342 static void LoadLevelTemplate_LoadAndInit(void)
7344 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7346 LoadLevel_InitVersion(&level_template);
7347 LoadLevel_InitElements(&level_template);
7348 LoadLevel_InitSettings(&level_template);
7350 ActivateLevelTemplate();
7353 void LoadLevelTemplate(int nr)
7355 if (!fileExists(getGlobalLevelTemplateFilename()))
7357 Warn("no level template found for this level");
7362 setLevelFileInfo(&level_template.file_info, nr);
7364 LoadLevelTemplate_LoadAndInit();
7367 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7369 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7371 LoadLevelTemplate_LoadAndInit();
7374 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7376 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7378 if (level.use_custom_template)
7380 if (network_level != NULL)
7381 LoadNetworkLevelTemplate(network_level);
7383 LoadLevelTemplate(-1);
7386 LoadLevel_InitVersion(&level);
7387 LoadLevel_InitElements(&level);
7388 LoadLevel_InitPlayfield(&level);
7389 LoadLevel_InitSettings(&level);
7391 LoadLevel_InitNativeEngines(&level);
7394 void LoadLevel(int nr)
7396 SetLevelSetInfo(leveldir_current->identifier, nr);
7398 setLevelFileInfo(&level.file_info, nr);
7400 LoadLevel_LoadAndInit(NULL);
7403 void LoadLevelInfoOnly(int nr)
7405 setLevelFileInfo(&level.file_info, nr);
7407 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7410 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7412 SetLevelSetInfo(network_level->leveldir_identifier,
7413 network_level->file_info.nr);
7415 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7417 LoadLevel_LoadAndInit(network_level);
7420 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7424 chunk_size += putFileVersion(file, level->file_version);
7425 chunk_size += putFileVersion(file, level->game_version);
7430 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7434 chunk_size += putFile16BitBE(file, level->creation_date.year);
7435 chunk_size += putFile8Bit(file, level->creation_date.month);
7436 chunk_size += putFile8Bit(file, level->creation_date.day);
7441 #if ENABLE_HISTORIC_CHUNKS
7442 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7446 putFile8Bit(file, level->fieldx);
7447 putFile8Bit(file, level->fieldy);
7449 putFile16BitBE(file, level->time);
7450 putFile16BitBE(file, level->gems_needed);
7452 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7453 putFile8Bit(file, level->name[i]);
7455 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7456 putFile8Bit(file, level->score[i]);
7458 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7459 for (y = 0; y < 3; y++)
7460 for (x = 0; x < 3; x++)
7461 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7462 level->yamyam_content[i].e[x][y]));
7463 putFile8Bit(file, level->amoeba_speed);
7464 putFile8Bit(file, level->time_magic_wall);
7465 putFile8Bit(file, level->time_wheel);
7466 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7467 level->amoeba_content));
7468 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7469 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7470 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7471 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7473 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7475 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7476 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7477 putFile32BitBE(file, level->can_move_into_acid_bits);
7478 putFile8Bit(file, level->dont_collide_with_bits);
7480 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7481 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7483 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7484 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7485 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7487 putFile8Bit(file, level->game_engine_type);
7489 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7493 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7498 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7499 chunk_size += putFile8Bit(file, level->name[i]);
7504 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7509 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7510 chunk_size += putFile8Bit(file, level->author[i]);
7515 #if ENABLE_HISTORIC_CHUNKS
7516 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7521 for (y = 0; y < level->fieldy; y++)
7522 for (x = 0; x < level->fieldx; x++)
7523 if (level->encoding_16bit_field)
7524 chunk_size += putFile16BitBE(file, level->field[x][y]);
7526 chunk_size += putFile8Bit(file, level->field[x][y]);
7532 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7537 for (y = 0; y < level->fieldy; y++)
7538 for (x = 0; x < level->fieldx; x++)
7539 chunk_size += putFile16BitBE(file, level->field[x][y]);
7544 #if ENABLE_HISTORIC_CHUNKS
7545 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7549 putFile8Bit(file, EL_YAMYAM);
7550 putFile8Bit(file, level->num_yamyam_contents);
7551 putFile8Bit(file, 0);
7552 putFile8Bit(file, 0);
7554 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7555 for (y = 0; y < 3; y++)
7556 for (x = 0; x < 3; x++)
7557 if (level->encoding_16bit_field)
7558 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7560 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7564 #if ENABLE_HISTORIC_CHUNKS
7565 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7568 int num_contents, content_xsize, content_ysize;
7569 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7571 if (element == EL_YAMYAM)
7573 num_contents = level->num_yamyam_contents;
7577 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7578 for (y = 0; y < 3; y++)
7579 for (x = 0; x < 3; x++)
7580 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7582 else if (element == EL_BD_AMOEBA)
7588 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7589 for (y = 0; y < 3; y++)
7590 for (x = 0; x < 3; x++)
7591 content_array[i][x][y] = EL_EMPTY;
7592 content_array[0][0][0] = level->amoeba_content;
7596 // chunk header already written -- write empty chunk data
7597 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7599 Warn("cannot save content for element '%d'", element);
7604 putFile16BitBE(file, element);
7605 putFile8Bit(file, num_contents);
7606 putFile8Bit(file, content_xsize);
7607 putFile8Bit(file, content_ysize);
7609 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7611 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7612 for (y = 0; y < 3; y++)
7613 for (x = 0; x < 3; x++)
7614 putFile16BitBE(file, content_array[i][x][y]);
7618 #if ENABLE_HISTORIC_CHUNKS
7619 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7621 int envelope_nr = element - EL_ENVELOPE_1;
7622 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7626 chunk_size += putFile16BitBE(file, element);
7627 chunk_size += putFile16BitBE(file, envelope_len);
7628 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7629 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7631 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7632 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7634 for (i = 0; i < envelope_len; i++)
7635 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7641 #if ENABLE_HISTORIC_CHUNKS
7642 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7643 int num_changed_custom_elements)
7647 putFile16BitBE(file, num_changed_custom_elements);
7649 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7651 int element = EL_CUSTOM_START + i;
7653 struct ElementInfo *ei = &element_info[element];
7655 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7657 if (check < num_changed_custom_elements)
7659 putFile16BitBE(file, element);
7660 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7667 if (check != num_changed_custom_elements) // should not happen
7668 Warn("inconsistent number of custom element properties");
7672 #if ENABLE_HISTORIC_CHUNKS
7673 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7674 int num_changed_custom_elements)
7678 putFile16BitBE(file, num_changed_custom_elements);
7680 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7682 int element = EL_CUSTOM_START + i;
7684 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7686 if (check < num_changed_custom_elements)
7688 putFile16BitBE(file, element);
7689 putFile16BitBE(file, element_info[element].change->target_element);
7696 if (check != num_changed_custom_elements) // should not happen
7697 Warn("inconsistent number of custom target elements");
7701 #if ENABLE_HISTORIC_CHUNKS
7702 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7703 int num_changed_custom_elements)
7705 int i, j, x, y, check = 0;
7707 putFile16BitBE(file, num_changed_custom_elements);
7709 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7711 int element = EL_CUSTOM_START + i;
7712 struct ElementInfo *ei = &element_info[element];
7714 if (ei->modified_settings)
7716 if (check < num_changed_custom_elements)
7718 putFile16BitBE(file, element);
7720 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7721 putFile8Bit(file, ei->description[j]);
7723 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7725 // some free bytes for future properties and padding
7726 WriteUnusedBytesToFile(file, 7);
7728 putFile8Bit(file, ei->use_gfx_element);
7729 putFile16BitBE(file, ei->gfx_element_initial);
7731 putFile8Bit(file, ei->collect_score_initial);
7732 putFile8Bit(file, ei->collect_count_initial);
7734 putFile16BitBE(file, ei->push_delay_fixed);
7735 putFile16BitBE(file, ei->push_delay_random);
7736 putFile16BitBE(file, ei->move_delay_fixed);
7737 putFile16BitBE(file, ei->move_delay_random);
7739 putFile16BitBE(file, ei->move_pattern);
7740 putFile8Bit(file, ei->move_direction_initial);
7741 putFile8Bit(file, ei->move_stepsize);
7743 for (y = 0; y < 3; y++)
7744 for (x = 0; x < 3; x++)
7745 putFile16BitBE(file, ei->content.e[x][y]);
7747 putFile32BitBE(file, ei->change->events);
7749 putFile16BitBE(file, ei->change->target_element);
7751 putFile16BitBE(file, ei->change->delay_fixed);
7752 putFile16BitBE(file, ei->change->delay_random);
7753 putFile16BitBE(file, ei->change->delay_frames);
7755 putFile16BitBE(file, ei->change->initial_trigger_element);
7757 putFile8Bit(file, ei->change->explode);
7758 putFile8Bit(file, ei->change->use_target_content);
7759 putFile8Bit(file, ei->change->only_if_complete);
7760 putFile8Bit(file, ei->change->use_random_replace);
7762 putFile8Bit(file, ei->change->random_percentage);
7763 putFile8Bit(file, ei->change->replace_when);
7765 for (y = 0; y < 3; y++)
7766 for (x = 0; x < 3; x++)
7767 putFile16BitBE(file, ei->change->content.e[x][y]);
7769 putFile8Bit(file, ei->slippery_type);
7771 // some free bytes for future properties and padding
7772 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7779 if (check != num_changed_custom_elements) // should not happen
7780 Warn("inconsistent number of custom element properties");
7784 #if ENABLE_HISTORIC_CHUNKS
7785 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7787 struct ElementInfo *ei = &element_info[element];
7790 // ---------- custom element base property values (96 bytes) ----------------
7792 putFile16BitBE(file, element);
7794 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7795 putFile8Bit(file, ei->description[i]);
7797 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7799 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7801 putFile8Bit(file, ei->num_change_pages);
7803 putFile16BitBE(file, ei->ce_value_fixed_initial);
7804 putFile16BitBE(file, ei->ce_value_random_initial);
7805 putFile8Bit(file, ei->use_last_ce_value);
7807 putFile8Bit(file, ei->use_gfx_element);
7808 putFile16BitBE(file, ei->gfx_element_initial);
7810 putFile8Bit(file, ei->collect_score_initial);
7811 putFile8Bit(file, ei->collect_count_initial);
7813 putFile8Bit(file, ei->drop_delay_fixed);
7814 putFile8Bit(file, ei->push_delay_fixed);
7815 putFile8Bit(file, ei->drop_delay_random);
7816 putFile8Bit(file, ei->push_delay_random);
7817 putFile16BitBE(file, ei->move_delay_fixed);
7818 putFile16BitBE(file, ei->move_delay_random);
7820 // bits 0 - 15 of "move_pattern" ...
7821 putFile16BitBE(file, ei->move_pattern & 0xffff);
7822 putFile8Bit(file, ei->move_direction_initial);
7823 putFile8Bit(file, ei->move_stepsize);
7825 putFile8Bit(file, ei->slippery_type);
7827 for (y = 0; y < 3; y++)
7828 for (x = 0; x < 3; x++)
7829 putFile16BitBE(file, ei->content.e[x][y]);
7831 putFile16BitBE(file, ei->move_enter_element);
7832 putFile16BitBE(file, ei->move_leave_element);
7833 putFile8Bit(file, ei->move_leave_type);
7835 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7836 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7838 putFile8Bit(file, ei->access_direction);
7840 putFile8Bit(file, ei->explosion_delay);
7841 putFile8Bit(file, ei->ignition_delay);
7842 putFile8Bit(file, ei->explosion_type);
7844 // some free bytes for future custom property values and padding
7845 WriteUnusedBytesToFile(file, 1);
7847 // ---------- change page property values (48 bytes) ------------------------
7849 for (i = 0; i < ei->num_change_pages; i++)
7851 struct ElementChangeInfo *change = &ei->change_page[i];
7852 unsigned int event_bits;
7854 // bits 0 - 31 of "has_event[]" ...
7856 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7857 if (change->has_event[j])
7858 event_bits |= (1u << j);
7859 putFile32BitBE(file, event_bits);
7861 putFile16BitBE(file, change->target_element);
7863 putFile16BitBE(file, change->delay_fixed);
7864 putFile16BitBE(file, change->delay_random);
7865 putFile16BitBE(file, change->delay_frames);
7867 putFile16BitBE(file, change->initial_trigger_element);
7869 putFile8Bit(file, change->explode);
7870 putFile8Bit(file, change->use_target_content);
7871 putFile8Bit(file, change->only_if_complete);
7872 putFile8Bit(file, change->use_random_replace);
7874 putFile8Bit(file, change->random_percentage);
7875 putFile8Bit(file, change->replace_when);
7877 for (y = 0; y < 3; y++)
7878 for (x = 0; x < 3; x++)
7879 putFile16BitBE(file, change->target_content.e[x][y]);
7881 putFile8Bit(file, change->can_change);
7883 putFile8Bit(file, change->trigger_side);
7885 putFile8Bit(file, change->trigger_player);
7886 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7887 log_2(change->trigger_page)));
7889 putFile8Bit(file, change->has_action);
7890 putFile8Bit(file, change->action_type);
7891 putFile8Bit(file, change->action_mode);
7892 putFile16BitBE(file, change->action_arg);
7894 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7896 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7897 if (change->has_event[j])
7898 event_bits |= (1u << (j - 32));
7899 putFile8Bit(file, event_bits);
7904 #if ENABLE_HISTORIC_CHUNKS
7905 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7907 struct ElementInfo *ei = &element_info[element];
7908 struct ElementGroupInfo *group = ei->group;
7911 putFile16BitBE(file, element);
7913 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7914 putFile8Bit(file, ei->description[i]);
7916 putFile8Bit(file, group->num_elements);
7918 putFile8Bit(file, ei->use_gfx_element);
7919 putFile16BitBE(file, ei->gfx_element_initial);
7921 putFile8Bit(file, group->choice_mode);
7923 // some free bytes for future values and padding
7924 WriteUnusedBytesToFile(file, 3);
7926 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7927 putFile16BitBE(file, group->element[i]);
7931 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7932 boolean write_element)
7934 int save_type = entry->save_type;
7935 int data_type = entry->data_type;
7936 int conf_type = entry->conf_type;
7937 int byte_mask = conf_type & CONF_MASK_BYTES;
7938 int element = entry->element;
7939 int default_value = entry->default_value;
7941 boolean modified = FALSE;
7943 if (byte_mask != CONF_MASK_MULTI_BYTES)
7945 void *value_ptr = entry->value;
7946 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7949 // check if any settings have been modified before saving them
7950 if (value != default_value)
7953 // do not save if explicitly told or if unmodified default settings
7954 if ((save_type == SAVE_CONF_NEVER) ||
7955 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7959 num_bytes += putFile16BitBE(file, element);
7961 num_bytes += putFile8Bit(file, conf_type);
7962 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7963 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7964 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7967 else if (data_type == TYPE_STRING)
7969 char *default_string = entry->default_string;
7970 char *string = (char *)(entry->value);
7971 int string_length = strlen(string);
7974 // check if any settings have been modified before saving them
7975 if (!strEqual(string, default_string))
7978 // do not save if explicitly told or if unmodified default settings
7979 if ((save_type == SAVE_CONF_NEVER) ||
7980 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7984 num_bytes += putFile16BitBE(file, element);
7986 num_bytes += putFile8Bit(file, conf_type);
7987 num_bytes += putFile16BitBE(file, string_length);
7989 for (i = 0; i < string_length; i++)
7990 num_bytes += putFile8Bit(file, string[i]);
7992 else if (data_type == TYPE_ELEMENT_LIST)
7994 int *element_array = (int *)(entry->value);
7995 int num_elements = *(int *)(entry->num_entities);
7998 // check if any settings have been modified before saving them
7999 for (i = 0; i < num_elements; i++)
8000 if (element_array[i] != default_value)
8003 // do not save if explicitly told or if unmodified default settings
8004 if ((save_type == SAVE_CONF_NEVER) ||
8005 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8009 num_bytes += putFile16BitBE(file, element);
8011 num_bytes += putFile8Bit(file, conf_type);
8012 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8014 for (i = 0; i < num_elements; i++)
8015 num_bytes += putFile16BitBE(file, element_array[i]);
8017 else if (data_type == TYPE_CONTENT_LIST)
8019 struct Content *content = (struct Content *)(entry->value);
8020 int num_contents = *(int *)(entry->num_entities);
8023 // check if any settings have been modified before saving them
8024 for (i = 0; i < num_contents; i++)
8025 for (y = 0; y < 3; y++)
8026 for (x = 0; x < 3; x++)
8027 if (content[i].e[x][y] != default_value)
8030 // do not save if explicitly told or if unmodified default settings
8031 if ((save_type == SAVE_CONF_NEVER) ||
8032 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8036 num_bytes += putFile16BitBE(file, element);
8038 num_bytes += putFile8Bit(file, conf_type);
8039 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8041 for (i = 0; i < num_contents; i++)
8042 for (y = 0; y < 3; y++)
8043 for (x = 0; x < 3; x++)
8044 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8050 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8055 li = *level; // copy level data into temporary buffer
8057 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8058 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8063 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8068 li = *level; // copy level data into temporary buffer
8070 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8071 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8076 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8078 int envelope_nr = element - EL_ENVELOPE_1;
8082 chunk_size += putFile16BitBE(file, element);
8084 // copy envelope data into temporary buffer
8085 xx_envelope = level->envelope[envelope_nr];
8087 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8088 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8093 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8095 struct ElementInfo *ei = &element_info[element];
8099 chunk_size += putFile16BitBE(file, element);
8101 xx_ei = *ei; // copy element data into temporary buffer
8103 // set default description string for this specific element
8104 strcpy(xx_default_description, getDefaultElementDescription(ei));
8106 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8107 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8109 for (i = 0; i < ei->num_change_pages; i++)
8111 struct ElementChangeInfo *change = &ei->change_page[i];
8113 xx_current_change_page = i;
8115 xx_change = *change; // copy change data into temporary buffer
8118 setEventBitsFromEventFlags(change);
8120 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8121 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8128 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8130 struct ElementInfo *ei = &element_info[element];
8131 struct ElementGroupInfo *group = ei->group;
8135 chunk_size += putFile16BitBE(file, element);
8137 xx_ei = *ei; // copy element data into temporary buffer
8138 xx_group = *group; // copy group data into temporary buffer
8140 // set default description string for this specific element
8141 strcpy(xx_default_description, getDefaultElementDescription(ei));
8143 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8144 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8149 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8151 struct ElementInfo *ei = &element_info[element];
8155 chunk_size += putFile16BitBE(file, element);
8157 xx_ei = *ei; // copy element data into temporary buffer
8159 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8160 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8165 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8166 boolean save_as_template)
8172 if (!(file = fopen(filename, MODE_WRITE)))
8174 Warn("cannot save level file '%s'", filename);
8179 level->file_version = FILE_VERSION_ACTUAL;
8180 level->game_version = GAME_VERSION_ACTUAL;
8182 level->creation_date = getCurrentDate();
8184 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8185 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8187 chunk_size = SaveLevel_VERS(NULL, level);
8188 putFileChunkBE(file, "VERS", chunk_size);
8189 SaveLevel_VERS(file, level);
8191 chunk_size = SaveLevel_DATE(NULL, level);
8192 putFileChunkBE(file, "DATE", chunk_size);
8193 SaveLevel_DATE(file, level);
8195 chunk_size = SaveLevel_NAME(NULL, level);
8196 putFileChunkBE(file, "NAME", chunk_size);
8197 SaveLevel_NAME(file, level);
8199 chunk_size = SaveLevel_AUTH(NULL, level);
8200 putFileChunkBE(file, "AUTH", chunk_size);
8201 SaveLevel_AUTH(file, level);
8203 chunk_size = SaveLevel_INFO(NULL, level);
8204 putFileChunkBE(file, "INFO", chunk_size);
8205 SaveLevel_INFO(file, level);
8207 chunk_size = SaveLevel_BODY(NULL, level);
8208 putFileChunkBE(file, "BODY", chunk_size);
8209 SaveLevel_BODY(file, level);
8211 chunk_size = SaveLevel_ELEM(NULL, level);
8212 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8214 putFileChunkBE(file, "ELEM", chunk_size);
8215 SaveLevel_ELEM(file, level);
8218 for (i = 0; i < NUM_ENVELOPES; i++)
8220 int element = EL_ENVELOPE_1 + i;
8222 chunk_size = SaveLevel_NOTE(NULL, level, element);
8223 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8225 putFileChunkBE(file, "NOTE", chunk_size);
8226 SaveLevel_NOTE(file, level, element);
8230 // if not using template level, check for non-default custom/group elements
8231 if (!level->use_custom_template || save_as_template)
8233 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8235 int element = EL_CUSTOM_START + i;
8237 chunk_size = SaveLevel_CUSX(NULL, level, element);
8238 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8240 putFileChunkBE(file, "CUSX", chunk_size);
8241 SaveLevel_CUSX(file, level, element);
8245 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8247 int element = EL_GROUP_START + i;
8249 chunk_size = SaveLevel_GRPX(NULL, level, element);
8250 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8252 putFileChunkBE(file, "GRPX", chunk_size);
8253 SaveLevel_GRPX(file, level, element);
8257 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8259 int element = GET_EMPTY_ELEMENT(i);
8261 chunk_size = SaveLevel_EMPX(NULL, level, element);
8262 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8264 putFileChunkBE(file, "EMPX", chunk_size);
8265 SaveLevel_EMPX(file, level, element);
8272 SetFilePermissions(filename, PERMS_PRIVATE);
8275 void SaveLevel(int nr)
8277 char *filename = getDefaultLevelFilename(nr);
8279 SaveLevelFromFilename(&level, filename, FALSE);
8282 void SaveLevelTemplate(void)
8284 char *filename = getLocalLevelTemplateFilename();
8286 SaveLevelFromFilename(&level, filename, TRUE);
8289 boolean SaveLevelChecked(int nr)
8291 char *filename = getDefaultLevelFilename(nr);
8292 boolean new_level = !fileExists(filename);
8293 boolean level_saved = FALSE;
8295 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8300 Request("Level saved!", REQ_CONFIRM);
8308 void DumpLevel(struct LevelInfo *level)
8310 if (level->no_level_file || level->no_valid_file)
8312 Warn("cannot dump -- no valid level file found");
8318 Print("Level xxx (file version %08d, game version %08d)\n",
8319 level->file_version, level->game_version);
8322 Print("Level author: '%s'\n", level->author);
8323 Print("Level title: '%s'\n", level->name);
8325 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8327 Print("Level time: %d seconds\n", level->time);
8328 Print("Gems needed: %d\n", level->gems_needed);
8330 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8331 Print("Time for wheel: %d seconds\n", level->time_wheel);
8332 Print("Time for light: %d seconds\n", level->time_light);
8333 Print("Time for timegate: %d seconds\n", level->time_timegate);
8335 Print("Amoeba speed: %d\n", level->amoeba_speed);
8338 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8339 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8340 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8341 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8342 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8343 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8349 for (i = 0; i < NUM_ENVELOPES; i++)
8351 char *text = level->envelope[i].text;
8352 int text_len = strlen(text);
8353 boolean has_text = FALSE;
8355 for (j = 0; j < text_len; j++)
8356 if (text[j] != ' ' && text[j] != '\n')
8362 Print("Envelope %d:\n'%s'\n", i + 1, text);
8370 void DumpLevels(void)
8372 static LevelDirTree *dumplevel_leveldir = NULL;
8374 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8375 global.dumplevel_leveldir);
8377 if (dumplevel_leveldir == NULL)
8378 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8380 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8381 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8382 Fail("no such level number: %d", global.dumplevel_level_nr);
8384 leveldir_current = dumplevel_leveldir;
8386 LoadLevel(global.dumplevel_level_nr);
8393 // ============================================================================
8394 // tape file functions
8395 // ============================================================================
8397 static void setTapeInfoToDefaults(void)
8401 // always start with reliable default values (empty tape)
8404 // default values (also for pre-1.2 tapes) with only the first player
8405 tape.player_participates[0] = TRUE;
8406 for (i = 1; i < MAX_PLAYERS; i++)
8407 tape.player_participates[i] = FALSE;
8409 // at least one (default: the first) player participates in every tape
8410 tape.num_participating_players = 1;
8412 tape.property_bits = TAPE_PROPERTY_NONE;
8414 tape.level_nr = level_nr;
8416 tape.changed = FALSE;
8417 tape.solved = FALSE;
8419 tape.recording = FALSE;
8420 tape.playing = FALSE;
8421 tape.pausing = FALSE;
8423 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8424 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8426 tape.no_info_chunk = TRUE;
8427 tape.no_valid_file = FALSE;
8430 static int getTapePosSize(struct TapeInfo *tape)
8432 int tape_pos_size = 0;
8434 if (tape->use_key_actions)
8435 tape_pos_size += tape->num_participating_players;
8437 if (tape->use_mouse_actions)
8438 tape_pos_size += 3; // x and y position and mouse button mask
8440 tape_pos_size += 1; // tape action delay value
8442 return tape_pos_size;
8445 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8447 tape->use_key_actions = FALSE;
8448 tape->use_mouse_actions = FALSE;
8450 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8451 tape->use_key_actions = TRUE;
8453 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8454 tape->use_mouse_actions = TRUE;
8457 static int getTapeActionValue(struct TapeInfo *tape)
8459 return (tape->use_key_actions &&
8460 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8461 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8462 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8463 TAPE_ACTIONS_DEFAULT);
8466 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8468 tape->file_version = getFileVersion(file);
8469 tape->game_version = getFileVersion(file);
8474 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8478 tape->random_seed = getFile32BitBE(file);
8479 tape->date = getFile32BitBE(file);
8480 tape->length = getFile32BitBE(file);
8482 // read header fields that are new since version 1.2
8483 if (tape->file_version >= FILE_VERSION_1_2)
8485 byte store_participating_players = getFile8Bit(file);
8488 // since version 1.2, tapes store which players participate in the tape
8489 tape->num_participating_players = 0;
8490 for (i = 0; i < MAX_PLAYERS; i++)
8492 tape->player_participates[i] = FALSE;
8494 if (store_participating_players & (1 << i))
8496 tape->player_participates[i] = TRUE;
8497 tape->num_participating_players++;
8501 setTapeActionFlags(tape, getFile8Bit(file));
8503 tape->property_bits = getFile8Bit(file);
8504 tape->solved = getFile8Bit(file);
8506 engine_version = getFileVersion(file);
8507 if (engine_version > 0)
8508 tape->engine_version = engine_version;
8510 tape->engine_version = tape->game_version;
8516 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8518 tape->scr_fieldx = getFile8Bit(file);
8519 tape->scr_fieldy = getFile8Bit(file);
8524 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8526 char *level_identifier = NULL;
8527 int level_identifier_size;
8530 tape->no_info_chunk = FALSE;
8532 level_identifier_size = getFile16BitBE(file);
8534 level_identifier = checked_malloc(level_identifier_size);
8536 for (i = 0; i < level_identifier_size; i++)
8537 level_identifier[i] = getFile8Bit(file);
8539 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8540 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8542 checked_free(level_identifier);
8544 tape->level_nr = getFile16BitBE(file);
8546 chunk_size = 2 + level_identifier_size + 2;
8551 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8554 int tape_pos_size = getTapePosSize(tape);
8555 int chunk_size_expected = tape_pos_size * tape->length;
8557 if (chunk_size_expected != chunk_size)
8559 ReadUnusedBytesFromFile(file, chunk_size);
8560 return chunk_size_expected;
8563 for (i = 0; i < tape->length; i++)
8565 if (i >= MAX_TAPE_LEN)
8567 Warn("tape truncated -- size exceeds maximum tape size %d",
8570 // tape too large; read and ignore remaining tape data from this chunk
8571 for (;i < tape->length; i++)
8572 ReadUnusedBytesFromFile(file, tape_pos_size);
8577 if (tape->use_key_actions)
8579 for (j = 0; j < MAX_PLAYERS; j++)
8581 tape->pos[i].action[j] = MV_NONE;
8583 if (tape->player_participates[j])
8584 tape->pos[i].action[j] = getFile8Bit(file);
8588 if (tape->use_mouse_actions)
8590 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8591 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8592 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8595 tape->pos[i].delay = getFile8Bit(file);
8597 if (tape->file_version == FILE_VERSION_1_0)
8599 // eliminate possible diagonal moves in old tapes
8600 // this is only for backward compatibility
8602 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8603 byte action = tape->pos[i].action[0];
8604 int k, num_moves = 0;
8606 for (k = 0; k < 4; k++)
8608 if (action & joy_dir[k])
8610 tape->pos[i + num_moves].action[0] = joy_dir[k];
8612 tape->pos[i + num_moves].delay = 0;
8621 tape->length += num_moves;
8624 else if (tape->file_version < FILE_VERSION_2_0)
8626 // convert pre-2.0 tapes to new tape format
8628 if (tape->pos[i].delay > 1)
8631 tape->pos[i + 1] = tape->pos[i];
8632 tape->pos[i + 1].delay = 1;
8635 for (j = 0; j < MAX_PLAYERS; j++)
8636 tape->pos[i].action[j] = MV_NONE;
8637 tape->pos[i].delay--;
8644 if (checkEndOfFile(file))
8648 if (i != tape->length)
8649 chunk_size = tape_pos_size * i;
8654 static void LoadTape_SokobanSolution(char *filename)
8657 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8659 if (!(file = openFile(filename, MODE_READ)))
8661 tape.no_valid_file = TRUE;
8666 while (!checkEndOfFile(file))
8668 unsigned char c = getByteFromFile(file);
8670 if (checkEndOfFile(file))
8677 tape.pos[tape.length].action[0] = MV_UP;
8678 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8684 tape.pos[tape.length].action[0] = MV_DOWN;
8685 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8691 tape.pos[tape.length].action[0] = MV_LEFT;
8692 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8698 tape.pos[tape.length].action[0] = MV_RIGHT;
8699 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8707 // ignore white-space characters
8711 tape.no_valid_file = TRUE;
8713 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8721 if (tape.no_valid_file)
8724 tape.length_frames = GetTapeLengthFrames();
8725 tape.length_seconds = GetTapeLengthSeconds();
8728 void LoadTapeFromFilename(char *filename)
8730 char cookie[MAX_LINE_LEN];
8731 char chunk_name[CHUNK_ID_LEN + 1];
8735 // always start with reliable default values
8736 setTapeInfoToDefaults();
8738 if (strSuffix(filename, ".sln"))
8740 LoadTape_SokobanSolution(filename);
8745 if (!(file = openFile(filename, MODE_READ)))
8747 tape.no_valid_file = TRUE;
8752 getFileChunkBE(file, chunk_name, NULL);
8753 if (strEqual(chunk_name, "RND1"))
8755 getFile32BitBE(file); // not used
8757 getFileChunkBE(file, chunk_name, NULL);
8758 if (!strEqual(chunk_name, "TAPE"))
8760 tape.no_valid_file = TRUE;
8762 Warn("unknown format of tape file '%s'", filename);
8769 else // check for pre-2.0 file format with cookie string
8771 strcpy(cookie, chunk_name);
8772 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8774 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8775 cookie[strlen(cookie) - 1] = '\0';
8777 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8779 tape.no_valid_file = TRUE;
8781 Warn("unknown format of tape file '%s'", filename);
8788 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8790 tape.no_valid_file = TRUE;
8792 Warn("unsupported version of tape file '%s'", filename);
8799 // pre-2.0 tape files have no game version, so use file version here
8800 tape.game_version = tape.file_version;
8803 if (tape.file_version < FILE_VERSION_1_2)
8805 // tape files from versions before 1.2.0 without chunk structure
8806 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8807 LoadTape_BODY(file, 2 * tape.length, &tape);
8815 int (*loader)(File *, int, struct TapeInfo *);
8819 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8820 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8821 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8822 { "INFO", -1, LoadTape_INFO },
8823 { "BODY", -1, LoadTape_BODY },
8827 while (getFileChunkBE(file, chunk_name, &chunk_size))
8831 while (chunk_info[i].name != NULL &&
8832 !strEqual(chunk_name, chunk_info[i].name))
8835 if (chunk_info[i].name == NULL)
8837 Warn("unknown chunk '%s' in tape file '%s'",
8838 chunk_name, filename);
8840 ReadUnusedBytesFromFile(file, chunk_size);
8842 else if (chunk_info[i].size != -1 &&
8843 chunk_info[i].size != chunk_size)
8845 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8846 chunk_size, chunk_name, filename);
8848 ReadUnusedBytesFromFile(file, chunk_size);
8852 // call function to load this tape chunk
8853 int chunk_size_expected =
8854 (chunk_info[i].loader)(file, chunk_size, &tape);
8856 // the size of some chunks cannot be checked before reading other
8857 // chunks first (like "HEAD" and "BODY") that contain some header
8858 // information, so check them here
8859 if (chunk_size_expected != chunk_size)
8861 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8862 chunk_size, chunk_name, filename);
8870 tape.length_frames = GetTapeLengthFrames();
8871 tape.length_seconds = GetTapeLengthSeconds();
8874 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8876 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8878 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8879 tape.engine_version);
8883 void LoadTape(int nr)
8885 char *filename = getTapeFilename(nr);
8887 LoadTapeFromFilename(filename);
8890 void LoadSolutionTape(int nr)
8892 char *filename = getSolutionTapeFilename(nr);
8894 LoadTapeFromFilename(filename);
8896 if (TAPE_IS_EMPTY(tape))
8898 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8899 level.native_bd_level->replay != NULL)
8900 CopyNativeTape_BD_to_RND(&level);
8901 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8902 level.native_sp_level->demo.is_available)
8903 CopyNativeTape_SP_to_RND(&level);
8907 void LoadScoreTape(char *score_tape_basename, int nr)
8909 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8911 LoadTapeFromFilename(filename);
8914 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8916 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8918 LoadTapeFromFilename(filename);
8921 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8923 // chunk required for team mode tapes with non-default screen size
8924 return (tape->num_participating_players > 1 &&
8925 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8926 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8929 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8931 putFileVersion(file, tape->file_version);
8932 putFileVersion(file, tape->game_version);
8935 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8938 byte store_participating_players = 0;
8940 // set bits for participating players for compact storage
8941 for (i = 0; i < MAX_PLAYERS; i++)
8942 if (tape->player_participates[i])
8943 store_participating_players |= (1 << i);
8945 putFile32BitBE(file, tape->random_seed);
8946 putFile32BitBE(file, tape->date);
8947 putFile32BitBE(file, tape->length);
8949 putFile8Bit(file, store_participating_players);
8951 putFile8Bit(file, getTapeActionValue(tape));
8953 putFile8Bit(file, tape->property_bits);
8954 putFile8Bit(file, tape->solved);
8956 putFileVersion(file, tape->engine_version);
8959 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8961 putFile8Bit(file, tape->scr_fieldx);
8962 putFile8Bit(file, tape->scr_fieldy);
8965 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8967 int level_identifier_size = strlen(tape->level_identifier) + 1;
8970 putFile16BitBE(file, level_identifier_size);
8972 for (i = 0; i < level_identifier_size; i++)
8973 putFile8Bit(file, tape->level_identifier[i]);
8975 putFile16BitBE(file, tape->level_nr);
8978 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8982 for (i = 0; i < tape->length; i++)
8984 if (tape->use_key_actions)
8986 for (j = 0; j < MAX_PLAYERS; j++)
8987 if (tape->player_participates[j])
8988 putFile8Bit(file, tape->pos[i].action[j]);
8991 if (tape->use_mouse_actions)
8993 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8994 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8995 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8998 putFile8Bit(file, tape->pos[i].delay);
9002 void SaveTapeToFilename(char *filename)
9006 int info_chunk_size;
9007 int body_chunk_size;
9009 if (!(file = fopen(filename, MODE_WRITE)))
9011 Warn("cannot save level recording file '%s'", filename);
9016 tape_pos_size = getTapePosSize(&tape);
9018 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9019 body_chunk_size = tape_pos_size * tape.length;
9021 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9022 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9024 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9025 SaveTape_VERS(file, &tape);
9027 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9028 SaveTape_HEAD(file, &tape);
9030 if (checkSaveTape_SCRN(&tape))
9032 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9033 SaveTape_SCRN(file, &tape);
9036 putFileChunkBE(file, "INFO", info_chunk_size);
9037 SaveTape_INFO(file, &tape);
9039 putFileChunkBE(file, "BODY", body_chunk_size);
9040 SaveTape_BODY(file, &tape);
9044 SetFilePermissions(filename, PERMS_PRIVATE);
9047 static void SaveTapeExt(char *filename)
9051 tape.file_version = FILE_VERSION_ACTUAL;
9052 tape.game_version = GAME_VERSION_ACTUAL;
9054 tape.num_participating_players = 0;
9056 // count number of participating players
9057 for (i = 0; i < MAX_PLAYERS; i++)
9058 if (tape.player_participates[i])
9059 tape.num_participating_players++;
9061 SaveTapeToFilename(filename);
9063 tape.changed = FALSE;
9066 void SaveTape(int nr)
9068 char *filename = getTapeFilename(nr);
9070 InitTapeDirectory(leveldir_current->subdir);
9072 SaveTapeExt(filename);
9075 void SaveScoreTape(int nr)
9077 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9079 // used instead of "leveldir_current->subdir" (for network games)
9080 InitScoreTapeDirectory(levelset.identifier, nr);
9082 SaveTapeExt(filename);
9085 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9086 unsigned int req_state_added)
9088 char *filename = getTapeFilename(nr);
9089 boolean new_tape = !fileExists(filename);
9090 boolean tape_saved = FALSE;
9092 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9097 Request(msg_saved, REQ_CONFIRM | req_state_added);
9105 boolean SaveTapeChecked(int nr)
9107 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9110 boolean SaveTapeChecked_LevelSolved(int nr)
9112 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9113 "Level solved! Tape saved!", REQ_STAY_OPEN);
9116 void DumpTape(struct TapeInfo *tape)
9118 int tape_frame_counter;
9121 if (tape->no_valid_file)
9123 Warn("cannot dump -- no valid tape file found");
9130 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9131 tape->level_nr, tape->file_version, tape->game_version);
9132 Print(" (effective engine version %08d)\n",
9133 tape->engine_version);
9134 Print("Level series identifier: '%s'\n", tape->level_identifier);
9136 Print("Solution tape: %s\n",
9137 tape->solved ? "yes" :
9138 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9140 Print("Special tape properties: ");
9141 if (tape->property_bits == TAPE_PROPERTY_NONE)
9143 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9144 Print("[em_random_bug]");
9145 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9146 Print("[game_speed]");
9147 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9149 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9150 Print("[single_step]");
9151 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9152 Print("[snapshot]");
9153 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9154 Print("[replayed]");
9155 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9156 Print("[tas_keys]");
9157 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9158 Print("[small_graphics]");
9161 int year2 = tape->date / 10000;
9162 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9163 int month_index_raw = (tape->date / 100) % 100;
9164 int month_index = month_index_raw % 12; // prevent invalid index
9165 int month = month_index + 1;
9166 int day = tape->date % 100;
9168 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9172 tape_frame_counter = 0;
9174 for (i = 0; i < tape->length; i++)
9176 if (i >= MAX_TAPE_LEN)
9181 for (j = 0; j < MAX_PLAYERS; j++)
9183 if (tape->player_participates[j])
9185 int action = tape->pos[i].action[j];
9187 Print("%d:%02x ", j, action);
9188 Print("[%c%c%c%c|%c%c] - ",
9189 (action & JOY_LEFT ? '<' : ' '),
9190 (action & JOY_RIGHT ? '>' : ' '),
9191 (action & JOY_UP ? '^' : ' '),
9192 (action & JOY_DOWN ? 'v' : ' '),
9193 (action & JOY_BUTTON_1 ? '1' : ' '),
9194 (action & JOY_BUTTON_2 ? '2' : ' '));
9198 Print("(%03d) ", tape->pos[i].delay);
9199 Print("[%05d]\n", tape_frame_counter);
9201 tape_frame_counter += tape->pos[i].delay;
9207 void DumpTapes(void)
9209 static LevelDirTree *dumptape_leveldir = NULL;
9211 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9212 global.dumptape_leveldir);
9214 if (dumptape_leveldir == NULL)
9215 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9217 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9218 global.dumptape_level_nr > dumptape_leveldir->last_level)
9219 Fail("no such level number: %d", global.dumptape_level_nr);
9221 leveldir_current = dumptape_leveldir;
9223 if (options.mytapes)
9224 LoadTape(global.dumptape_level_nr);
9226 LoadSolutionTape(global.dumptape_level_nr);
9234 // ============================================================================
9235 // score file functions
9236 // ============================================================================
9238 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9242 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9244 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9245 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9246 scores->entry[i].score = 0;
9247 scores->entry[i].time = 0;
9249 scores->entry[i].id = -1;
9250 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9251 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9252 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9253 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9254 strcpy(scores->entry[i].country_code, "??");
9257 scores->num_entries = 0;
9258 scores->last_added = -1;
9259 scores->last_added_local = -1;
9261 scores->updated = FALSE;
9262 scores->uploaded = FALSE;
9263 scores->tape_downloaded = FALSE;
9264 scores->force_last_added = FALSE;
9266 // The following values are intentionally not reset here:
9270 // - continue_playing
9271 // - continue_on_return
9274 static void setScoreInfoToDefaults(void)
9276 setScoreInfoToDefaultsExt(&scores);
9279 static void setServerScoreInfoToDefaults(void)
9281 setScoreInfoToDefaultsExt(&server_scores);
9284 static void LoadScore_OLD(int nr)
9287 char *filename = getScoreFilename(nr);
9288 char cookie[MAX_LINE_LEN];
9289 char line[MAX_LINE_LEN];
9293 if (!(file = fopen(filename, MODE_READ)))
9296 // check file identifier
9297 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9299 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9300 cookie[strlen(cookie) - 1] = '\0';
9302 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9304 Warn("unknown format of score file '%s'", filename);
9311 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9313 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9314 Warn("fscanf() failed; %s", strerror(errno));
9316 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9319 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9320 line[strlen(line) - 1] = '\0';
9322 for (line_ptr = line; *line_ptr; line_ptr++)
9324 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9326 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9327 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9336 static void ConvertScore_OLD(void)
9338 // only convert score to time for levels that rate playing time over score
9339 if (!level.rate_time_over_score)
9342 // convert old score to playing time for score-less levels (like Supaplex)
9343 int time_final_max = 999;
9346 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9348 int score = scores.entry[i].score;
9350 if (score > 0 && score < time_final_max)
9351 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9355 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9357 scores->file_version = getFileVersion(file);
9358 scores->game_version = getFileVersion(file);
9363 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9365 char *level_identifier = NULL;
9366 int level_identifier_size;
9369 level_identifier_size = getFile16BitBE(file);
9371 level_identifier = checked_malloc(level_identifier_size);
9373 for (i = 0; i < level_identifier_size; i++)
9374 level_identifier[i] = getFile8Bit(file);
9376 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9377 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9379 checked_free(level_identifier);
9381 scores->level_nr = getFile16BitBE(file);
9382 scores->num_entries = getFile16BitBE(file);
9384 chunk_size = 2 + level_identifier_size + 2 + 2;
9389 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9393 for (i = 0; i < scores->num_entries; i++)
9395 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9396 scores->entry[i].name[j] = getFile8Bit(file);
9398 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9401 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9406 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9410 for (i = 0; i < scores->num_entries; i++)
9411 scores->entry[i].score = getFile16BitBE(file);
9413 chunk_size = scores->num_entries * 2;
9418 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9422 for (i = 0; i < scores->num_entries; i++)
9423 scores->entry[i].score = getFile32BitBE(file);
9425 chunk_size = scores->num_entries * 4;
9430 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9434 for (i = 0; i < scores->num_entries; i++)
9435 scores->entry[i].time = getFile32BitBE(file);
9437 chunk_size = scores->num_entries * 4;
9442 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9446 for (i = 0; i < scores->num_entries; i++)
9448 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9449 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9451 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9454 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9459 void LoadScore(int nr)
9461 char *filename = getScoreFilename(nr);
9462 char cookie[MAX_LINE_LEN];
9463 char chunk_name[CHUNK_ID_LEN + 1];
9465 boolean old_score_file_format = FALSE;
9468 // always start with reliable default values
9469 setScoreInfoToDefaults();
9471 if (!(file = openFile(filename, MODE_READ)))
9474 getFileChunkBE(file, chunk_name, NULL);
9475 if (strEqual(chunk_name, "RND1"))
9477 getFile32BitBE(file); // not used
9479 getFileChunkBE(file, chunk_name, NULL);
9480 if (!strEqual(chunk_name, "SCOR"))
9482 Warn("unknown format of score file '%s'", filename);
9489 else // check for old file format with cookie string
9491 strcpy(cookie, chunk_name);
9492 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9494 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9495 cookie[strlen(cookie) - 1] = '\0';
9497 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9499 Warn("unknown format of score file '%s'", filename);
9506 old_score_file_format = TRUE;
9509 if (old_score_file_format)
9511 // score files from versions before 4.2.4.0 without chunk structure
9514 // convert score to time, if possible (mainly for Supaplex levels)
9523 int (*loader)(File *, int, struct ScoreInfo *);
9527 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9528 { "INFO", -1, LoadScore_INFO },
9529 { "NAME", -1, LoadScore_NAME },
9530 { "SCOR", -1, LoadScore_SCOR },
9531 { "SC4R", -1, LoadScore_SC4R },
9532 { "TIME", -1, LoadScore_TIME },
9533 { "TAPE", -1, LoadScore_TAPE },
9538 while (getFileChunkBE(file, chunk_name, &chunk_size))
9542 while (chunk_info[i].name != NULL &&
9543 !strEqual(chunk_name, chunk_info[i].name))
9546 if (chunk_info[i].name == NULL)
9548 Warn("unknown chunk '%s' in score file '%s'",
9549 chunk_name, filename);
9551 ReadUnusedBytesFromFile(file, chunk_size);
9553 else if (chunk_info[i].size != -1 &&
9554 chunk_info[i].size != chunk_size)
9556 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9557 chunk_size, chunk_name, filename);
9559 ReadUnusedBytesFromFile(file, chunk_size);
9563 // call function to load this score chunk
9564 int chunk_size_expected =
9565 (chunk_info[i].loader)(file, chunk_size, &scores);
9567 // the size of some chunks cannot be checked before reading other
9568 // chunks first (like "HEAD" and "BODY") that contain some header
9569 // information, so check them here
9570 if (chunk_size_expected != chunk_size)
9572 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9573 chunk_size, chunk_name, filename);
9582 #if ENABLE_HISTORIC_CHUNKS
9583 void SaveScore_OLD(int nr)
9586 char *filename = getScoreFilename(nr);
9589 // used instead of "leveldir_current->subdir" (for network games)
9590 InitScoreDirectory(levelset.identifier);
9592 if (!(file = fopen(filename, MODE_WRITE)))
9594 Warn("cannot save score for level %d", nr);
9599 fprintf(file, "%s\n\n", SCORE_COOKIE);
9601 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9602 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9606 SetFilePermissions(filename, PERMS_PRIVATE);
9610 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9612 putFileVersion(file, scores->file_version);
9613 putFileVersion(file, scores->game_version);
9616 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9618 int level_identifier_size = strlen(scores->level_identifier) + 1;
9621 putFile16BitBE(file, level_identifier_size);
9623 for (i = 0; i < level_identifier_size; i++)
9624 putFile8Bit(file, scores->level_identifier[i]);
9626 putFile16BitBE(file, scores->level_nr);
9627 putFile16BitBE(file, scores->num_entries);
9630 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9634 for (i = 0; i < scores->num_entries; i++)
9636 int name_size = strlen(scores->entry[i].name);
9638 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9639 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9643 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9647 for (i = 0; i < scores->num_entries; i++)
9648 putFile16BitBE(file, scores->entry[i].score);
9651 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9655 for (i = 0; i < scores->num_entries; i++)
9656 putFile32BitBE(file, scores->entry[i].score);
9659 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9663 for (i = 0; i < scores->num_entries; i++)
9664 putFile32BitBE(file, scores->entry[i].time);
9667 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9671 for (i = 0; i < scores->num_entries; i++)
9673 int size = strlen(scores->entry[i].tape_basename);
9675 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9676 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9680 static void SaveScoreToFilename(char *filename)
9683 int info_chunk_size;
9684 int name_chunk_size;
9685 int scor_chunk_size;
9686 int sc4r_chunk_size;
9687 int time_chunk_size;
9688 int tape_chunk_size;
9689 boolean has_large_score_values;
9692 if (!(file = fopen(filename, MODE_WRITE)))
9694 Warn("cannot save score file '%s'", filename);
9699 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9700 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9701 scor_chunk_size = scores.num_entries * 2;
9702 sc4r_chunk_size = scores.num_entries * 4;
9703 time_chunk_size = scores.num_entries * 4;
9704 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9706 has_large_score_values = FALSE;
9707 for (i = 0; i < scores.num_entries; i++)
9708 if (scores.entry[i].score > 0xffff)
9709 has_large_score_values = TRUE;
9711 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9712 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9714 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9715 SaveScore_VERS(file, &scores);
9717 putFileChunkBE(file, "INFO", info_chunk_size);
9718 SaveScore_INFO(file, &scores);
9720 putFileChunkBE(file, "NAME", name_chunk_size);
9721 SaveScore_NAME(file, &scores);
9723 if (has_large_score_values)
9725 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9726 SaveScore_SC4R(file, &scores);
9730 putFileChunkBE(file, "SCOR", scor_chunk_size);
9731 SaveScore_SCOR(file, &scores);
9734 putFileChunkBE(file, "TIME", time_chunk_size);
9735 SaveScore_TIME(file, &scores);
9737 putFileChunkBE(file, "TAPE", tape_chunk_size);
9738 SaveScore_TAPE(file, &scores);
9742 SetFilePermissions(filename, PERMS_PRIVATE);
9745 void SaveScore(int nr)
9747 char *filename = getScoreFilename(nr);
9750 // used instead of "leveldir_current->subdir" (for network games)
9751 InitScoreDirectory(levelset.identifier);
9753 scores.file_version = FILE_VERSION_ACTUAL;
9754 scores.game_version = GAME_VERSION_ACTUAL;
9756 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9757 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9758 scores.level_nr = level_nr;
9760 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9761 if (scores.entry[i].score == 0 &&
9762 scores.entry[i].time == 0 &&
9763 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9766 scores.num_entries = i;
9768 if (scores.num_entries == 0)
9771 SaveScoreToFilename(filename);
9774 static void LoadServerScoreFromCache(int nr)
9776 struct ScoreEntry score_entry;
9785 { &score_entry.score, FALSE, 0 },
9786 { &score_entry.time, FALSE, 0 },
9787 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9788 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9789 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9790 { &score_entry.id, FALSE, 0 },
9791 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9792 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9793 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9794 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9798 char *filename = getScoreCacheFilename(nr);
9799 SetupFileHash *score_hash = loadSetupFileHash(filename);
9802 server_scores.num_entries = 0;
9804 if (score_hash == NULL)
9807 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9809 score_entry = server_scores.entry[i];
9811 for (j = 0; score_mapping[j].value != NULL; j++)
9815 sprintf(token, "%02d.%d", i, j);
9817 char *value = getHashEntry(score_hash, token);
9822 if (score_mapping[j].is_string)
9824 char *score_value = (char *)score_mapping[j].value;
9825 int value_size = score_mapping[j].string_size;
9827 strncpy(score_value, value, value_size);
9828 score_value[value_size] = '\0';
9832 int *score_value = (int *)score_mapping[j].value;
9834 *score_value = atoi(value);
9837 server_scores.num_entries = i + 1;
9840 server_scores.entry[i] = score_entry;
9843 freeSetupFileHash(score_hash);
9846 void LoadServerScore(int nr, boolean download_score)
9848 if (!setup.use_api_server)
9851 // always start with reliable default values
9852 setServerScoreInfoToDefaults();
9854 // 1st step: load server scores from cache file (which may not exist)
9855 // (this should prevent reading it while the thread is writing to it)
9856 LoadServerScoreFromCache(nr);
9858 if (download_score && runtime.use_api_server)
9860 // 2nd step: download server scores from score server to cache file
9861 // (as thread, as it might time out if the server is not reachable)
9862 ApiGetScoreAsThread(nr);
9866 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9868 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9870 // if score tape not uploaded, ask for uploading missing tapes later
9871 if (!setup.has_remaining_tapes)
9872 setup.ask_for_remaining_tapes = TRUE;
9874 setup.provide_uploading_tapes = TRUE;
9875 setup.has_remaining_tapes = TRUE;
9877 SaveSetup_ServerSetup();
9880 void SaveServerScore(int nr, boolean tape_saved)
9882 if (!runtime.use_api_server)
9884 PrepareScoreTapesForUpload(leveldir_current->subdir);
9889 ApiAddScoreAsThread(nr, tape_saved, NULL);
9892 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9893 char *score_tape_filename)
9895 if (!runtime.use_api_server)
9898 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9901 void LoadLocalAndServerScore(int nr, boolean download_score)
9903 int last_added_local = scores.last_added_local;
9904 boolean force_last_added = scores.force_last_added;
9906 // needed if only showing server scores
9907 setScoreInfoToDefaults();
9909 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9912 // restore last added local score entry (before merging server scores)
9913 scores.last_added = scores.last_added_local = last_added_local;
9915 if (setup.use_api_server &&
9916 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9918 // load server scores from cache file and trigger update from server
9919 LoadServerScore(nr, download_score);
9921 // merge local scores with scores from server
9925 if (force_last_added)
9926 scores.force_last_added = force_last_added;
9930 // ============================================================================
9931 // setup file functions
9932 // ============================================================================
9934 #define TOKEN_STR_PLAYER_PREFIX "player_"
9937 static struct TokenInfo global_setup_tokens[] =
9941 &setup.player_name, "player_name"
9945 &setup.multiple_users, "multiple_users"
9949 &setup.sound, "sound"
9953 &setup.sound_loops, "repeating_sound_loops"
9957 &setup.sound_music, "background_music"
9961 &setup.sound_simple, "simple_sound_effects"
9965 &setup.toons, "toons"
9969 &setup.global_animations, "global_animations"
9973 &setup.scroll_delay, "scroll_delay"
9977 &setup.forced_scroll_delay, "forced_scroll_delay"
9981 &setup.scroll_delay_value, "scroll_delay_value"
9985 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9989 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9993 &setup.fade_screens, "fade_screens"
9997 &setup.autorecord, "automatic_tape_recording"
10001 &setup.autorecord_after_replay, "autorecord_after_replay"
10005 &setup.auto_pause_on_start, "auto_pause_on_start"
10009 &setup.show_titlescreen, "show_titlescreen"
10013 &setup.quick_doors, "quick_doors"
10017 &setup.team_mode, "team_mode"
10021 &setup.handicap, "handicap"
10025 &setup.skip_levels, "skip_levels"
10029 &setup.increment_levels, "increment_levels"
10033 &setup.auto_play_next_level, "auto_play_next_level"
10037 &setup.count_score_after_game, "count_score_after_game"
10041 &setup.show_scores_after_game, "show_scores_after_game"
10045 &setup.time_limit, "time_limit"
10049 &setup.fullscreen, "fullscreen"
10053 &setup.window_scaling_percent, "window_scaling_percent"
10057 &setup.window_scaling_quality, "window_scaling_quality"
10061 &setup.screen_rendering_mode, "screen_rendering_mode"
10065 &setup.vsync_mode, "vsync_mode"
10069 &setup.ask_on_escape, "ask_on_escape"
10073 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10077 &setup.ask_on_game_over, "ask_on_game_over"
10081 &setup.ask_on_quit_game, "ask_on_quit_game"
10085 &setup.ask_on_quit_program, "ask_on_quit_program"
10089 &setup.quick_switch, "quick_player_switch"
10093 &setup.input_on_focus, "input_on_focus"
10097 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10101 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10105 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10109 &setup.game_speed_extended, "game_speed_extended"
10113 &setup.game_frame_delay, "game_frame_delay"
10117 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10121 &setup.bd_skip_hatching, "bd_skip_hatching"
10125 &setup.bd_scroll_delay, "bd_scroll_delay"
10129 &setup.bd_smooth_movements, "bd_smooth_movements"
10133 &setup.sp_show_border_elements, "sp_show_border_elements"
10137 &setup.small_game_graphics, "small_game_graphics"
10141 &setup.show_load_save_buttons, "show_load_save_buttons"
10145 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10149 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10153 &setup.graphics_set, "graphics_set"
10157 &setup.sounds_set, "sounds_set"
10161 &setup.music_set, "music_set"
10165 &setup.override_level_graphics, "override_level_graphics"
10169 &setup.override_level_sounds, "override_level_sounds"
10173 &setup.override_level_music, "override_level_music"
10177 &setup.volume_simple, "volume_simple"
10181 &setup.volume_loops, "volume_loops"
10185 &setup.volume_music, "volume_music"
10189 &setup.network_mode, "network_mode"
10193 &setup.network_player_nr, "network_player"
10197 &setup.network_server_hostname, "network_server_hostname"
10201 &setup.touch.control_type, "touch.control_type"
10205 &setup.touch.move_distance, "touch.move_distance"
10209 &setup.touch.drop_distance, "touch.drop_distance"
10213 &setup.touch.transparency, "touch.transparency"
10217 &setup.touch.draw_outlined, "touch.draw_outlined"
10221 &setup.touch.draw_pressed, "touch.draw_pressed"
10225 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10229 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10233 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10237 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10241 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10245 static struct TokenInfo auto_setup_tokens[] =
10249 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10253 static struct TokenInfo server_setup_tokens[] =
10257 &setup.player_uuid, "player_uuid"
10261 &setup.player_version, "player_version"
10265 &setup.use_api_server, TEST_PREFIX "use_api_server"
10269 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10273 &setup.api_server_password, TEST_PREFIX "api_server_password"
10277 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10281 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10285 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10289 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10293 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10297 static struct TokenInfo editor_setup_tokens[] =
10301 &setup.editor.el_classic, "editor.el_classic"
10305 &setup.editor.el_custom, "editor.el_custom"
10309 &setup.editor.el_user_defined, "editor.el_user_defined"
10313 &setup.editor.el_dynamic, "editor.el_dynamic"
10317 &setup.editor.el_headlines, "editor.el_headlines"
10321 &setup.editor.show_element_token, "editor.show_element_token"
10325 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10329 static struct TokenInfo editor_cascade_setup_tokens[] =
10333 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10337 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10341 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10345 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10349 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10353 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10357 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10361 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10365 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10369 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10373 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10377 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10381 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10385 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10389 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10393 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10397 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10401 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10405 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10409 static struct TokenInfo shortcut_setup_tokens[] =
10413 &setup.shortcut.save_game, "shortcut.save_game"
10417 &setup.shortcut.load_game, "shortcut.load_game"
10421 &setup.shortcut.restart_game, "shortcut.restart_game"
10425 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10429 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10433 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10437 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10441 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10445 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10449 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10453 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10457 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10461 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10465 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10469 &setup.shortcut.tape_record, "shortcut.tape_record"
10473 &setup.shortcut.tape_play, "shortcut.tape_play"
10477 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10481 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10485 &setup.shortcut.sound_music, "shortcut.sound_music"
10489 &setup.shortcut.snap_left, "shortcut.snap_left"
10493 &setup.shortcut.snap_right, "shortcut.snap_right"
10497 &setup.shortcut.snap_up, "shortcut.snap_up"
10501 &setup.shortcut.snap_down, "shortcut.snap_down"
10505 static struct SetupInputInfo setup_input;
10506 static struct TokenInfo player_setup_tokens[] =
10510 &setup_input.use_joystick, ".use_joystick"
10514 &setup_input.joy.device_name, ".joy.device_name"
10518 &setup_input.joy.xleft, ".joy.xleft"
10522 &setup_input.joy.xmiddle, ".joy.xmiddle"
10526 &setup_input.joy.xright, ".joy.xright"
10530 &setup_input.joy.yupper, ".joy.yupper"
10534 &setup_input.joy.ymiddle, ".joy.ymiddle"
10538 &setup_input.joy.ylower, ".joy.ylower"
10542 &setup_input.joy.snap, ".joy.snap_field"
10546 &setup_input.joy.drop, ".joy.place_bomb"
10550 &setup_input.key.left, ".key.move_left"
10554 &setup_input.key.right, ".key.move_right"
10558 &setup_input.key.up, ".key.move_up"
10562 &setup_input.key.down, ".key.move_down"
10566 &setup_input.key.snap, ".key.snap_field"
10570 &setup_input.key.drop, ".key.place_bomb"
10574 static struct TokenInfo system_setup_tokens[] =
10578 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10582 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10586 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10590 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10594 static struct TokenInfo internal_setup_tokens[] =
10598 &setup.internal.program_title, "program_title"
10602 &setup.internal.program_version, "program_version"
10606 &setup.internal.program_author, "program_author"
10610 &setup.internal.program_email, "program_email"
10614 &setup.internal.program_website, "program_website"
10618 &setup.internal.program_copyright, "program_copyright"
10622 &setup.internal.program_company, "program_company"
10626 &setup.internal.program_icon_file, "program_icon_file"
10630 &setup.internal.default_graphics_set, "default_graphics_set"
10634 &setup.internal.default_sounds_set, "default_sounds_set"
10638 &setup.internal.default_music_set, "default_music_set"
10642 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10646 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10650 &setup.internal.fallback_music_file, "fallback_music_file"
10654 &setup.internal.default_level_series, "default_level_series"
10658 &setup.internal.default_window_width, "default_window_width"
10662 &setup.internal.default_window_height, "default_window_height"
10666 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10670 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10674 &setup.internal.create_user_levelset, "create_user_levelset"
10678 &setup.internal.info_screens_from_main, "info_screens_from_main"
10682 &setup.internal.menu_game, "menu_game"
10686 &setup.internal.menu_engines, "menu_engines"
10690 &setup.internal.menu_editor, "menu_editor"
10694 &setup.internal.menu_graphics, "menu_graphics"
10698 &setup.internal.menu_sound, "menu_sound"
10702 &setup.internal.menu_artwork, "menu_artwork"
10706 &setup.internal.menu_input, "menu_input"
10710 &setup.internal.menu_touch, "menu_touch"
10714 &setup.internal.menu_shortcuts, "menu_shortcuts"
10718 &setup.internal.menu_exit, "menu_exit"
10722 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10726 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10730 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10734 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10738 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10742 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10746 &setup.internal.info_title, "info_title"
10750 &setup.internal.info_elements, "info_elements"
10754 &setup.internal.info_music, "info_music"
10758 &setup.internal.info_credits, "info_credits"
10762 &setup.internal.info_program, "info_program"
10766 &setup.internal.info_version, "info_version"
10770 &setup.internal.info_levelset, "info_levelset"
10774 &setup.internal.info_exit, "info_exit"
10778 static struct TokenInfo debug_setup_tokens[] =
10782 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10786 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10790 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10794 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10798 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10802 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10806 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10810 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10814 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10818 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10822 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10826 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10830 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10834 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10838 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10842 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10846 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10850 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10854 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10858 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10862 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10865 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10869 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10873 &setup.debug.xsn_mode, "debug.xsn_mode"
10877 &setup.debug.xsn_percent, "debug.xsn_percent"
10881 static struct TokenInfo options_setup_tokens[] =
10885 &setup.options.verbose, "options.verbose"
10889 &setup.options.debug, "options.debug"
10893 &setup.options.debug_mode, "options.debug_mode"
10897 static void setSetupInfoToDefaults(struct SetupInfo *si)
10901 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10903 si->multiple_users = TRUE;
10906 si->sound_loops = TRUE;
10907 si->sound_music = TRUE;
10908 si->sound_simple = TRUE;
10910 si->global_animations = TRUE;
10911 si->scroll_delay = TRUE;
10912 si->forced_scroll_delay = FALSE;
10913 si->scroll_delay_value = STD_SCROLL_DELAY;
10914 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10915 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10916 si->fade_screens = TRUE;
10917 si->autorecord = TRUE;
10918 si->autorecord_after_replay = TRUE;
10919 si->auto_pause_on_start = FALSE;
10920 si->show_titlescreen = TRUE;
10921 si->quick_doors = FALSE;
10922 si->team_mode = FALSE;
10923 si->handicap = TRUE;
10924 si->skip_levels = TRUE;
10925 si->increment_levels = TRUE;
10926 si->auto_play_next_level = TRUE;
10927 si->count_score_after_game = TRUE;
10928 si->show_scores_after_game = TRUE;
10929 si->time_limit = TRUE;
10930 si->fullscreen = FALSE;
10931 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10932 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10933 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10934 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10935 si->ask_on_escape = TRUE;
10936 si->ask_on_escape_editor = TRUE;
10937 si->ask_on_game_over = TRUE;
10938 si->ask_on_quit_game = TRUE;
10939 si->ask_on_quit_program = TRUE;
10940 si->quick_switch = FALSE;
10941 si->input_on_focus = FALSE;
10942 si->prefer_aga_graphics = TRUE;
10943 si->prefer_lowpass_sounds = FALSE;
10944 si->prefer_extra_panel_items = TRUE;
10945 si->game_speed_extended = FALSE;
10946 si->game_frame_delay = GAME_FRAME_DELAY;
10947 si->bd_skip_uncovering = FALSE;
10948 si->bd_skip_hatching = FALSE;
10949 si->bd_scroll_delay = TRUE;
10950 si->bd_smooth_movements = AUTO;
10951 si->sp_show_border_elements = FALSE;
10952 si->small_game_graphics = FALSE;
10953 si->show_load_save_buttons = FALSE;
10954 si->show_undo_redo_buttons = FALSE;
10955 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10957 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10958 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10959 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10961 si->override_level_graphics = FALSE;
10962 si->override_level_sounds = FALSE;
10963 si->override_level_music = FALSE;
10965 si->volume_simple = 100; // percent
10966 si->volume_loops = 100; // percent
10967 si->volume_music = 100; // percent
10969 si->network_mode = FALSE;
10970 si->network_player_nr = 0; // first player
10971 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10973 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10974 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10975 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10976 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10977 si->touch.draw_outlined = TRUE;
10978 si->touch.draw_pressed = TRUE;
10980 for (i = 0; i < 2; i++)
10982 char *default_grid_button[6][2] =
10988 { "111222", " vv " },
10989 { "111222", " vv " }
10991 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10992 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10993 int min_xsize = MIN(6, grid_xsize);
10994 int min_ysize = MIN(6, grid_ysize);
10995 int startx = grid_xsize - min_xsize;
10996 int starty = grid_ysize - min_ysize;
10999 // virtual buttons grid can only be set to defaults if video is initialized
11000 // (this will be repeated if virtual buttons are not loaded from setup file)
11001 if (video.initialized)
11003 si->touch.grid_xsize[i] = grid_xsize;
11004 si->touch.grid_ysize[i] = grid_ysize;
11008 si->touch.grid_xsize[i] = -1;
11009 si->touch.grid_ysize[i] = -1;
11012 for (x = 0; x < MAX_GRID_XSIZE; x++)
11013 for (y = 0; y < MAX_GRID_YSIZE; y++)
11014 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11016 for (x = 0; x < min_xsize; x++)
11017 for (y = 0; y < min_ysize; y++)
11018 si->touch.grid_button[i][x][starty + y] =
11019 default_grid_button[y][0][x];
11021 for (x = 0; x < min_xsize; x++)
11022 for (y = 0; y < min_ysize; y++)
11023 si->touch.grid_button[i][startx + x][starty + y] =
11024 default_grid_button[y][1][x];
11027 si->touch.grid_initialized = video.initialized;
11029 si->touch.overlay_buttons = FALSE;
11031 si->editor.el_boulderdash = TRUE;
11032 si->editor.el_boulderdash_native = TRUE;
11033 si->editor.el_emerald_mine = TRUE;
11034 si->editor.el_emerald_mine_club = TRUE;
11035 si->editor.el_more = TRUE;
11036 si->editor.el_sokoban = TRUE;
11037 si->editor.el_supaplex = TRUE;
11038 si->editor.el_diamond_caves = TRUE;
11039 si->editor.el_dx_boulderdash = TRUE;
11041 si->editor.el_mirror_magic = TRUE;
11042 si->editor.el_deflektor = TRUE;
11044 si->editor.el_chars = TRUE;
11045 si->editor.el_steel_chars = TRUE;
11047 si->editor.el_classic = TRUE;
11048 si->editor.el_custom = TRUE;
11050 si->editor.el_user_defined = FALSE;
11051 si->editor.el_dynamic = TRUE;
11053 si->editor.el_headlines = TRUE;
11055 si->editor.show_element_token = FALSE;
11057 si->editor.show_read_only_warning = TRUE;
11059 si->editor.use_template_for_new_levels = TRUE;
11061 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11062 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11063 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11064 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11065 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11067 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11068 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11069 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11070 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11071 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11073 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11074 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11075 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11076 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11077 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11078 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11080 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11081 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11082 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11084 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11085 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11086 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11087 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11089 for (i = 0; i < MAX_PLAYERS; i++)
11091 si->input[i].use_joystick = FALSE;
11092 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11093 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11094 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11095 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11096 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11097 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11098 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11099 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11100 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11101 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11102 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11103 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11104 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11105 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11106 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11109 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11110 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11111 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11112 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11114 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11115 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11116 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11117 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11118 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11119 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11120 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11122 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11124 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11125 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11126 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11128 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11129 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11130 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11132 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11133 si->internal.choose_from_top_leveldir = FALSE;
11134 si->internal.show_scaling_in_title = TRUE;
11135 si->internal.create_user_levelset = TRUE;
11136 si->internal.info_screens_from_main = FALSE;
11138 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11139 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11141 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11142 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11143 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11144 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11145 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11146 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11147 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11148 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11149 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11150 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11152 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11153 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11154 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11155 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11156 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11157 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11158 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11159 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11160 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11161 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11163 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11164 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11166 si->debug.show_frames_per_second = FALSE;
11168 si->debug.xsn_mode = AUTO;
11169 si->debug.xsn_percent = 0;
11171 si->options.verbose = FALSE;
11172 si->options.debug = FALSE;
11173 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11175 #if defined(PLATFORM_ANDROID)
11176 si->fullscreen = TRUE;
11177 si->touch.overlay_buttons = TRUE;
11180 setHideSetupEntry(&setup.debug.xsn_mode);
11183 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11185 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11188 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11190 si->player_uuid = NULL; // (will be set later)
11191 si->player_version = 1; // (will be set later)
11193 si->use_api_server = TRUE;
11194 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11195 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11196 si->ask_for_uploading_tapes = TRUE;
11197 si->ask_for_remaining_tapes = FALSE;
11198 si->provide_uploading_tapes = TRUE;
11199 si->ask_for_using_api_server = TRUE;
11200 si->has_remaining_tapes = FALSE;
11203 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11205 si->editor_cascade.el_bd = TRUE;
11206 si->editor_cascade.el_bd_native = TRUE;
11207 si->editor_cascade.el_em = TRUE;
11208 si->editor_cascade.el_emc = TRUE;
11209 si->editor_cascade.el_rnd = TRUE;
11210 si->editor_cascade.el_sb = TRUE;
11211 si->editor_cascade.el_sp = TRUE;
11212 si->editor_cascade.el_dc = TRUE;
11213 si->editor_cascade.el_dx = TRUE;
11215 si->editor_cascade.el_mm = TRUE;
11216 si->editor_cascade.el_df = TRUE;
11218 si->editor_cascade.el_chars = FALSE;
11219 si->editor_cascade.el_steel_chars = FALSE;
11220 si->editor_cascade.el_ce = FALSE;
11221 si->editor_cascade.el_ge = FALSE;
11222 si->editor_cascade.el_es = FALSE;
11223 si->editor_cascade.el_ref = FALSE;
11224 si->editor_cascade.el_user = FALSE;
11225 si->editor_cascade.el_dynamic = FALSE;
11228 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11230 static char *getHideSetupToken(void *setup_value)
11232 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11234 if (setup_value != NULL)
11235 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11237 return hide_setup_token;
11240 void setHideSetupEntry(void *setup_value)
11242 char *hide_setup_token = getHideSetupToken(setup_value);
11244 if (hide_setup_hash == NULL)
11245 hide_setup_hash = newSetupFileHash();
11247 if (setup_value != NULL)
11248 setHashEntry(hide_setup_hash, hide_setup_token, "");
11251 void removeHideSetupEntry(void *setup_value)
11253 char *hide_setup_token = getHideSetupToken(setup_value);
11255 if (setup_value != NULL)
11256 removeHashEntry(hide_setup_hash, hide_setup_token);
11259 boolean hideSetupEntry(void *setup_value)
11261 char *hide_setup_token = getHideSetupToken(setup_value);
11263 return (setup_value != NULL &&
11264 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11267 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11268 struct TokenInfo *token_info,
11269 int token_nr, char *token_text)
11271 char *token_hide_text = getStringCat2(token_text, ".hide");
11272 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11274 // set the value of this setup option in the setup option structure
11275 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11277 // check if this setup option should be hidden in the setup menu
11278 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11279 setHideSetupEntry(token_info[token_nr].value);
11281 free(token_hide_text);
11284 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11285 struct TokenInfo *token_info,
11288 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11289 token_info[token_nr].text);
11292 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11296 if (!setup_file_hash)
11299 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11300 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11302 setup.touch.grid_initialized = TRUE;
11303 for (i = 0; i < 2; i++)
11305 int grid_xsize = setup.touch.grid_xsize[i];
11306 int grid_ysize = setup.touch.grid_ysize[i];
11309 // if virtual buttons are not loaded from setup file, repeat initializing
11310 // virtual buttons grid with default values later when video is initialized
11311 if (grid_xsize == -1 ||
11314 setup.touch.grid_initialized = FALSE;
11319 for (y = 0; y < grid_ysize; y++)
11321 char token_string[MAX_LINE_LEN];
11323 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11325 char *value_string = getHashEntry(setup_file_hash, token_string);
11327 if (value_string == NULL)
11330 for (x = 0; x < grid_xsize; x++)
11332 char c = value_string[x];
11334 setup.touch.grid_button[i][x][y] =
11335 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11340 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11341 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11343 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11344 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11346 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11350 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11352 setup_input = setup.input[pnr];
11353 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11355 char full_token[100];
11357 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11358 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11361 setup.input[pnr] = setup_input;
11364 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11365 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11367 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11368 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11370 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11371 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11373 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11374 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11376 setHideRelatedSetupEntries();
11379 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11383 if (!setup_file_hash)
11386 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11387 setSetupInfo(auto_setup_tokens, i,
11388 getHashEntry(setup_file_hash,
11389 auto_setup_tokens[i].text));
11392 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11396 if (!setup_file_hash)
11399 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11400 setSetupInfo(server_setup_tokens, i,
11401 getHashEntry(setup_file_hash,
11402 server_setup_tokens[i].text));
11405 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11409 if (!setup_file_hash)
11412 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11413 setSetupInfo(editor_cascade_setup_tokens, i,
11414 getHashEntry(setup_file_hash,
11415 editor_cascade_setup_tokens[i].text));
11418 void LoadUserNames(void)
11420 int last_user_nr = user.nr;
11423 if (global.user_names != NULL)
11425 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11426 checked_free(global.user_names[i]);
11428 checked_free(global.user_names);
11431 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11433 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11437 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11439 if (setup_file_hash)
11441 char *player_name = getHashEntry(setup_file_hash, "player_name");
11443 global.user_names[i] = getFixedUserName(player_name);
11445 freeSetupFileHash(setup_file_hash);
11448 if (global.user_names[i] == NULL)
11449 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11452 user.nr = last_user_nr;
11455 void LoadSetupFromFilename(char *filename)
11457 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11459 if (setup_file_hash)
11461 decodeSetupFileHash_Default(setup_file_hash);
11463 freeSetupFileHash(setup_file_hash);
11467 Debug("setup", "using default setup values");
11471 static void LoadSetup_SpecialPostProcessing(void)
11473 char *player_name_new;
11475 // needed to work around problems with fixed length strings
11476 player_name_new = getFixedUserName(setup.player_name);
11477 free(setup.player_name);
11478 setup.player_name = player_name_new;
11480 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11481 if (setup.scroll_delay == FALSE)
11483 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11484 setup.scroll_delay = TRUE; // now always "on"
11487 // make sure that scroll delay value stays inside valid range
11488 setup.scroll_delay_value =
11489 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11492 void LoadSetup_Default(void)
11496 // always start with reliable default values
11497 setSetupInfoToDefaults(&setup);
11499 // try to load setup values from default setup file
11500 filename = getDefaultSetupFilename();
11502 if (fileExists(filename))
11503 LoadSetupFromFilename(filename);
11505 // try to load setup values from platform setup file
11506 filename = getPlatformSetupFilename();
11508 if (fileExists(filename))
11509 LoadSetupFromFilename(filename);
11511 // try to load setup values from user setup file
11512 filename = getSetupFilename();
11514 LoadSetupFromFilename(filename);
11516 LoadSetup_SpecialPostProcessing();
11519 void LoadSetup_AutoSetup(void)
11521 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11522 SetupFileHash *setup_file_hash = NULL;
11524 // always start with reliable default values
11525 setSetupInfoToDefaults_AutoSetup(&setup);
11527 setup_file_hash = loadSetupFileHash(filename);
11529 if (setup_file_hash)
11531 decodeSetupFileHash_AutoSetup(setup_file_hash);
11533 freeSetupFileHash(setup_file_hash);
11539 void LoadSetup_ServerSetup(void)
11541 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11542 SetupFileHash *setup_file_hash = NULL;
11544 // always start with reliable default values
11545 setSetupInfoToDefaults_ServerSetup(&setup);
11547 setup_file_hash = loadSetupFileHash(filename);
11549 if (setup_file_hash)
11551 decodeSetupFileHash_ServerSetup(setup_file_hash);
11553 freeSetupFileHash(setup_file_hash);
11558 if (setup.player_uuid == NULL)
11560 // player UUID does not yet exist in setup file
11561 setup.player_uuid = getStringCopy(getUUID());
11562 setup.player_version = 2;
11564 SaveSetup_ServerSetup();
11568 void LoadSetup_EditorCascade(void)
11570 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11571 SetupFileHash *setup_file_hash = NULL;
11573 // always start with reliable default values
11574 setSetupInfoToDefaults_EditorCascade(&setup);
11576 setup_file_hash = loadSetupFileHash(filename);
11578 if (setup_file_hash)
11580 decodeSetupFileHash_EditorCascade(setup_file_hash);
11582 freeSetupFileHash(setup_file_hash);
11588 void LoadSetup(void)
11590 LoadSetup_Default();
11591 LoadSetup_AutoSetup();
11592 LoadSetup_ServerSetup();
11593 LoadSetup_EditorCascade();
11596 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11597 char *mapping_line)
11599 char mapping_guid[MAX_LINE_LEN];
11600 char *mapping_start, *mapping_end;
11602 // get GUID from game controller mapping line: copy complete line
11603 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11604 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11606 // get GUID from game controller mapping line: cut after GUID part
11607 mapping_start = strchr(mapping_guid, ',');
11608 if (mapping_start != NULL)
11609 *mapping_start = '\0';
11611 // cut newline from game controller mapping line
11612 mapping_end = strchr(mapping_line, '\n');
11613 if (mapping_end != NULL)
11614 *mapping_end = '\0';
11616 // add mapping entry to game controller mappings hash
11617 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11620 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11625 if (!(file = fopen(filename, MODE_READ)))
11627 Warn("cannot read game controller mappings file '%s'", filename);
11632 while (!feof(file))
11634 char line[MAX_LINE_LEN];
11636 if (!fgets(line, MAX_LINE_LEN, file))
11639 addGameControllerMappingToHash(mappings_hash, line);
11645 void SaveSetup_Default(void)
11647 char *filename = getSetupFilename();
11651 InitUserDataDirectory();
11653 if (!(file = fopen(filename, MODE_WRITE)))
11655 Warn("cannot write setup file '%s'", filename);
11660 fprintFileHeader(file, SETUP_FILENAME);
11662 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11664 // just to make things nicer :)
11665 if (global_setup_tokens[i].value == &setup.multiple_users ||
11666 global_setup_tokens[i].value == &setup.sound ||
11667 global_setup_tokens[i].value == &setup.graphics_set ||
11668 global_setup_tokens[i].value == &setup.volume_simple ||
11669 global_setup_tokens[i].value == &setup.network_mode ||
11670 global_setup_tokens[i].value == &setup.touch.control_type ||
11671 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11672 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11673 fprintf(file, "\n");
11675 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11678 for (i = 0; i < 2; i++)
11680 int grid_xsize = setup.touch.grid_xsize[i];
11681 int grid_ysize = setup.touch.grid_ysize[i];
11684 fprintf(file, "\n");
11686 for (y = 0; y < grid_ysize; y++)
11688 char token_string[MAX_LINE_LEN];
11689 char value_string[MAX_LINE_LEN];
11691 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11693 for (x = 0; x < grid_xsize; x++)
11695 char c = setup.touch.grid_button[i][x][y];
11697 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11700 value_string[grid_xsize] = '\0';
11702 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11706 fprintf(file, "\n");
11707 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11708 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11710 fprintf(file, "\n");
11711 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11712 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11714 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11718 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11719 fprintf(file, "\n");
11721 setup_input = setup.input[pnr];
11722 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11723 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11726 fprintf(file, "\n");
11727 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11728 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11730 // (internal setup values not saved to user setup file)
11732 fprintf(file, "\n");
11733 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11734 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11735 setup.debug.xsn_mode != AUTO)
11736 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11738 fprintf(file, "\n");
11739 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11740 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11744 SetFilePermissions(filename, PERMS_PRIVATE);
11747 void SaveSetup_AutoSetup(void)
11749 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11753 InitUserDataDirectory();
11755 if (!(file = fopen(filename, MODE_WRITE)))
11757 Warn("cannot write auto setup file '%s'", filename);
11764 fprintFileHeader(file, AUTOSETUP_FILENAME);
11766 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11767 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11771 SetFilePermissions(filename, PERMS_PRIVATE);
11776 void SaveSetup_ServerSetup(void)
11778 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11782 InitUserDataDirectory();
11784 if (!(file = fopen(filename, MODE_WRITE)))
11786 Warn("cannot write server setup file '%s'", filename);
11793 fprintFileHeader(file, SERVERSETUP_FILENAME);
11795 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11797 // just to make things nicer :)
11798 if (server_setup_tokens[i].value == &setup.use_api_server)
11799 fprintf(file, "\n");
11801 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11806 SetFilePermissions(filename, PERMS_PRIVATE);
11811 void SaveSetup_EditorCascade(void)
11813 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11817 InitUserDataDirectory();
11819 if (!(file = fopen(filename, MODE_WRITE)))
11821 Warn("cannot write editor cascade state file '%s'", filename);
11828 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11830 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11831 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11835 SetFilePermissions(filename, PERMS_PRIVATE);
11840 void SaveSetup(void)
11842 SaveSetup_Default();
11843 SaveSetup_AutoSetup();
11844 SaveSetup_ServerSetup();
11845 SaveSetup_EditorCascade();
11848 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11853 if (!(file = fopen(filename, MODE_WRITE)))
11855 Warn("cannot write game controller mappings file '%s'", filename);
11860 BEGIN_HASH_ITERATION(mappings_hash, itr)
11862 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11864 END_HASH_ITERATION(mappings_hash, itr)
11869 void SaveSetup_AddGameControllerMapping(char *mapping)
11871 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11872 SetupFileHash *mappings_hash = newSetupFileHash();
11874 InitUserDataDirectory();
11876 // load existing personal game controller mappings
11877 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11879 // add new mapping to personal game controller mappings
11880 addGameControllerMappingToHash(mappings_hash, mapping);
11882 // save updated personal game controller mappings
11883 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11885 freeSetupFileHash(mappings_hash);
11889 void LoadCustomElementDescriptions(void)
11891 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11892 SetupFileHash *setup_file_hash;
11895 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11897 if (element_info[i].custom_description != NULL)
11899 free(element_info[i].custom_description);
11900 element_info[i].custom_description = NULL;
11904 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11907 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11909 char *token = getStringCat2(element_info[i].token_name, ".name");
11910 char *value = getHashEntry(setup_file_hash, token);
11913 element_info[i].custom_description = getStringCopy(value);
11918 freeSetupFileHash(setup_file_hash);
11921 static int getElementFromToken(char *token)
11923 char *value = getHashEntry(element_token_hash, token);
11926 return atoi(value);
11928 Warn("unknown element token '%s'", token);
11930 return EL_UNDEFINED;
11933 void FreeGlobalAnimEventInfo(void)
11935 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11937 if (gaei->event_list == NULL)
11942 for (i = 0; i < gaei->num_event_lists; i++)
11944 checked_free(gaei->event_list[i]->event_value);
11945 checked_free(gaei->event_list[i]);
11948 checked_free(gaei->event_list);
11950 gaei->event_list = NULL;
11951 gaei->num_event_lists = 0;
11954 static int AddGlobalAnimEventList(void)
11956 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11957 int list_pos = gaei->num_event_lists++;
11959 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11960 sizeof(struct GlobalAnimEventListInfo *));
11962 gaei->event_list[list_pos] =
11963 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11965 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11967 gaeli->event_value = NULL;
11968 gaeli->num_event_values = 0;
11973 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11975 // do not add empty global animation events
11976 if (event_value == ANIM_EVENT_NONE)
11979 // if list position is undefined, create new list
11980 if (list_pos == ANIM_EVENT_UNDEFINED)
11981 list_pos = AddGlobalAnimEventList();
11983 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11984 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11985 int value_pos = gaeli->num_event_values++;
11987 gaeli->event_value = checked_realloc(gaeli->event_value,
11988 gaeli->num_event_values * sizeof(int *));
11990 gaeli->event_value[value_pos] = event_value;
11995 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11997 if (list_pos == ANIM_EVENT_UNDEFINED)
11998 return ANIM_EVENT_NONE;
12000 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12001 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12003 return gaeli->event_value[value_pos];
12006 int GetGlobalAnimEventValueCount(int list_pos)
12008 if (list_pos == ANIM_EVENT_UNDEFINED)
12011 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12012 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12014 return gaeli->num_event_values;
12017 // This function checks if a string <s> of the format "string1, string2, ..."
12018 // exactly contains a string <s_contained>.
12020 static boolean string_has_parameter(char *s, char *s_contained)
12024 if (s == NULL || s_contained == NULL)
12027 if (strlen(s_contained) > strlen(s))
12030 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12032 char next_char = s[strlen(s_contained)];
12034 // check if next character is delimiter or whitespace
12035 if (next_char == ',' || next_char == '\0' ||
12036 next_char == ' ' || next_char == '\t')
12040 // check if string contains another parameter string after a comma
12041 substring = strchr(s, ',');
12042 if (substring == NULL) // string does not contain a comma
12045 // advance string pointer to next character after the comma
12048 // skip potential whitespaces after the comma
12049 while (*substring == ' ' || *substring == '\t')
12052 return string_has_parameter(substring, s_contained);
12055 static int get_anim_parameter_value_ce(char *s)
12058 char *pattern_1 = "ce_change:custom_";
12059 char *pattern_2 = ".page_";
12060 int pattern_1_len = strlen(pattern_1);
12061 char *matching_char = strstr(s_ptr, pattern_1);
12062 int result = ANIM_EVENT_NONE;
12064 if (matching_char == NULL)
12065 return ANIM_EVENT_NONE;
12067 result = ANIM_EVENT_CE_CHANGE;
12069 s_ptr = matching_char + pattern_1_len;
12071 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12072 if (*s_ptr >= '0' && *s_ptr <= '9')
12074 int gic_ce_nr = (*s_ptr++ - '0');
12076 if (*s_ptr >= '0' && *s_ptr <= '9')
12078 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12080 if (*s_ptr >= '0' && *s_ptr <= '9')
12081 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12084 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12085 return ANIM_EVENT_NONE;
12087 // custom element stored as 0 to 255
12090 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12094 // invalid custom element number specified
12096 return ANIM_EVENT_NONE;
12099 // check for change page number ("page_X" or "page_XX") (optional)
12100 if (strPrefix(s_ptr, pattern_2))
12102 s_ptr += strlen(pattern_2);
12104 if (*s_ptr >= '0' && *s_ptr <= '9')
12106 int gic_page_nr = (*s_ptr++ - '0');
12108 if (*s_ptr >= '0' && *s_ptr <= '9')
12109 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12111 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12112 return ANIM_EVENT_NONE;
12114 // change page stored as 1 to 32 (0 means "all change pages")
12116 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12120 // invalid animation part number specified
12122 return ANIM_EVENT_NONE;
12126 // discard result if next character is neither delimiter nor whitespace
12127 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12128 *s_ptr == ' ' || *s_ptr == '\t'))
12129 return ANIM_EVENT_NONE;
12134 static int get_anim_parameter_value(char *s)
12136 int event_value[] =
12144 char *pattern_1[] =
12152 char *pattern_2 = ".part_";
12153 char *matching_char = NULL;
12155 int pattern_1_len = 0;
12156 int result = ANIM_EVENT_NONE;
12159 result = get_anim_parameter_value_ce(s);
12161 if (result != ANIM_EVENT_NONE)
12164 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12166 matching_char = strstr(s_ptr, pattern_1[i]);
12167 pattern_1_len = strlen(pattern_1[i]);
12168 result = event_value[i];
12170 if (matching_char != NULL)
12174 if (matching_char == NULL)
12175 return ANIM_EVENT_NONE;
12177 s_ptr = matching_char + pattern_1_len;
12179 // check for main animation number ("anim_X" or "anim_XX")
12180 if (*s_ptr >= '0' && *s_ptr <= '9')
12182 int gic_anim_nr = (*s_ptr++ - '0');
12184 if (*s_ptr >= '0' && *s_ptr <= '9')
12185 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12187 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12188 return ANIM_EVENT_NONE;
12190 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12194 // invalid main animation number specified
12196 return ANIM_EVENT_NONE;
12199 // check for animation part number ("part_X" or "part_XX") (optional)
12200 if (strPrefix(s_ptr, pattern_2))
12202 s_ptr += strlen(pattern_2);
12204 if (*s_ptr >= '0' && *s_ptr <= '9')
12206 int gic_part_nr = (*s_ptr++ - '0');
12208 if (*s_ptr >= '0' && *s_ptr <= '9')
12209 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12211 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12212 return ANIM_EVENT_NONE;
12214 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12218 // invalid animation part number specified
12220 return ANIM_EVENT_NONE;
12224 // discard result if next character is neither delimiter nor whitespace
12225 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12226 *s_ptr == ' ' || *s_ptr == '\t'))
12227 return ANIM_EVENT_NONE;
12232 static int get_anim_parameter_values(char *s)
12234 int list_pos = ANIM_EVENT_UNDEFINED;
12235 int event_value = ANIM_EVENT_DEFAULT;
12237 if (string_has_parameter(s, "any"))
12238 event_value |= ANIM_EVENT_ANY;
12240 if (string_has_parameter(s, "click:self") ||
12241 string_has_parameter(s, "click") ||
12242 string_has_parameter(s, "self"))
12243 event_value |= ANIM_EVENT_SELF;
12245 if (string_has_parameter(s, "unclick:any"))
12246 event_value |= ANIM_EVENT_UNCLICK_ANY;
12248 // if animation event found, add it to global animation event list
12249 if (event_value != ANIM_EVENT_NONE)
12250 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12254 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12255 event_value = get_anim_parameter_value(s);
12257 // if animation event found, add it to global animation event list
12258 if (event_value != ANIM_EVENT_NONE)
12259 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12261 // continue with next part of the string, starting with next comma
12262 s = strchr(s + 1, ',');
12268 static int get_anim_action_parameter_value(char *token)
12270 // check most common default case first to massively speed things up
12271 if (strEqual(token, ARG_UNDEFINED))
12272 return ANIM_EVENT_ACTION_NONE;
12274 int result = getImageIDFromToken(token);
12278 char *gfx_token = getStringCat2("gfx.", token);
12280 result = getImageIDFromToken(gfx_token);
12282 checked_free(gfx_token);
12287 Key key = getKeyFromX11KeyName(token);
12289 if (key != KSYM_UNDEFINED)
12290 result = -(int)key;
12297 result = get_hash_from_string(token); // unsigned int => int
12298 result = ABS(result); // may be negative now
12299 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12301 setHashEntry(anim_url_hash, int2str(result, 0), token);
12306 result = ANIM_EVENT_ACTION_NONE;
12311 int get_parameter_value(char *value_raw, char *suffix, int type)
12313 char *value = getStringToLower(value_raw);
12314 int result = 0; // probably a save default value
12316 if (strEqual(suffix, ".direction"))
12318 result = (strEqual(value, "left") ? MV_LEFT :
12319 strEqual(value, "right") ? MV_RIGHT :
12320 strEqual(value, "up") ? MV_UP :
12321 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12323 else if (strEqual(suffix, ".position"))
12325 result = (strEqual(value, "left") ? POS_LEFT :
12326 strEqual(value, "right") ? POS_RIGHT :
12327 strEqual(value, "top") ? POS_TOP :
12328 strEqual(value, "upper") ? POS_UPPER :
12329 strEqual(value, "middle") ? POS_MIDDLE :
12330 strEqual(value, "lower") ? POS_LOWER :
12331 strEqual(value, "bottom") ? POS_BOTTOM :
12332 strEqual(value, "any") ? POS_ANY :
12333 strEqual(value, "ce") ? POS_CE :
12334 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12335 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12337 else if (strEqual(suffix, ".align"))
12339 result = (strEqual(value, "left") ? ALIGN_LEFT :
12340 strEqual(value, "right") ? ALIGN_RIGHT :
12341 strEqual(value, "center") ? ALIGN_CENTER :
12342 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12344 else if (strEqual(suffix, ".valign"))
12346 result = (strEqual(value, "top") ? VALIGN_TOP :
12347 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12348 strEqual(value, "middle") ? VALIGN_MIDDLE :
12349 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12351 else if (strEqual(suffix, ".anim_mode"))
12353 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12354 string_has_parameter(value, "loop") ? ANIM_LOOP :
12355 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12356 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12357 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12358 string_has_parameter(value, "random") ? ANIM_RANDOM :
12359 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12360 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12361 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12362 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12363 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12364 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12365 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12366 string_has_parameter(value, "all") ? ANIM_ALL :
12367 string_has_parameter(value, "tiled") ? ANIM_TILED :
12368 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12371 if (string_has_parameter(value, "once"))
12372 result |= ANIM_ONCE;
12374 if (string_has_parameter(value, "reverse"))
12375 result |= ANIM_REVERSE;
12377 if (string_has_parameter(value, "opaque_player"))
12378 result |= ANIM_OPAQUE_PLAYER;
12380 if (string_has_parameter(value, "static_panel"))
12381 result |= ANIM_STATIC_PANEL;
12383 else if (strEqual(suffix, ".init_event") ||
12384 strEqual(suffix, ".anim_event"))
12386 result = get_anim_parameter_values(value);
12388 else if (strEqual(suffix, ".init_delay_action") ||
12389 strEqual(suffix, ".anim_delay_action") ||
12390 strEqual(suffix, ".post_delay_action") ||
12391 strEqual(suffix, ".init_event_action") ||
12392 strEqual(suffix, ".anim_event_action"))
12394 result = get_anim_action_parameter_value(value_raw);
12396 else if (strEqual(suffix, ".class"))
12398 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12399 get_hash_from_string(value));
12401 else if (strEqual(suffix, ".style"))
12403 result = STYLE_DEFAULT;
12405 if (string_has_parameter(value, "accurate_borders"))
12406 result |= STYLE_ACCURATE_BORDERS;
12408 if (string_has_parameter(value, "inner_corners"))
12409 result |= STYLE_INNER_CORNERS;
12411 if (string_has_parameter(value, "reverse"))
12412 result |= STYLE_REVERSE;
12414 if (string_has_parameter(value, "leftmost_position"))
12415 result |= STYLE_LEFTMOST_POSITION;
12417 if (string_has_parameter(value, "block_clicks"))
12418 result |= STYLE_BLOCK;
12420 if (string_has_parameter(value, "passthrough_clicks"))
12421 result |= STYLE_PASSTHROUGH;
12423 if (string_has_parameter(value, "multiple_actions"))
12424 result |= STYLE_MULTIPLE_ACTIONS;
12426 if (string_has_parameter(value, "consume_ce_event"))
12427 result |= STYLE_CONSUME_CE_EVENT;
12429 else if (strEqual(suffix, ".fade_mode"))
12431 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12432 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12433 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12434 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12435 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12436 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12437 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12438 FADE_MODE_DEFAULT);
12440 else if (strEqual(suffix, ".auto_delay_unit"))
12442 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12443 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12444 AUTO_DELAY_UNIT_DEFAULT);
12446 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12448 result = gfx.get_font_from_token_function(value);
12450 else // generic parameter of type integer or boolean
12452 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12453 type == TYPE_INTEGER ? get_integer_from_string(value) :
12454 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12455 ARG_UNDEFINED_VALUE);
12463 static int get_token_parameter_value(char *token, char *value_raw)
12467 if (token == NULL || value_raw == NULL)
12468 return ARG_UNDEFINED_VALUE;
12470 suffix = strrchr(token, '.');
12471 if (suffix == NULL)
12474 if (strEqual(suffix, ".element"))
12475 return getElementFromToken(value_raw);
12477 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12478 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12481 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12482 boolean ignore_defaults)
12486 for (i = 0; image_config_vars[i].token != NULL; i++)
12488 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12490 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12491 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12495 *image_config_vars[i].value =
12496 get_token_parameter_value(image_config_vars[i].token, value);
12500 void InitMenuDesignSettings_Static(void)
12502 // always start with reliable default values from static default config
12503 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12506 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12510 // the following initializes hierarchical values from static configuration
12512 // special case: initialize "ARG_DEFAULT" values in static default config
12513 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12514 titlescreen_initial_first_default.fade_mode =
12515 title_initial_first_default.fade_mode;
12516 titlescreen_initial_first_default.fade_delay =
12517 title_initial_first_default.fade_delay;
12518 titlescreen_initial_first_default.post_delay =
12519 title_initial_first_default.post_delay;
12520 titlescreen_initial_first_default.auto_delay =
12521 title_initial_first_default.auto_delay;
12522 titlescreen_initial_first_default.auto_delay_unit =
12523 title_initial_first_default.auto_delay_unit;
12524 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12525 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12526 titlescreen_first_default.post_delay = title_first_default.post_delay;
12527 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12528 titlescreen_first_default.auto_delay_unit =
12529 title_first_default.auto_delay_unit;
12530 titlemessage_initial_first_default.fade_mode =
12531 title_initial_first_default.fade_mode;
12532 titlemessage_initial_first_default.fade_delay =
12533 title_initial_first_default.fade_delay;
12534 titlemessage_initial_first_default.post_delay =
12535 title_initial_first_default.post_delay;
12536 titlemessage_initial_first_default.auto_delay =
12537 title_initial_first_default.auto_delay;
12538 titlemessage_initial_first_default.auto_delay_unit =
12539 title_initial_first_default.auto_delay_unit;
12540 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12541 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12542 titlemessage_first_default.post_delay = title_first_default.post_delay;
12543 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12544 titlemessage_first_default.auto_delay_unit =
12545 title_first_default.auto_delay_unit;
12547 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12548 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12549 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12550 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12551 titlescreen_initial_default.auto_delay_unit =
12552 title_initial_default.auto_delay_unit;
12553 titlescreen_default.fade_mode = title_default.fade_mode;
12554 titlescreen_default.fade_delay = title_default.fade_delay;
12555 titlescreen_default.post_delay = title_default.post_delay;
12556 titlescreen_default.auto_delay = title_default.auto_delay;
12557 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12558 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12559 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12560 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12561 titlemessage_initial_default.auto_delay_unit =
12562 title_initial_default.auto_delay_unit;
12563 titlemessage_default.fade_mode = title_default.fade_mode;
12564 titlemessage_default.fade_delay = title_default.fade_delay;
12565 titlemessage_default.post_delay = title_default.post_delay;
12566 titlemessage_default.auto_delay = title_default.auto_delay;
12567 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12569 // special case: initialize "ARG_DEFAULT" values in static default config
12570 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12571 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12573 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12574 titlescreen_first[i] = titlescreen_first_default;
12575 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12576 titlemessage_first[i] = titlemessage_first_default;
12578 titlescreen_initial[i] = titlescreen_initial_default;
12579 titlescreen[i] = titlescreen_default;
12580 titlemessage_initial[i] = titlemessage_initial_default;
12581 titlemessage[i] = titlemessage_default;
12584 // special case: initialize "ARG_DEFAULT" values in static default config
12585 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12586 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12588 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12591 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12592 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12593 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12596 // special case: initialize "ARG_DEFAULT" values in static default config
12597 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12598 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12600 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12601 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12602 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12604 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12607 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12611 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12615 struct XY *dst, *src;
12617 game_buttons_xy[] =
12619 { &game.button.save, &game.button.stop },
12620 { &game.button.pause2, &game.button.pause },
12621 { &game.button.load, &game.button.play },
12622 { &game.button.undo, &game.button.stop },
12623 { &game.button.redo, &game.button.play },
12629 // special case: initialize later added SETUP list size from LEVELS value
12630 if (menu.list_size[GAME_MODE_SETUP] == -1)
12631 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12633 // set default position for snapshot buttons to stop/pause/play buttons
12634 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12635 if ((*game_buttons_xy[i].dst).x == -1 &&
12636 (*game_buttons_xy[i].dst).y == -1)
12637 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12639 // --------------------------------------------------------------------------
12640 // dynamic viewports (including playfield margins, borders and alignments)
12641 // --------------------------------------------------------------------------
12643 // dynamic viewports currently only supported for landscape mode
12644 int display_width = MAX(video.display_width, video.display_height);
12645 int display_height = MIN(video.display_width, video.display_height);
12647 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12649 struct RectWithBorder *vp_window = &viewport.window[i];
12650 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12651 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12652 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12653 boolean dynamic_window_width = (vp_window->min_width != -1);
12654 boolean dynamic_window_height = (vp_window->min_height != -1);
12655 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12656 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12658 // adjust window size if min/max width/height is specified
12660 if (vp_window->min_width != -1)
12662 int window_width = display_width;
12664 // when using static window height, use aspect ratio of display
12665 if (vp_window->min_height == -1)
12666 window_width = vp_window->height * display_width / display_height;
12668 vp_window->width = MAX(vp_window->min_width, window_width);
12671 if (vp_window->min_height != -1)
12673 int window_height = display_height;
12675 // when using static window width, use aspect ratio of display
12676 if (vp_window->min_width == -1)
12677 window_height = vp_window->width * display_height / display_width;
12679 vp_window->height = MAX(vp_window->min_height, window_height);
12682 if (vp_window->max_width != -1)
12683 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12685 if (vp_window->max_height != -1)
12686 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12688 int playfield_width = vp_window->width;
12689 int playfield_height = vp_window->height;
12691 // adjust playfield size and position according to specified margins
12693 playfield_width -= vp_playfield->margin_left;
12694 playfield_width -= vp_playfield->margin_right;
12696 playfield_height -= vp_playfield->margin_top;
12697 playfield_height -= vp_playfield->margin_bottom;
12699 // adjust playfield size if min/max width/height is specified
12701 if (vp_playfield->min_width != -1)
12702 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12704 if (vp_playfield->min_height != -1)
12705 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12707 if (vp_playfield->max_width != -1)
12708 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12710 if (vp_playfield->max_height != -1)
12711 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12713 // adjust playfield position according to specified alignment
12715 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12716 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12717 else if (vp_playfield->align == ALIGN_CENTER)
12718 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12719 else if (vp_playfield->align == ALIGN_RIGHT)
12720 vp_playfield->x += playfield_width - vp_playfield->width;
12722 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12723 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12724 else if (vp_playfield->valign == VALIGN_MIDDLE)
12725 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12726 else if (vp_playfield->valign == VALIGN_BOTTOM)
12727 vp_playfield->y += playfield_height - vp_playfield->height;
12729 vp_playfield->x += vp_playfield->margin_left;
12730 vp_playfield->y += vp_playfield->margin_top;
12732 // adjust individual playfield borders if only default border is specified
12734 if (vp_playfield->border_left == -1)
12735 vp_playfield->border_left = vp_playfield->border_size;
12736 if (vp_playfield->border_right == -1)
12737 vp_playfield->border_right = vp_playfield->border_size;
12738 if (vp_playfield->border_top == -1)
12739 vp_playfield->border_top = vp_playfield->border_size;
12740 if (vp_playfield->border_bottom == -1)
12741 vp_playfield->border_bottom = vp_playfield->border_size;
12743 // set dynamic playfield borders if borders are specified as undefined
12744 // (but only if window size was dynamic and playfield size was static)
12746 if (dynamic_window_width && !dynamic_playfield_width)
12748 if (vp_playfield->border_left == -1)
12750 vp_playfield->border_left = (vp_playfield->x -
12751 vp_playfield->margin_left);
12752 vp_playfield->x -= vp_playfield->border_left;
12753 vp_playfield->width += vp_playfield->border_left;
12756 if (vp_playfield->border_right == -1)
12758 vp_playfield->border_right = (vp_window->width -
12760 vp_playfield->width -
12761 vp_playfield->margin_right);
12762 vp_playfield->width += vp_playfield->border_right;
12766 if (dynamic_window_height && !dynamic_playfield_height)
12768 if (vp_playfield->border_top == -1)
12770 vp_playfield->border_top = (vp_playfield->y -
12771 vp_playfield->margin_top);
12772 vp_playfield->y -= vp_playfield->border_top;
12773 vp_playfield->height += vp_playfield->border_top;
12776 if (vp_playfield->border_bottom == -1)
12778 vp_playfield->border_bottom = (vp_window->height -
12780 vp_playfield->height -
12781 vp_playfield->margin_bottom);
12782 vp_playfield->height += vp_playfield->border_bottom;
12786 // adjust playfield size to be a multiple of a defined alignment tile size
12788 int align_size = vp_playfield->align_size;
12789 int playfield_xtiles = vp_playfield->width / align_size;
12790 int playfield_ytiles = vp_playfield->height / align_size;
12791 int playfield_width_corrected = playfield_xtiles * align_size;
12792 int playfield_height_corrected = playfield_ytiles * align_size;
12793 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12794 i == GFX_SPECIAL_ARG_EDITOR);
12796 if (is_playfield_mode &&
12797 dynamic_playfield_width &&
12798 vp_playfield->width != playfield_width_corrected)
12800 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12802 vp_playfield->width = playfield_width_corrected;
12804 if (vp_playfield->align == ALIGN_LEFT)
12806 vp_playfield->border_left += playfield_xdiff;
12808 else if (vp_playfield->align == ALIGN_RIGHT)
12810 vp_playfield->border_right += playfield_xdiff;
12812 else if (vp_playfield->align == ALIGN_CENTER)
12814 int border_left_diff = playfield_xdiff / 2;
12815 int border_right_diff = playfield_xdiff - border_left_diff;
12817 vp_playfield->border_left += border_left_diff;
12818 vp_playfield->border_right += border_right_diff;
12822 if (is_playfield_mode &&
12823 dynamic_playfield_height &&
12824 vp_playfield->height != playfield_height_corrected)
12826 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12828 vp_playfield->height = playfield_height_corrected;
12830 if (vp_playfield->valign == VALIGN_TOP)
12832 vp_playfield->border_top += playfield_ydiff;
12834 else if (vp_playfield->align == VALIGN_BOTTOM)
12836 vp_playfield->border_right += playfield_ydiff;
12838 else if (vp_playfield->align == VALIGN_MIDDLE)
12840 int border_top_diff = playfield_ydiff / 2;
12841 int border_bottom_diff = playfield_ydiff - border_top_diff;
12843 vp_playfield->border_top += border_top_diff;
12844 vp_playfield->border_bottom += border_bottom_diff;
12848 // adjust door positions according to specified alignment
12850 for (j = 0; j < 2; j++)
12852 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12854 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12855 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12856 else if (vp_door->align == ALIGN_CENTER)
12857 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12858 else if (vp_door->align == ALIGN_RIGHT)
12859 vp_door->x += vp_window->width - vp_door->width;
12861 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12862 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12863 else if (vp_door->valign == VALIGN_MIDDLE)
12864 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12865 else if (vp_door->valign == VALIGN_BOTTOM)
12866 vp_door->y += vp_window->height - vp_door->height;
12871 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12875 struct XYTileSize *dst, *src;
12878 editor_buttons_xy[] =
12881 &editor.button.element_left, &editor.palette.element_left,
12882 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12885 &editor.button.element_middle, &editor.palette.element_middle,
12886 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12889 &editor.button.element_right, &editor.palette.element_right,
12890 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12897 // set default position for element buttons to element graphics
12898 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12900 if ((*editor_buttons_xy[i].dst).x == -1 &&
12901 (*editor_buttons_xy[i].dst).y == -1)
12903 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12905 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12907 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12911 // adjust editor palette rows and columns if specified to be dynamic
12913 if (editor.palette.cols == -1)
12915 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12916 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12917 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12919 editor.palette.cols = (vp_width - sc_width) / bt_width;
12921 if (editor.palette.x == -1)
12923 int palette_width = editor.palette.cols * bt_width + sc_width;
12925 editor.palette.x = (vp_width - palette_width) / 2;
12929 if (editor.palette.rows == -1)
12931 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12932 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12933 int tx_height = getFontHeight(FONT_TEXT_2);
12935 editor.palette.rows = (vp_height - tx_height) / bt_height;
12937 if (editor.palette.y == -1)
12939 int palette_height = editor.palette.rows * bt_height + tx_height;
12941 editor.palette.y = (vp_height - palette_height) / 2;
12946 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12947 boolean initialize)
12949 // special case: check if network and preview player positions are redefined,
12950 // to compare this later against the main menu level preview being redefined
12951 struct TokenIntPtrInfo menu_config_players[] =
12953 { "main.network_players.x", &menu.main.network_players.redefined },
12954 { "main.network_players.y", &menu.main.network_players.redefined },
12955 { "main.preview_players.x", &menu.main.preview_players.redefined },
12956 { "main.preview_players.y", &menu.main.preview_players.redefined },
12957 { "preview.x", &preview.redefined },
12958 { "preview.y", &preview.redefined }
12964 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12965 *menu_config_players[i].value = FALSE;
12969 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12970 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12971 *menu_config_players[i].value = TRUE;
12975 static void InitMenuDesignSettings_PreviewPlayers(void)
12977 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12980 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12982 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12985 static void LoadMenuDesignSettingsFromFilename(char *filename)
12987 static struct TitleFadingInfo tfi;
12988 static struct TitleMessageInfo tmi;
12989 static struct TokenInfo title_tokens[] =
12991 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12992 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12993 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12994 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12995 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12999 static struct TokenInfo titlemessage_tokens[] =
13001 { TYPE_INTEGER, &tmi.x, ".x" },
13002 { TYPE_INTEGER, &tmi.y, ".y" },
13003 { TYPE_INTEGER, &tmi.width, ".width" },
13004 { TYPE_INTEGER, &tmi.height, ".height" },
13005 { TYPE_INTEGER, &tmi.chars, ".chars" },
13006 { TYPE_INTEGER, &tmi.lines, ".lines" },
13007 { TYPE_INTEGER, &tmi.align, ".align" },
13008 { TYPE_INTEGER, &tmi.valign, ".valign" },
13009 { TYPE_INTEGER, &tmi.font, ".font" },
13010 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13011 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13012 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13013 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13014 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13015 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13016 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13017 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13018 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13024 struct TitleFadingInfo *info;
13029 // initialize first titles from "enter screen" definitions, if defined
13030 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13031 { &title_first_default, "menu.enter_screen.TITLE" },
13033 // initialize title screens from "next screen" definitions, if defined
13034 { &title_initial_default, "menu.next_screen.TITLE" },
13035 { &title_default, "menu.next_screen.TITLE" },
13041 struct TitleMessageInfo *array;
13044 titlemessage_arrays[] =
13046 // initialize first titles from "enter screen" definitions, if defined
13047 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13048 { titlescreen_first, "menu.enter_screen.TITLE" },
13049 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13050 { titlemessage_first, "menu.enter_screen.TITLE" },
13052 // initialize titles from "next screen" definitions, if defined
13053 { titlescreen_initial, "menu.next_screen.TITLE" },
13054 { titlescreen, "menu.next_screen.TITLE" },
13055 { titlemessage_initial, "menu.next_screen.TITLE" },
13056 { titlemessage, "menu.next_screen.TITLE" },
13058 // overwrite titles with title definitions, if defined
13059 { titlescreen_initial_first, "[title_initial]" },
13060 { titlescreen_first, "[title]" },
13061 { titlemessage_initial_first, "[title_initial]" },
13062 { titlemessage_first, "[title]" },
13064 { titlescreen_initial, "[title_initial]" },
13065 { titlescreen, "[title]" },
13066 { titlemessage_initial, "[title_initial]" },
13067 { titlemessage, "[title]" },
13069 // overwrite titles with title screen/message definitions, if defined
13070 { titlescreen_initial_first, "[titlescreen_initial]" },
13071 { titlescreen_first, "[titlescreen]" },
13072 { titlemessage_initial_first, "[titlemessage_initial]" },
13073 { titlemessage_first, "[titlemessage]" },
13075 { titlescreen_initial, "[titlescreen_initial]" },
13076 { titlescreen, "[titlescreen]" },
13077 { titlemessage_initial, "[titlemessage_initial]" },
13078 { titlemessage, "[titlemessage]" },
13082 SetupFileHash *setup_file_hash;
13085 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13088 // the following initializes hierarchical values from dynamic configuration
13090 // special case: initialize with default values that may be overwritten
13091 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13092 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13094 struct TokenIntPtrInfo menu_config[] =
13096 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13097 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13098 { "menu.list_size", &menu.list_size[i] }
13101 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13103 char *token = menu_config[j].token;
13104 char *value = getHashEntry(setup_file_hash, token);
13107 *menu_config[j].value = get_integer_from_string(value);
13111 // special case: initialize with default values that may be overwritten
13112 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13113 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13115 struct TokenIntPtrInfo menu_config[] =
13117 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13118 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13119 { "menu.list_size.INFO", &menu.list_size_info[i] },
13120 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13121 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13124 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13126 char *token = menu_config[j].token;
13127 char *value = getHashEntry(setup_file_hash, token);
13130 *menu_config[j].value = get_integer_from_string(value);
13134 // special case: initialize with default values that may be overwritten
13135 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13136 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13138 struct TokenIntPtrInfo menu_config[] =
13140 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13141 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13144 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13146 char *token = menu_config[j].token;
13147 char *value = getHashEntry(setup_file_hash, token);
13150 *menu_config[j].value = get_integer_from_string(value);
13154 // special case: initialize with default values that may be overwritten
13155 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13156 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13158 struct TokenIntPtrInfo menu_config[] =
13160 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13161 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13162 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13163 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13164 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13165 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13166 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13167 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13168 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13169 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13172 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13174 char *token = menu_config[j].token;
13175 char *value = getHashEntry(setup_file_hash, token);
13178 *menu_config[j].value = get_integer_from_string(value);
13182 // special case: initialize with default values that may be overwritten
13183 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13184 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13186 struct TokenIntPtrInfo menu_config[] =
13188 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13189 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13190 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13191 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13192 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13193 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13194 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13195 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13196 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13199 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13201 char *token = menu_config[j].token;
13202 char *value = getHashEntry(setup_file_hash, token);
13205 *menu_config[j].value = get_token_parameter_value(token, value);
13209 // special case: initialize with default values that may be overwritten
13210 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13211 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13215 char *token_prefix;
13216 struct RectWithBorder *struct_ptr;
13220 { "viewport.window", &viewport.window[i] },
13221 { "viewport.playfield", &viewport.playfield[i] },
13222 { "viewport.door_1", &viewport.door_1[i] },
13223 { "viewport.door_2", &viewport.door_2[i] }
13226 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13228 struct TokenIntPtrInfo vp_config[] =
13230 { ".x", &vp_struct[j].struct_ptr->x },
13231 { ".y", &vp_struct[j].struct_ptr->y },
13232 { ".width", &vp_struct[j].struct_ptr->width },
13233 { ".height", &vp_struct[j].struct_ptr->height },
13234 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13235 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13236 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13237 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13238 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13239 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13240 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13241 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13242 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13243 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13244 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13245 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13246 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13247 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13248 { ".align", &vp_struct[j].struct_ptr->align },
13249 { ".valign", &vp_struct[j].struct_ptr->valign }
13252 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13254 char *token = getStringCat2(vp_struct[j].token_prefix,
13255 vp_config[k].token);
13256 char *value = getHashEntry(setup_file_hash, token);
13259 *vp_config[k].value = get_token_parameter_value(token, value);
13266 // special case: initialize with default values that may be overwritten
13267 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13268 for (i = 0; title_info[i].info != NULL; i++)
13270 struct TitleFadingInfo *info = title_info[i].info;
13271 char *base_token = title_info[i].text;
13273 for (j = 0; title_tokens[j].type != -1; j++)
13275 char *token = getStringCat2(base_token, title_tokens[j].text);
13276 char *value = getHashEntry(setup_file_hash, token);
13280 int parameter_value = get_token_parameter_value(token, value);
13284 *(int *)title_tokens[j].value = (int)parameter_value;
13293 // special case: initialize with default values that may be overwritten
13294 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13295 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13297 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13298 char *base_token = titlemessage_arrays[i].text;
13300 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13302 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13303 char *value = getHashEntry(setup_file_hash, token);
13307 int parameter_value = get_token_parameter_value(token, value);
13309 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13313 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13314 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13316 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13326 // read (and overwrite with) values that may be specified in config file
13327 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13329 // special case: check if network and preview player positions are redefined
13330 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13332 freeSetupFileHash(setup_file_hash);
13335 void LoadMenuDesignSettings(void)
13337 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13339 InitMenuDesignSettings_Static();
13340 InitMenuDesignSettings_SpecialPreProcessing();
13341 InitMenuDesignSettings_PreviewPlayers();
13343 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13345 // first look for special settings configured in level series config
13346 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13348 if (fileExists(filename_base))
13349 LoadMenuDesignSettingsFromFilename(filename_base);
13352 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13354 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13355 LoadMenuDesignSettingsFromFilename(filename_local);
13357 InitMenuDesignSettings_SpecialPostProcessing();
13360 void LoadMenuDesignSettings_AfterGraphics(void)
13362 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13365 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13366 boolean ignore_defaults)
13370 for (i = 0; sound_config_vars[i].token != NULL; i++)
13372 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13374 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13375 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13379 *sound_config_vars[i].value =
13380 get_token_parameter_value(sound_config_vars[i].token, value);
13384 void InitSoundSettings_Static(void)
13386 // always start with reliable default values from static default config
13387 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13390 static void LoadSoundSettingsFromFilename(char *filename)
13392 SetupFileHash *setup_file_hash;
13394 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13397 // read (and overwrite with) values that may be specified in config file
13398 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13400 freeSetupFileHash(setup_file_hash);
13403 void LoadSoundSettings(void)
13405 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13407 InitSoundSettings_Static();
13409 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13411 // first look for special settings configured in level series config
13412 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13414 if (fileExists(filename_base))
13415 LoadSoundSettingsFromFilename(filename_base);
13418 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13420 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13421 LoadSoundSettingsFromFilename(filename_local);
13424 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13426 char *filename = getEditorSetupFilename();
13427 SetupFileList *setup_file_list, *list;
13428 SetupFileHash *element_hash;
13429 int num_unknown_tokens = 0;
13432 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13435 element_hash = newSetupFileHash();
13437 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13438 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13440 // determined size may be larger than needed (due to unknown elements)
13442 for (list = setup_file_list; list != NULL; list = list->next)
13445 // add space for up to 3 more elements for padding that may be needed
13446 *num_elements += 3;
13448 // free memory for old list of elements, if needed
13449 checked_free(*elements);
13451 // allocate memory for new list of elements
13452 *elements = checked_malloc(*num_elements * sizeof(int));
13455 for (list = setup_file_list; list != NULL; list = list->next)
13457 char *value = getHashEntry(element_hash, list->token);
13459 if (value == NULL) // try to find obsolete token mapping
13461 char *mapped_token = get_mapped_token(list->token);
13463 if (mapped_token != NULL)
13465 value = getHashEntry(element_hash, mapped_token);
13467 free(mapped_token);
13473 (*elements)[(*num_elements)++] = atoi(value);
13477 if (num_unknown_tokens == 0)
13480 Warn("unknown token(s) found in config file:");
13481 Warn("- config file: '%s'", filename);
13483 num_unknown_tokens++;
13486 Warn("- token: '%s'", list->token);
13490 if (num_unknown_tokens > 0)
13493 while (*num_elements % 4) // pad with empty elements, if needed
13494 (*elements)[(*num_elements)++] = EL_EMPTY;
13496 freeSetupFileList(setup_file_list);
13497 freeSetupFileHash(element_hash);
13500 for (i = 0; i < *num_elements; i++)
13501 Debug("editor", "element '%s' [%d]\n",
13502 element_info[(*elements)[i]].token_name, (*elements)[i]);
13506 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13509 SetupFileHash *setup_file_hash = NULL;
13510 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13511 char *filename_music, *filename_prefix, *filename_info;
13517 token_to_value_ptr[] =
13519 { "title_header", &tmp_music_file_info.title_header },
13520 { "artist_header", &tmp_music_file_info.artist_header },
13521 { "album_header", &tmp_music_file_info.album_header },
13522 { "year_header", &tmp_music_file_info.year_header },
13523 { "played_header", &tmp_music_file_info.played_header },
13525 { "title", &tmp_music_file_info.title },
13526 { "artist", &tmp_music_file_info.artist },
13527 { "album", &tmp_music_file_info.album },
13528 { "year", &tmp_music_file_info.year },
13529 { "played", &tmp_music_file_info.played },
13535 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13536 getCustomMusicFilename(basename));
13538 if (filename_music == NULL)
13541 // ---------- try to replace file extension ----------
13543 filename_prefix = getStringCopy(filename_music);
13544 if (strrchr(filename_prefix, '.') != NULL)
13545 *strrchr(filename_prefix, '.') = '\0';
13546 filename_info = getStringCat2(filename_prefix, ".txt");
13548 if (fileExists(filename_info))
13549 setup_file_hash = loadSetupFileHash(filename_info);
13551 free(filename_prefix);
13552 free(filename_info);
13554 if (setup_file_hash == NULL)
13556 // ---------- try to add file extension ----------
13558 filename_prefix = getStringCopy(filename_music);
13559 filename_info = getStringCat2(filename_prefix, ".txt");
13561 if (fileExists(filename_info))
13562 setup_file_hash = loadSetupFileHash(filename_info);
13564 free(filename_prefix);
13565 free(filename_info);
13568 if (setup_file_hash == NULL)
13571 // ---------- music file info found ----------
13573 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13575 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13577 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13579 *token_to_value_ptr[i].value_ptr =
13580 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13583 tmp_music_file_info.basename = getStringCopy(basename);
13584 tmp_music_file_info.music = music;
13585 tmp_music_file_info.is_sound = is_sound;
13587 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13588 *new_music_file_info = tmp_music_file_info;
13590 return new_music_file_info;
13593 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13595 return get_music_file_info_ext(basename, music, FALSE);
13598 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13600 return get_music_file_info_ext(basename, sound, TRUE);
13603 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13604 char *basename, boolean is_sound)
13606 for (; list != NULL; list = list->next)
13607 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13613 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13615 return music_info_listed_ext(list, basename, FALSE);
13618 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13620 return music_info_listed_ext(list, basename, TRUE);
13623 void LoadMusicInfo(void)
13625 int num_music_noconf = getMusicListSize_NoConf();
13626 int num_music = getMusicListSize();
13627 int num_sounds = getSoundListSize();
13628 struct FileInfo *music, *sound;
13629 struct MusicFileInfo *next, **new;
13633 while (music_file_info != NULL)
13635 next = music_file_info->next;
13637 checked_free(music_file_info->basename);
13639 checked_free(music_file_info->title_header);
13640 checked_free(music_file_info->artist_header);
13641 checked_free(music_file_info->album_header);
13642 checked_free(music_file_info->year_header);
13643 checked_free(music_file_info->played_header);
13645 checked_free(music_file_info->title);
13646 checked_free(music_file_info->artist);
13647 checked_free(music_file_info->album);
13648 checked_free(music_file_info->year);
13649 checked_free(music_file_info->played);
13651 free(music_file_info);
13653 music_file_info = next;
13656 new = &music_file_info;
13658 // get (configured or unconfigured) music file info for all levels
13659 for (i = leveldir_current->first_level;
13660 i <= leveldir_current->last_level; i++)
13664 if (levelset.music[i] != MUS_UNDEFINED)
13666 // get music file info for configured level music
13667 music_nr = levelset.music[i];
13669 else if (num_music_noconf > 0)
13671 // get music file info for unconfigured level music
13672 int level_pos = i - leveldir_current->first_level;
13674 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13681 char *basename = getMusicInfoEntryFilename(music_nr);
13683 if (basename == NULL)
13686 if (!music_info_listed(music_file_info, basename))
13688 *new = get_music_file_info(basename, music_nr);
13691 new = &(*new)->next;
13695 // get music file info for all remaining configured music files
13696 for (i = 0; i < num_music; i++)
13698 music = getMusicListEntry(i);
13700 if (music->filename == NULL)
13703 if (strEqual(music->filename, UNDEFINED_FILENAME))
13706 // a configured file may be not recognized as music
13707 if (!FileIsMusic(music->filename))
13710 if (!music_info_listed(music_file_info, music->filename))
13712 *new = get_music_file_info(music->filename, i);
13715 new = &(*new)->next;
13719 // get sound file info for all configured sound files
13720 for (i = 0; i < num_sounds; i++)
13722 sound = getSoundListEntry(i);
13724 if (sound->filename == NULL)
13727 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13730 // a configured file may be not recognized as sound
13731 if (!FileIsSound(sound->filename))
13734 if (!sound_info_listed(music_file_info, sound->filename))
13736 *new = get_sound_file_info(sound->filename, i);
13738 new = &(*new)->next;
13742 // add pointers to previous list nodes
13744 struct MusicFileInfo *node = music_file_info;
13746 while (node != NULL)
13749 node->next->prev = node;
13755 static void add_helpanim_entry(int element, int action, int direction,
13756 int delay, int *num_list_entries)
13758 struct HelpAnimInfo *new_list_entry;
13759 (*num_list_entries)++;
13762 checked_realloc(helpanim_info,
13763 *num_list_entries * sizeof(struct HelpAnimInfo));
13764 new_list_entry = &helpanim_info[*num_list_entries - 1];
13766 new_list_entry->element = element;
13767 new_list_entry->action = action;
13768 new_list_entry->direction = direction;
13769 new_list_entry->delay = delay;
13772 static void print_unknown_token(char *filename, char *token, int token_nr)
13777 Warn("unknown token(s) found in config file:");
13778 Warn("- config file: '%s'", filename);
13781 Warn("- token: '%s'", token);
13784 static void print_unknown_token_end(int token_nr)
13790 void LoadHelpAnimInfo(void)
13792 char *filename = getHelpAnimFilename();
13793 SetupFileList *setup_file_list = NULL, *list;
13794 SetupFileHash *element_hash, *action_hash, *direction_hash;
13795 int num_list_entries = 0;
13796 int num_unknown_tokens = 0;
13799 if (fileExists(filename))
13800 setup_file_list = loadSetupFileList(filename);
13802 if (setup_file_list == NULL)
13804 // use reliable default values from static configuration
13805 SetupFileList *insert_ptr;
13807 insert_ptr = setup_file_list =
13808 newSetupFileList(helpanim_config[0].token,
13809 helpanim_config[0].value);
13811 for (i = 1; helpanim_config[i].token; i++)
13812 insert_ptr = addListEntry(insert_ptr,
13813 helpanim_config[i].token,
13814 helpanim_config[i].value);
13817 element_hash = newSetupFileHash();
13818 action_hash = newSetupFileHash();
13819 direction_hash = newSetupFileHash();
13821 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13822 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13824 for (i = 0; i < NUM_ACTIONS; i++)
13825 setHashEntry(action_hash, element_action_info[i].suffix,
13826 i_to_a(element_action_info[i].value));
13828 // do not store direction index (bit) here, but direction value!
13829 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13830 setHashEntry(direction_hash, element_direction_info[i].suffix,
13831 i_to_a(1 << element_direction_info[i].value));
13833 for (list = setup_file_list; list != NULL; list = list->next)
13835 char *element_token, *action_token, *direction_token;
13836 char *element_value, *action_value, *direction_value;
13837 int delay = atoi(list->value);
13839 if (strEqual(list->token, "end"))
13841 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13846 /* first try to break element into element/action/direction parts;
13847 if this does not work, also accept combined "element[.act][.dir]"
13848 elements (like "dynamite.active"), which are unique elements */
13850 if (strchr(list->token, '.') == NULL) // token contains no '.'
13852 element_value = getHashEntry(element_hash, list->token);
13853 if (element_value != NULL) // element found
13854 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13855 &num_list_entries);
13858 // no further suffixes found -- this is not an element
13859 print_unknown_token(filename, list->token, num_unknown_tokens++);
13865 // token has format "<prefix>.<something>"
13867 action_token = strchr(list->token, '.'); // suffix may be action ...
13868 direction_token = action_token; // ... or direction
13870 element_token = getStringCopy(list->token);
13871 *strchr(element_token, '.') = '\0';
13873 element_value = getHashEntry(element_hash, element_token);
13875 if (element_value == NULL) // this is no element
13877 element_value = getHashEntry(element_hash, list->token);
13878 if (element_value != NULL) // combined element found
13879 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13880 &num_list_entries);
13882 print_unknown_token(filename, list->token, num_unknown_tokens++);
13884 free(element_token);
13889 action_value = getHashEntry(action_hash, action_token);
13891 if (action_value != NULL) // action found
13893 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13894 &num_list_entries);
13896 free(element_token);
13901 direction_value = getHashEntry(direction_hash, direction_token);
13903 if (direction_value != NULL) // direction found
13905 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13906 &num_list_entries);
13908 free(element_token);
13913 if (strchr(action_token + 1, '.') == NULL)
13915 // no further suffixes found -- this is not an action nor direction
13917 element_value = getHashEntry(element_hash, list->token);
13918 if (element_value != NULL) // combined element found
13919 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13920 &num_list_entries);
13922 print_unknown_token(filename, list->token, num_unknown_tokens++);
13924 free(element_token);
13929 // token has format "<prefix>.<suffix>.<something>"
13931 direction_token = strchr(action_token + 1, '.');
13933 action_token = getStringCopy(action_token);
13934 *strchr(action_token + 1, '.') = '\0';
13936 action_value = getHashEntry(action_hash, action_token);
13938 if (action_value == NULL) // this is no action
13940 element_value = getHashEntry(element_hash, list->token);
13941 if (element_value != NULL) // combined element found
13942 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13943 &num_list_entries);
13945 print_unknown_token(filename, list->token, num_unknown_tokens++);
13947 free(element_token);
13948 free(action_token);
13953 direction_value = getHashEntry(direction_hash, direction_token);
13955 if (direction_value != NULL) // direction found
13957 add_helpanim_entry(atoi(element_value), atoi(action_value),
13958 atoi(direction_value), delay, &num_list_entries);
13960 free(element_token);
13961 free(action_token);
13966 // this is no direction
13968 element_value = getHashEntry(element_hash, list->token);
13969 if (element_value != NULL) // combined element found
13970 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13971 &num_list_entries);
13973 print_unknown_token(filename, list->token, num_unknown_tokens++);
13975 free(element_token);
13976 free(action_token);
13979 print_unknown_token_end(num_unknown_tokens);
13981 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13982 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13984 freeSetupFileList(setup_file_list);
13985 freeSetupFileHash(element_hash);
13986 freeSetupFileHash(action_hash);
13987 freeSetupFileHash(direction_hash);
13990 for (i = 0; i < num_list_entries; i++)
13991 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13992 EL_NAME(helpanim_info[i].element),
13993 helpanim_info[i].element,
13994 helpanim_info[i].action,
13995 helpanim_info[i].direction,
13996 helpanim_info[i].delay);
14000 void LoadHelpTextInfo(void)
14002 char *filename = getHelpTextFilename();
14005 if (helptext_info != NULL)
14007 freeSetupFileHash(helptext_info);
14008 helptext_info = NULL;
14011 if (fileExists(filename))
14012 helptext_info = loadSetupFileHash(filename);
14014 if (helptext_info == NULL)
14016 // use reliable default values from static configuration
14017 helptext_info = newSetupFileHash();
14019 for (i = 0; helptext_config[i].token; i++)
14020 setHashEntry(helptext_info,
14021 helptext_config[i].token,
14022 helptext_config[i].value);
14026 BEGIN_HASH_ITERATION(helptext_info, itr)
14028 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14029 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14031 END_HASH_ITERATION(hash, itr)
14036 // ----------------------------------------------------------------------------
14038 // ----------------------------------------------------------------------------
14040 #define MAX_NUM_CONVERT_LEVELS 1000
14042 void ConvertLevels(void)
14044 static LevelDirTree *convert_leveldir = NULL;
14045 static int convert_level_nr = -1;
14046 static int num_levels_handled = 0;
14047 static int num_levels_converted = 0;
14048 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14051 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14052 global.convert_leveldir);
14054 if (convert_leveldir == NULL)
14055 Fail("no such level identifier: '%s'", global.convert_leveldir);
14057 leveldir_current = convert_leveldir;
14059 if (global.convert_level_nr != -1)
14061 convert_leveldir->first_level = global.convert_level_nr;
14062 convert_leveldir->last_level = global.convert_level_nr;
14065 convert_level_nr = convert_leveldir->first_level;
14067 PrintLine("=", 79);
14068 Print("Converting levels\n");
14069 PrintLine("-", 79);
14070 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14071 Print("Level series name: '%s'\n", convert_leveldir->name);
14072 Print("Level series author: '%s'\n", convert_leveldir->author);
14073 Print("Number of levels: %d\n", convert_leveldir->levels);
14074 PrintLine("=", 79);
14077 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14078 levels_failed[i] = FALSE;
14080 while (convert_level_nr <= convert_leveldir->last_level)
14082 char *level_filename;
14085 level_nr = convert_level_nr++;
14087 Print("Level %03d: ", level_nr);
14089 LoadLevel(level_nr);
14090 if (level.no_level_file || level.no_valid_file)
14092 Print("(no level)\n");
14096 Print("converting level ... ");
14099 // special case: conversion of some EMC levels as requested by ACME
14100 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14103 level_filename = getDefaultLevelFilename(level_nr);
14104 new_level = !fileExists(level_filename);
14108 SaveLevel(level_nr);
14110 num_levels_converted++;
14112 Print("converted.\n");
14116 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14117 levels_failed[level_nr] = TRUE;
14119 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14122 num_levels_handled++;
14126 PrintLine("=", 79);
14127 Print("Number of levels handled: %d\n", num_levels_handled);
14128 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14129 (num_levels_handled ?
14130 num_levels_converted * 100 / num_levels_handled : 0));
14131 PrintLine("-", 79);
14132 Print("Summary (for automatic parsing by scripts):\n");
14133 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14134 convert_leveldir->identifier, num_levels_converted,
14135 num_levels_handled,
14136 (num_levels_handled ?
14137 num_levels_converted * 100 / num_levels_handled : 0));
14139 if (num_levels_handled != num_levels_converted)
14141 Print(", FAILED:");
14142 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14143 if (levels_failed[i])
14148 PrintLine("=", 79);
14150 CloseAllAndExit(0);
14154 // ----------------------------------------------------------------------------
14155 // create and save images for use in level sketches (raw BMP format)
14156 // ----------------------------------------------------------------------------
14158 void CreateLevelSketchImages(void)
14164 InitElementPropertiesGfxElement();
14166 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14167 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14169 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14171 int element = getMappedElement(i);
14172 char basename1[16];
14173 char basename2[16];
14177 sprintf(basename1, "%04d.bmp", i);
14178 sprintf(basename2, "%04ds.bmp", i);
14180 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14181 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14183 DrawSizedElement(0, 0, element, TILESIZE);
14184 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14186 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14187 Fail("cannot save level sketch image file '%s'", filename1);
14189 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14190 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14192 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14193 Fail("cannot save level sketch image file '%s'", filename2);
14198 // create corresponding SQL statements (for normal and small images)
14201 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14202 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14205 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14206 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14208 // optional: create content for forum level sketch demonstration post
14210 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14213 FreeBitmap(bitmap1);
14214 FreeBitmap(bitmap2);
14217 fprintf(stderr, "\n");
14219 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14221 CloseAllAndExit(0);
14225 // ----------------------------------------------------------------------------
14226 // create and save images for element collecting animations (raw BMP format)
14227 // ----------------------------------------------------------------------------
14229 static boolean createCollectImage(int element)
14231 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14234 void CreateCollectElementImages(void)
14238 int anim_frames = num_steps - 1;
14239 int tile_size = TILESIZE;
14240 int anim_width = tile_size * anim_frames;
14241 int anim_height = tile_size;
14242 int num_collect_images = 0;
14243 int pos_collect_images = 0;
14245 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14246 if (createCollectImage(i))
14247 num_collect_images++;
14249 Info("Creating %d element collecting animation images ...",
14250 num_collect_images);
14252 int dst_width = anim_width * 2;
14253 int dst_height = anim_height * num_collect_images / 2;
14254 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14255 char *basename_bmp = "RocksCollect.bmp";
14256 char *basename_png = "RocksCollect.png";
14257 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14258 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14259 int len_filename_bmp = strlen(filename_bmp);
14260 int len_filename_png = strlen(filename_png);
14261 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14262 char cmd_convert[max_command_len];
14264 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14268 // force using RGBA surface for destination bitmap
14269 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14270 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14272 dst_bitmap->surface =
14273 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14275 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14277 if (!createCollectImage(i))
14280 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14281 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14282 int graphic = el2img(i);
14283 char *token_name = element_info[i].token_name;
14284 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14285 Bitmap *src_bitmap;
14288 Info("- creating collecting image for '%s' ...", token_name);
14290 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14292 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14293 tile_size, tile_size, 0, 0);
14295 // force using RGBA surface for temporary bitmap (using transparent black)
14296 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14297 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14299 tmp_bitmap->surface =
14300 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14302 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14304 for (j = 0; j < anim_frames; j++)
14306 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14307 int frame_size = frame_size_final * num_steps;
14308 int offset = (tile_size - frame_size_final) / 2;
14309 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14311 while (frame_size > frame_size_final)
14315 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14317 FreeBitmap(frame_bitmap);
14319 frame_bitmap = half_bitmap;
14322 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14323 frame_size_final, frame_size_final,
14324 dst_x + j * tile_size + offset, dst_y + offset);
14326 FreeBitmap(frame_bitmap);
14329 tmp_bitmap->surface_masked = NULL;
14331 FreeBitmap(tmp_bitmap);
14333 pos_collect_images++;
14336 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14337 Fail("cannot save element collecting image file '%s'", filename_bmp);
14339 FreeBitmap(dst_bitmap);
14341 Info("Converting image file from BMP to PNG ...");
14343 if (system(cmd_convert) != 0)
14344 Fail("converting image file failed");
14346 unlink(filename_bmp);
14350 CloseAllAndExit(0);
14354 // ----------------------------------------------------------------------------
14355 // create and save images for custom and group elements (raw BMP format)
14356 // ----------------------------------------------------------------------------
14358 void CreateCustomElementImages(char *directory)
14360 char *src_basename = "RocksCE-template.ilbm";
14361 char *dst_basename = "RocksCE.bmp";
14362 char *src_filename = getPath2(directory, src_basename);
14363 char *dst_filename = getPath2(directory, dst_basename);
14364 Bitmap *src_bitmap;
14366 int yoffset_ce = 0;
14367 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14370 InitVideoDefaults();
14372 ReCreateBitmap(&backbuffer, video.width, video.height);
14374 src_bitmap = LoadImage(src_filename);
14376 bitmap = CreateBitmap(TILEX * 16 * 2,
14377 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14380 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14387 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14388 TILEX * x, TILEY * y + yoffset_ce);
14390 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14392 TILEX * x + TILEX * 16,
14393 TILEY * y + yoffset_ce);
14395 for (j = 2; j >= 0; j--)
14399 BlitBitmap(src_bitmap, bitmap,
14400 TILEX + c * 7, 0, 6, 10,
14401 TILEX * x + 6 + j * 7,
14402 TILEY * y + 11 + yoffset_ce);
14404 BlitBitmap(src_bitmap, bitmap,
14405 TILEX + c * 8, TILEY, 6, 10,
14406 TILEX * 16 + TILEX * x + 6 + j * 8,
14407 TILEY * y + 10 + yoffset_ce);
14413 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14420 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14421 TILEX * x, TILEY * y + yoffset_ge);
14423 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14425 TILEX * x + TILEX * 16,
14426 TILEY * y + yoffset_ge);
14428 for (j = 1; j >= 0; j--)
14432 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14433 TILEX * x + 6 + j * 10,
14434 TILEY * y + 11 + yoffset_ge);
14436 BlitBitmap(src_bitmap, bitmap,
14437 TILEX + c * 8, TILEY + 12, 6, 10,
14438 TILEX * 16 + TILEX * x + 10 + j * 8,
14439 TILEY * y + 10 + yoffset_ge);
14445 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14446 Fail("cannot save CE graphics file '%s'", dst_filename);
14448 FreeBitmap(bitmap);
14450 CloseAllAndExit(0);