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
315 TYPE_INTEGER, CONF_VALUE_8_BIT(24),
316 &li.bd_cave_random_seed_c64, 0
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
328 // (these values are the same for each player)
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
332 &li.block_last_field, FALSE // default case for EM levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
337 &li.sp_block_last_field, TRUE // default case for SP levels
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
342 &li.instant_relocation, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
347 &li.can_pass_to_walkable, FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
352 &li.block_snap_field, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
357 &li.continuous_snapping, TRUE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
362 &li.shifted_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
367 &li.lazy_relocation, FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
372 &li.finish_dig_collect, TRUE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
377 &li.keep_walkable_ce, FALSE
380 // (these values are different for each player)
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[0], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[0], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[0], EL_PLAYER_1
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[0], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[0], EL_PLAYER_1
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[0], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[0], EL_PLAYER_1
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[0], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[0], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[1], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[1], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[1], EL_PLAYER_2
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[1], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[1], EL_PLAYER_2
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[1], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[1], EL_PLAYER_2
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[1], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[1], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[2], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[2], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[2], EL_PLAYER_3
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[2], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[2], EL_PLAYER_3
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[2], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[2], EL_PLAYER_3
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[2], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[2], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
555 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
560 &li.initial_player_gravity[3], FALSE
564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
565 &li.use_start_element[3], FALSE
569 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
570 &li.start_element[3], EL_PLAYER_4
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
575 &li.use_artwork_element[3], FALSE
579 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
580 &li.artwork_element[3], EL_PLAYER_4
584 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
585 &li.use_explosion_element[3], FALSE
589 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
590 &li.explosion_element[3], EL_PLAYER_4
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
595 &li.use_initial_inventory[3], FALSE
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
600 &li.initial_inventory_size[3], 1
604 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
605 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
609 // (these values are only valid for BD style levels)
610 // (some values for BD style amoeba following below)
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
614 &li.bd_diagonal_movements, FALSE
618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
619 &li.bd_topmost_player_active, TRUE
623 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
624 &li.bd_pushing_prob, 25
628 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
629 &li.bd_pushing_prob_with_sweet, 100
633 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
634 &li.bd_push_mega_rock_with_sweet, FALSE
638 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
639 &li.bd_snap_element, EL_EMPTY
644 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
645 &li.score[SC_DIAMOND_EXTRA], 20
649 EL_BD_MAGIC_WALL, -1,
650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
651 &li.bd_magic_wall_wait_hatching, FALSE
654 EL_BD_MAGIC_WALL, -1,
655 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
656 &li.bd_magic_wall_stops_amoeba, TRUE
661 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
662 &li.bd_clock_extra_time, 30
666 EL_BD_VOODOO_DOLL, -1,
667 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
668 &li.bd_voodoo_collects_diamonds, FALSE
671 EL_BD_VOODOO_DOLL, -1,
672 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
673 &li.bd_voodoo_hurt_kills_player, FALSE
676 EL_BD_VOODOO_DOLL, -1,
677 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
678 &li.bd_voodoo_dies_by_rock, FALSE
681 EL_BD_VOODOO_DOLL, -1,
682 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
683 &li.bd_voodoo_vanish_by_explosion, TRUE
686 EL_BD_VOODOO_DOLL, -1,
687 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
688 &li.bd_voodoo_penalty_time, 30
693 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
694 &li.bd_slime_is_predictable, TRUE
698 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
699 &li.bd_slime_permeability_rate, 100
703 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
704 &li.bd_slime_permeability_bits_c64, 0
708 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
709 &li.bd_slime_random_seed_c64, -1
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.bd_acid_eats_element, EL_BD_SAND
719 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
720 &li.bd_acid_spread_rate, 3
724 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
725 &li.bd_acid_turns_to_element, EL_EMPTY
730 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
731 &li.bd_biter_move_delay, 0
735 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
736 &li.bd_biter_eats_element, EL_BD_DIAMOND
741 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
742 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
746 EL_BD_EXPANDABLE_WALL_ANY, -1,
747 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
748 &li.bd_change_expanding_wall, FALSE
752 EL_BD_REPLICATOR, -1,
753 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
754 &li.bd_replicators_active, TRUE
757 EL_BD_REPLICATOR, -1,
758 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
759 &li.bd_replicator_create_delay, 4
762 // (the following values are related to various game elements)
766 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
767 &li.score[SC_EMERALD], 10
772 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
773 &li.score[SC_DIAMOND], 10
778 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
779 &li.score[SC_BUG], 10
784 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
785 &li.score[SC_SPACESHIP], 10
790 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
791 &li.score[SC_PACMAN], 10
796 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
797 &li.score[SC_NUT], 10
802 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
803 &li.score[SC_DYNAMITE], 10
808 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
809 &li.score[SC_KEY], 10
814 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
815 &li.score[SC_PEARL], 10
820 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
821 &li.score[SC_CRYSTAL], 10
824 // (amoeba values used by R'n'D game engine only)
827 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
828 &li.amoeba_content, EL_DIAMOND
832 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
837 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
838 &li.grow_into_diggable, TRUE
840 // (amoeba values used by BD game engine only)
843 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
844 &li.bd_amoeba_wait_for_hatching, FALSE
848 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
849 &li.bd_amoeba_start_immediately, TRUE
853 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
854 &li.bd_amoeba_2_explode_by_amoeba, TRUE
858 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
859 &li.bd_amoeba_threshold_too_big, 200
863 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
864 &li.bd_amoeba_slow_growth_time, 200
868 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
869 &li.bd_amoeba_slow_growth_rate, 3
873 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
874 &li.bd_amoeba_fast_growth_rate, 25
878 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
879 &li.bd_amoeba_content_too_big, EL_BD_ROCK
883 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
884 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
889 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
890 &li.bd_amoeba_2_threshold_too_big, 200
894 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
895 &li.bd_amoeba_2_slow_growth_time, 200
899 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
900 &li.bd_amoeba_2_slow_growth_rate, 3
904 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
905 &li.bd_amoeba_2_fast_growth_rate, 25
909 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
910 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
914 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
915 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
919 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
920 &li.bd_amoeba_2_content_exploding, EL_EMPTY
924 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
925 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
930 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
931 &li.yamyam_content, EL_ROCK, NULL,
932 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
936 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
937 &li.score[SC_YAMYAM], 10
942 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
943 &li.score[SC_ROBOT], 10
947 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
953 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
959 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
960 &li.time_magic_wall, 10
965 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
966 &li.game_of_life[0], 2
970 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
971 &li.game_of_life[1], 3
975 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
976 &li.game_of_life[2], 3
980 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
981 &li.game_of_life[3], 3
985 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
986 &li.use_life_bugs, FALSE
991 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
996 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1001 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1006 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1011 EL_TIMEGATE_SWITCH, -1,
1012 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1013 &li.time_timegate, 10
1017 EL_LIGHT_SWITCH_ACTIVE, -1,
1018 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1023 EL_SHIELD_NORMAL, -1,
1024 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1025 &li.shield_normal_time, 10
1028 EL_SHIELD_NORMAL, -1,
1029 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1030 &li.score[SC_SHIELD], 10
1034 EL_SHIELD_DEADLY, -1,
1035 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1036 &li.shield_deadly_time, 10
1039 EL_SHIELD_DEADLY, -1,
1040 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1041 &li.score[SC_SHIELD], 10
1046 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1051 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1052 &li.extra_time_score, 10
1056 EL_TIME_ORB_FULL, -1,
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1058 &li.time_orb_time, 10
1061 EL_TIME_ORB_FULL, -1,
1062 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1063 &li.use_time_orb_bug, FALSE
1068 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1069 &li.use_spring_bug, FALSE
1074 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1075 &li.android_move_time, 10
1079 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1080 &li.android_clone_time, 10
1083 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1084 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1085 &li.android_clone_element[0], EL_EMPTY, NULL,
1086 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1090 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1091 &li.android_clone_element[0], EL_EMPTY, NULL,
1092 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1097 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1098 &li.lenses_score, 10
1102 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1107 EL_EMC_MAGNIFIER, -1,
1108 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1109 &li.magnify_score, 10
1112 EL_EMC_MAGNIFIER, -1,
1113 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1114 &li.magnify_time, 10
1118 EL_EMC_MAGIC_BALL, -1,
1119 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1123 EL_EMC_MAGIC_BALL, -1,
1124 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1125 &li.ball_random, FALSE
1128 EL_EMC_MAGIC_BALL, -1,
1129 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1130 &li.ball_active_initial, FALSE
1133 EL_EMC_MAGIC_BALL, -1,
1134 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1135 &li.ball_content, EL_EMPTY, NULL,
1136 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1140 EL_SOKOBAN_FIELD_EMPTY, -1,
1141 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1142 &li.sb_fields_needed, TRUE
1146 EL_SOKOBAN_OBJECT, -1,
1147 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1148 &li.sb_objects_needed, TRUE
1153 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1154 &li.mm_laser_red, FALSE
1158 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1159 &li.mm_laser_green, FALSE
1163 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1164 &li.mm_laser_blue, TRUE
1169 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1170 &li.df_laser_red, TRUE
1174 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1175 &li.df_laser_green, TRUE
1179 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1180 &li.df_laser_blue, FALSE
1184 EL_MM_FUSE_ACTIVE, -1,
1185 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1186 &li.mm_time_fuse, 25
1190 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1191 &li.mm_time_bomb, 75
1195 EL_MM_GRAY_BALL, -1,
1196 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1197 &li.mm_time_ball, 75
1200 EL_MM_GRAY_BALL, -1,
1201 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1202 &li.mm_ball_choice_mode, ANIM_RANDOM
1205 EL_MM_GRAY_BALL, -1,
1206 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1207 &li.mm_ball_content, EL_EMPTY, NULL,
1208 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1211 EL_MM_GRAY_BALL, -1,
1212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1213 &li.rotate_mm_ball_content, TRUE
1216 EL_MM_GRAY_BALL, -1,
1217 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1218 &li.explode_mm_ball, FALSE
1222 EL_MM_STEEL_BLOCK, -1,
1223 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1224 &li.mm_time_block, 75
1227 EL_MM_LIGHTBALL, -1,
1228 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1229 &li.score[SC_ELEM_BONUS], 10
1239 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1243 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1244 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1248 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1249 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1254 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1255 &xx_envelope.autowrap, FALSE
1259 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1260 &xx_envelope.centered, FALSE
1265 TYPE_STRING, CONF_VALUE_BYTES(1),
1266 &xx_envelope.text, -1, NULL,
1267 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1268 &xx_default_string_empty[0]
1278 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1282 TYPE_STRING, CONF_VALUE_BYTES(1),
1283 &xx_ei.description[0], -1,
1284 &yy_ei.description[0],
1285 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1286 &xx_default_description[0]
1291 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1292 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1293 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1295 #if ENABLE_RESERVED_CODE
1296 // (reserved for later use)
1299 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1300 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1301 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1307 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1308 &xx_ei.use_gfx_element, FALSE,
1309 &yy_ei.use_gfx_element
1313 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1314 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1315 &yy_ei.gfx_element_initial
1320 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1321 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1322 &yy_ei.access_direction
1327 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1328 &xx_ei.collect_score_initial, 10,
1329 &yy_ei.collect_score_initial
1333 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1334 &xx_ei.collect_count_initial, 1,
1335 &yy_ei.collect_count_initial
1340 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1341 &xx_ei.ce_value_fixed_initial, 0,
1342 &yy_ei.ce_value_fixed_initial
1346 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1347 &xx_ei.ce_value_random_initial, 0,
1348 &yy_ei.ce_value_random_initial
1352 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1353 &xx_ei.use_last_ce_value, FALSE,
1354 &yy_ei.use_last_ce_value
1359 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1360 &xx_ei.push_delay_fixed, 8,
1361 &yy_ei.push_delay_fixed
1365 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1366 &xx_ei.push_delay_random, 8,
1367 &yy_ei.push_delay_random
1371 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1372 &xx_ei.drop_delay_fixed, 0,
1373 &yy_ei.drop_delay_fixed
1377 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1378 &xx_ei.drop_delay_random, 0,
1379 &yy_ei.drop_delay_random
1383 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1384 &xx_ei.move_delay_fixed, 0,
1385 &yy_ei.move_delay_fixed
1389 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1390 &xx_ei.move_delay_random, 0,
1391 &yy_ei.move_delay_random
1395 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1396 &xx_ei.step_delay_fixed, 0,
1397 &yy_ei.step_delay_fixed
1401 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1402 &xx_ei.step_delay_random, 0,
1403 &yy_ei.step_delay_random
1408 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1409 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1414 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1415 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1416 &yy_ei.move_direction_initial
1420 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1421 &xx_ei.move_stepsize, TILEX / 8,
1422 &yy_ei.move_stepsize
1427 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1428 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1429 &yy_ei.move_enter_element
1433 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1434 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1435 &yy_ei.move_leave_element
1439 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1440 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1441 &yy_ei.move_leave_type
1446 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1447 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1448 &yy_ei.slippery_type
1453 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1454 &xx_ei.explosion_type, EXPLODES_3X3,
1455 &yy_ei.explosion_type
1459 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1460 &xx_ei.explosion_delay, 16,
1461 &yy_ei.explosion_delay
1465 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1466 &xx_ei.ignition_delay, 8,
1467 &yy_ei.ignition_delay
1472 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1473 &xx_ei.content, EL_EMPTY_SPACE,
1475 &xx_num_contents, 1, 1
1478 // ---------- "num_change_pages" must be the last entry ---------------------
1481 -1, SAVE_CONF_ALWAYS,
1482 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1483 &xx_ei.num_change_pages, 1,
1484 &yy_ei.num_change_pages
1495 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1497 // ---------- "current_change_page" must be the first entry -----------------
1500 -1, SAVE_CONF_ALWAYS,
1501 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1502 &xx_current_change_page, -1
1505 // ---------- (the remaining entries can be in any order) -------------------
1509 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1510 &xx_change.can_change, FALSE
1515 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1516 &xx_event_bits[0], 0
1520 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1521 &xx_event_bits[1], 0
1526 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1527 &xx_change.trigger_player, CH_PLAYER_ANY
1531 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1532 &xx_change.trigger_side, CH_SIDE_ANY
1536 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1537 &xx_change.trigger_page, CH_PAGE_ANY
1542 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1543 &xx_change.target_element, EL_EMPTY_SPACE
1548 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1549 &xx_change.delay_fixed, 0
1553 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1554 &xx_change.delay_random, 0
1558 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1559 &xx_change.delay_frames, FRAMES_PER_SECOND
1564 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1565 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1570 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1571 &xx_change.explode, FALSE
1575 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1576 &xx_change.use_target_content, FALSE
1580 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1581 &xx_change.only_if_complete, FALSE
1585 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1586 &xx_change.use_random_replace, FALSE
1590 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1591 &xx_change.random_percentage, 100
1595 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1596 &xx_change.replace_when, CP_WHEN_EMPTY
1601 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1602 &xx_change.has_action, FALSE
1606 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1607 &xx_change.action_type, CA_NO_ACTION
1611 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1612 &xx_change.action_mode, CA_MODE_UNDEFINED
1616 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1617 &xx_change.action_arg, CA_ARG_UNDEFINED
1622 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1623 &xx_change.action_element, EL_EMPTY_SPACE
1628 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1629 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1630 &xx_num_contents, 1, 1
1640 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1644 TYPE_STRING, CONF_VALUE_BYTES(1),
1645 &xx_ei.description[0], -1, NULL,
1646 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1647 &xx_default_description[0]
1652 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1653 &xx_ei.use_gfx_element, FALSE
1657 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1658 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1663 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1664 &xx_group.choice_mode, ANIM_RANDOM
1669 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1670 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1671 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1681 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1685 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1686 &xx_ei.use_gfx_element, FALSE
1690 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1691 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1701 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1705 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1706 &li.block_snap_field, TRUE
1710 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1711 &li.continuous_snapping, TRUE
1715 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1716 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1720 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1721 &li.use_start_element[0], FALSE
1725 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1726 &li.start_element[0], EL_PLAYER_1
1730 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1731 &li.use_artwork_element[0], FALSE
1735 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1736 &li.artwork_element[0], EL_PLAYER_1
1740 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1741 &li.use_explosion_element[0], FALSE
1745 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1746 &li.explosion_element[0], EL_PLAYER_1
1761 filetype_id_list[] =
1763 { LEVEL_FILE_TYPE_RND, "RND" },
1764 { LEVEL_FILE_TYPE_BD, "BD" },
1765 { LEVEL_FILE_TYPE_EM, "EM" },
1766 { LEVEL_FILE_TYPE_SP, "SP" },
1767 { LEVEL_FILE_TYPE_DX, "DX" },
1768 { LEVEL_FILE_TYPE_SB, "SB" },
1769 { LEVEL_FILE_TYPE_DC, "DC" },
1770 { LEVEL_FILE_TYPE_MM, "MM" },
1771 { LEVEL_FILE_TYPE_MM, "DF" },
1776 // ============================================================================
1777 // level file functions
1778 // ============================================================================
1780 static boolean check_special_flags(char *flag)
1782 if (strEqual(options.special_flags, flag) ||
1783 strEqual(leveldir_current->special_flags, flag))
1789 static struct DateInfo getCurrentDate(void)
1791 time_t epoch_seconds = time(NULL);
1792 struct tm *now = localtime(&epoch_seconds);
1793 struct DateInfo date;
1795 date.year = now->tm_year + 1900;
1796 date.month = now->tm_mon + 1;
1797 date.day = now->tm_mday;
1799 date.src = DATE_SRC_CLOCK;
1804 static void resetEventFlags(struct ElementChangeInfo *change)
1808 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1809 change->has_event[i] = FALSE;
1812 static void resetEventBits(void)
1816 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1817 xx_event_bits[i] = 0;
1820 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1824 /* important: only change event flag if corresponding event bit is set
1825 (this is because all xx_event_bits[] values are loaded separately,
1826 and all xx_event_bits[] values are set back to zero before loading
1827 another value xx_event_bits[x] (each value representing 32 flags)) */
1829 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1830 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1831 change->has_event[i] = TRUE;
1834 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1838 /* in contrast to the above function setEventFlagsFromEventBits(), it
1839 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1840 depending on the corresponding change->has_event[i] values here, as
1841 all xx_event_bits[] values are reset in resetEventBits() before */
1843 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1844 if (change->has_event[i])
1845 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1848 static char *getDefaultElementDescription(struct ElementInfo *ei)
1850 static char description[MAX_ELEMENT_NAME_LEN + 1];
1851 char *default_description = (ei->custom_description != NULL ?
1852 ei->custom_description :
1853 ei->editor_description);
1856 // always start with reliable default values
1857 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1858 description[i] = '\0';
1860 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1861 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1863 return &description[0];
1866 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1868 char *default_description = getDefaultElementDescription(ei);
1871 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1872 ei->description[i] = default_description[i];
1875 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1879 for (i = 0; conf[i].data_type != -1; i++)
1881 int default_value = conf[i].default_value;
1882 int data_type = conf[i].data_type;
1883 int conf_type = conf[i].conf_type;
1884 int byte_mask = conf_type & CONF_MASK_BYTES;
1886 if (byte_mask == CONF_MASK_MULTI_BYTES)
1888 int default_num_entities = conf[i].default_num_entities;
1889 int max_num_entities = conf[i].max_num_entities;
1891 *(int *)(conf[i].num_entities) = default_num_entities;
1893 if (data_type == TYPE_STRING)
1895 char *default_string = conf[i].default_string;
1896 char *string = (char *)(conf[i].value);
1898 strncpy(string, default_string, max_num_entities);
1900 else if (data_type == TYPE_ELEMENT_LIST)
1902 int *element_array = (int *)(conf[i].value);
1905 for (j = 0; j < max_num_entities; j++)
1906 element_array[j] = default_value;
1908 else if (data_type == TYPE_CONTENT_LIST)
1910 struct Content *content = (struct Content *)(conf[i].value);
1913 for (c = 0; c < max_num_entities; c++)
1914 for (y = 0; y < 3; y++)
1915 for (x = 0; x < 3; x++)
1916 content[c].e[x][y] = default_value;
1919 else // constant size configuration data (1, 2 or 4 bytes)
1921 if (data_type == TYPE_BOOLEAN)
1922 *(boolean *)(conf[i].value) = default_value;
1924 *(int *) (conf[i].value) = default_value;
1929 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1933 for (i = 0; conf[i].data_type != -1; i++)
1935 int data_type = conf[i].data_type;
1936 int conf_type = conf[i].conf_type;
1937 int byte_mask = conf_type & CONF_MASK_BYTES;
1939 if (byte_mask == CONF_MASK_MULTI_BYTES)
1941 int max_num_entities = conf[i].max_num_entities;
1943 if (data_type == TYPE_STRING)
1945 char *string = (char *)(conf[i].value);
1946 char *string_copy = (char *)(conf[i].value_copy);
1948 strncpy(string_copy, string, max_num_entities);
1950 else if (data_type == TYPE_ELEMENT_LIST)
1952 int *element_array = (int *)(conf[i].value);
1953 int *element_array_copy = (int *)(conf[i].value_copy);
1956 for (j = 0; j < max_num_entities; j++)
1957 element_array_copy[j] = element_array[j];
1959 else if (data_type == TYPE_CONTENT_LIST)
1961 struct Content *content = (struct Content *)(conf[i].value);
1962 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1965 for (c = 0; c < max_num_entities; c++)
1966 for (y = 0; y < 3; y++)
1967 for (x = 0; x < 3; x++)
1968 content_copy[c].e[x][y] = content[c].e[x][y];
1971 else // constant size configuration data (1, 2 or 4 bytes)
1973 if (data_type == TYPE_BOOLEAN)
1974 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1976 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1981 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1985 xx_ei = *ei_from; // copy element data into temporary buffer
1986 yy_ei = *ei_to; // copy element data into temporary buffer
1988 copyConfigFromConfigList(chunk_config_CUSX_base);
1993 // ---------- reinitialize and copy change pages ----------
1995 ei_to->num_change_pages = ei_from->num_change_pages;
1996 ei_to->current_change_page = ei_from->current_change_page;
1998 setElementChangePages(ei_to, ei_to->num_change_pages);
2000 for (i = 0; i < ei_to->num_change_pages; i++)
2001 ei_to->change_page[i] = ei_from->change_page[i];
2003 // ---------- copy group element info ----------
2004 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2005 *ei_to->group = *ei_from->group;
2007 // mark this custom element as modified
2008 ei_to->modified_settings = TRUE;
2011 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2013 int change_page_size = sizeof(struct ElementChangeInfo);
2015 ei->num_change_pages = MAX(1, change_pages);
2018 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2020 if (ei->current_change_page >= ei->num_change_pages)
2021 ei->current_change_page = ei->num_change_pages - 1;
2023 ei->change = &ei->change_page[ei->current_change_page];
2026 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2028 xx_change = *change; // copy change data into temporary buffer
2030 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2032 *change = xx_change;
2034 resetEventFlags(change);
2036 change->direct_action = 0;
2037 change->other_action = 0;
2039 change->pre_change_function = NULL;
2040 change->change_function = NULL;
2041 change->post_change_function = NULL;
2044 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2048 li = *level; // copy level data into temporary buffer
2049 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2050 *level = li; // copy temporary buffer back to level data
2052 setLevelInfoToDefaults_BD();
2053 setLevelInfoToDefaults_EM();
2054 setLevelInfoToDefaults_SP();
2055 setLevelInfoToDefaults_MM();
2057 level->native_bd_level = &native_bd_level;
2058 level->native_em_level = &native_em_level;
2059 level->native_sp_level = &native_sp_level;
2060 level->native_mm_level = &native_mm_level;
2062 level->file_version = FILE_VERSION_ACTUAL;
2063 level->game_version = GAME_VERSION_ACTUAL;
2065 level->creation_date = getCurrentDate();
2067 level->encoding_16bit_field = TRUE;
2068 level->encoding_16bit_yamyam = TRUE;
2069 level->encoding_16bit_amoeba = TRUE;
2071 // clear level name and level author string buffers
2072 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2073 level->name[i] = '\0';
2074 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2075 level->author[i] = '\0';
2077 // set level name and level author to default values
2078 strcpy(level->name, NAMELESS_LEVEL_NAME);
2079 strcpy(level->author, ANONYMOUS_NAME);
2081 // set level playfield to playable default level with player and exit
2082 for (x = 0; x < MAX_LEV_FIELDX; x++)
2083 for (y = 0; y < MAX_LEV_FIELDY; y++)
2084 level->field[x][y] = EL_SAND;
2086 level->field[0][0] = EL_PLAYER_1;
2087 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2089 BorderElement = EL_STEELWALL;
2091 // detect custom elements when loading them
2092 level->file_has_custom_elements = FALSE;
2094 // set all bug compatibility flags to "false" => do not emulate this bug
2095 level->use_action_after_change_bug = FALSE;
2097 if (leveldir_current)
2099 // try to determine better author name than 'anonymous'
2100 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2102 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2103 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2107 switch (LEVELCLASS(leveldir_current))
2109 case LEVELCLASS_TUTORIAL:
2110 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2113 case LEVELCLASS_CONTRIB:
2114 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2115 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2118 case LEVELCLASS_PRIVATE:
2119 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2120 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2124 // keep default value
2131 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2133 static boolean clipboard_elements_initialized = FALSE;
2136 InitElementPropertiesStatic();
2138 li = *level; // copy level data into temporary buffer
2139 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2140 *level = li; // copy temporary buffer back to level data
2142 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2145 struct ElementInfo *ei = &element_info[element];
2147 if (element == EL_MM_GRAY_BALL)
2149 struct LevelInfo_MM *level_mm = level->native_mm_level;
2152 for (j = 0; j < level->num_mm_ball_contents; j++)
2153 level->mm_ball_content[j] =
2154 map_element_MM_to_RND(level_mm->ball_content[j]);
2157 // never initialize clipboard elements after the very first time
2158 // (to be able to use clipboard elements between several levels)
2159 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2162 if (IS_ENVELOPE(element))
2164 int envelope_nr = element - EL_ENVELOPE_1;
2166 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2168 level->envelope[envelope_nr] = xx_envelope;
2171 if (IS_CUSTOM_ELEMENT(element) ||
2172 IS_GROUP_ELEMENT(element) ||
2173 IS_INTERNAL_ELEMENT(element))
2175 xx_ei = *ei; // copy element data into temporary buffer
2177 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2182 setElementChangePages(ei, 1);
2183 setElementChangeInfoToDefaults(ei->change);
2185 if (IS_CUSTOM_ELEMENT(element) ||
2186 IS_GROUP_ELEMENT(element))
2188 setElementDescriptionToDefault(ei);
2190 ei->modified_settings = FALSE;
2193 if (IS_CUSTOM_ELEMENT(element) ||
2194 IS_INTERNAL_ELEMENT(element))
2196 // internal values used in level editor
2198 ei->access_type = 0;
2199 ei->access_layer = 0;
2200 ei->access_protected = 0;
2201 ei->walk_to_action = 0;
2202 ei->smash_targets = 0;
2205 ei->can_explode_by_fire = FALSE;
2206 ei->can_explode_smashed = FALSE;
2207 ei->can_explode_impact = FALSE;
2209 ei->current_change_page = 0;
2212 if (IS_GROUP_ELEMENT(element) ||
2213 IS_INTERNAL_ELEMENT(element))
2215 struct ElementGroupInfo *group;
2217 // initialize memory for list of elements in group
2218 if (ei->group == NULL)
2219 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2223 xx_group = *group; // copy group data into temporary buffer
2225 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2230 if (IS_EMPTY_ELEMENT(element) ||
2231 IS_INTERNAL_ELEMENT(element))
2233 xx_ei = *ei; // copy element data into temporary buffer
2235 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2241 clipboard_elements_initialized = TRUE;
2244 static void setLevelInfoToDefaults(struct LevelInfo *level,
2245 boolean level_info_only,
2246 boolean reset_file_status)
2248 setLevelInfoToDefaults_Level(level);
2250 if (!level_info_only)
2251 setLevelInfoToDefaults_Elements(level);
2253 if (reset_file_status)
2255 level->no_valid_file = FALSE;
2256 level->no_level_file = FALSE;
2259 level->changed = FALSE;
2262 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2264 level_file_info->nr = 0;
2265 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2266 level_file_info->packed = FALSE;
2268 setString(&level_file_info->basename, NULL);
2269 setString(&level_file_info->filename, NULL);
2272 int getMappedElement_SB(int, boolean);
2274 static void ActivateLevelTemplate(void)
2278 if (check_special_flags("load_xsb_to_ces"))
2280 // fill smaller playfields with padding "beyond border wall" elements
2281 if (level.fieldx < level_template.fieldx ||
2282 level.fieldy < level_template.fieldy)
2284 short field[level.fieldx][level.fieldy];
2285 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2286 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2287 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2288 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2290 // copy old playfield (which is smaller than the visible area)
2291 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2292 field[x][y] = level.field[x][y];
2294 // fill new, larger playfield with "beyond border wall" elements
2295 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2296 level.field[x][y] = getMappedElement_SB('_', TRUE);
2298 // copy the old playfield to the middle of the new playfield
2299 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2300 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2302 level.fieldx = new_fieldx;
2303 level.fieldy = new_fieldy;
2307 // Currently there is no special action needed to activate the template
2308 // data, because 'element_info' property settings overwrite the original
2309 // level data, while all other variables do not change.
2311 // Exception: 'from_level_template' elements in the original level playfield
2312 // are overwritten with the corresponding elements at the same position in
2313 // playfield from the level template.
2315 for (x = 0; x < level.fieldx; x++)
2316 for (y = 0; y < level.fieldy; y++)
2317 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2318 level.field[x][y] = level_template.field[x][y];
2320 if (check_special_flags("load_xsb_to_ces"))
2322 struct LevelInfo level_backup = level;
2324 // overwrite all individual level settings from template level settings
2325 level = level_template;
2327 // restore level file info
2328 level.file_info = level_backup.file_info;
2330 // restore playfield size
2331 level.fieldx = level_backup.fieldx;
2332 level.fieldy = level_backup.fieldy;
2334 // restore playfield content
2335 for (x = 0; x < level.fieldx; x++)
2336 for (y = 0; y < level.fieldy; y++)
2337 level.field[x][y] = level_backup.field[x][y];
2339 // restore name and author from individual level
2340 strcpy(level.name, level_backup.name);
2341 strcpy(level.author, level_backup.author);
2343 // restore flag "use_custom_template"
2344 level.use_custom_template = level_backup.use_custom_template;
2348 static boolean checkForPackageFromBasename_BD(char *basename)
2350 // check for native BD level file extensions
2351 if (!strSuffixLower(basename, ".bd") &&
2352 !strSuffixLower(basename, ".bdr") &&
2353 !strSuffixLower(basename, ".brc") &&
2354 !strSuffixLower(basename, ".gds"))
2357 // check for standard single-level BD files (like "001.bd")
2358 if (strSuffixLower(basename, ".bd") &&
2359 strlen(basename) == 6 &&
2360 basename[0] >= '0' && basename[0] <= '9' &&
2361 basename[1] >= '0' && basename[1] <= '9' &&
2362 basename[2] >= '0' && basename[2] <= '9')
2365 // this is a level package in native BD file format
2369 static char *getLevelFilenameFromBasename(char *basename)
2371 static char *filename = NULL;
2373 checked_free(filename);
2375 filename = getPath2(getCurrentLevelDir(), basename);
2380 static int getFileTypeFromBasename(char *basename)
2382 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2384 static char *filename = NULL;
2385 struct stat file_status;
2387 // ---------- try to determine file type from filename ----------
2389 // check for typical filename of a Supaplex level package file
2390 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2391 return LEVEL_FILE_TYPE_SP;
2393 // check for typical filename of a Diamond Caves II level package file
2394 if (strSuffixLower(basename, ".dc") ||
2395 strSuffixLower(basename, ".dc2"))
2396 return LEVEL_FILE_TYPE_DC;
2398 // check for typical filename of a Sokoban level package file
2399 if (strSuffixLower(basename, ".xsb") &&
2400 strchr(basename, '%') == NULL)
2401 return LEVEL_FILE_TYPE_SB;
2403 // check for typical filename of a Boulder Dash (GDash) level package file
2404 if (checkForPackageFromBasename_BD(basename))
2405 return LEVEL_FILE_TYPE_BD;
2407 // ---------- try to determine file type from filesize ----------
2409 checked_free(filename);
2410 filename = getPath2(getCurrentLevelDir(), basename);
2412 if (stat(filename, &file_status) == 0)
2414 // check for typical filesize of a Supaplex level package file
2415 if (file_status.st_size == 170496)
2416 return LEVEL_FILE_TYPE_SP;
2419 return LEVEL_FILE_TYPE_UNKNOWN;
2422 static int getFileTypeFromMagicBytes(char *filename, int type)
2426 if ((file = openFile(filename, MODE_READ)))
2428 char chunk_name[CHUNK_ID_LEN + 1];
2430 getFileChunkBE(file, chunk_name, NULL);
2432 if (strEqual(chunk_name, "MMII") ||
2433 strEqual(chunk_name, "MIRR"))
2434 type = LEVEL_FILE_TYPE_MM;
2442 static boolean checkForPackageFromBasename(char *basename)
2444 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2445 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2447 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2450 static char *getSingleLevelBasenameExt(int nr, char *extension)
2452 static char basename[MAX_FILENAME_LEN];
2455 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2457 sprintf(basename, "%03d.%s", nr, extension);
2462 static char *getSingleLevelBasename(int nr)
2464 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2467 static char *getPackedLevelBasename(int type)
2469 static char basename[MAX_FILENAME_LEN];
2470 char *directory = getCurrentLevelDir();
2472 DirectoryEntry *dir_entry;
2474 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2476 if ((dir = openDirectory(directory)) == NULL)
2478 Warn("cannot read current level directory '%s'", directory);
2483 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2485 char *entry_basename = dir_entry->basename;
2486 int entry_type = getFileTypeFromBasename(entry_basename);
2488 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2490 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2493 strcpy(basename, entry_basename);
2500 closeDirectory(dir);
2505 static char *getSingleLevelFilename(int nr)
2507 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2510 #if ENABLE_UNUSED_CODE
2511 static char *getPackedLevelFilename(int type)
2513 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2517 char *getDefaultLevelFilename(int nr)
2519 return getSingleLevelFilename(nr);
2522 #if ENABLE_UNUSED_CODE
2523 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2527 lfi->packed = FALSE;
2529 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2530 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2534 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2535 int type, char *format, ...)
2537 static char basename[MAX_FILENAME_LEN];
2540 va_start(ap, format);
2541 vsprintf(basename, format, ap);
2545 lfi->packed = FALSE;
2547 setString(&lfi->basename, basename);
2548 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2551 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2557 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2558 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2561 static int getFiletypeFromID(char *filetype_id)
2563 char *filetype_id_lower;
2564 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2567 if (filetype_id == NULL)
2568 return LEVEL_FILE_TYPE_UNKNOWN;
2570 filetype_id_lower = getStringToLower(filetype_id);
2572 for (i = 0; filetype_id_list[i].id != NULL; i++)
2574 char *id_lower = getStringToLower(filetype_id_list[i].id);
2576 if (strEqual(filetype_id_lower, id_lower))
2577 filetype = filetype_id_list[i].filetype;
2581 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2585 free(filetype_id_lower);
2590 char *getLocalLevelTemplateFilename(void)
2592 return getDefaultLevelFilename(-1);
2595 char *getGlobalLevelTemplateFilename(void)
2597 // global variable "leveldir_current" must be modified in the loop below
2598 LevelDirTree *leveldir_current_last = leveldir_current;
2599 char *filename = NULL;
2601 // check for template level in path from current to topmost tree node
2603 while (leveldir_current != NULL)
2605 filename = getDefaultLevelFilename(-1);
2607 if (fileExists(filename))
2610 leveldir_current = leveldir_current->node_parent;
2613 // restore global variable "leveldir_current" modified in above loop
2614 leveldir_current = leveldir_current_last;
2619 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2623 // special case: level number is negative => check for level template file
2626 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2627 getSingleLevelBasename(-1));
2629 // replace local level template filename with global template filename
2630 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2632 // no fallback if template file not existing
2636 // special case: check for file name/pattern specified in "levelinfo.conf"
2637 if (leveldir_current->level_filename != NULL)
2639 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2641 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2642 leveldir_current->level_filename, nr);
2644 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2646 if (fileExists(lfi->filename))
2649 else if (leveldir_current->level_filetype != NULL)
2651 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2653 // check for specified native level file with standard file name
2654 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2655 "%03d.%s", nr, LEVELFILE_EXTENSION);
2656 if (fileExists(lfi->filename))
2660 // check for native Rocks'n'Diamonds level file
2661 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2662 "%03d.%s", nr, LEVELFILE_EXTENSION);
2663 if (fileExists(lfi->filename))
2666 // check for native Boulder Dash level file
2667 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2668 if (fileExists(lfi->filename))
2671 // check for Emerald Mine level file (V1)
2672 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2673 'a' + (nr / 10) % 26, '0' + nr % 10);
2674 if (fileExists(lfi->filename))
2676 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2677 'A' + (nr / 10) % 26, '0' + nr % 10);
2678 if (fileExists(lfi->filename))
2681 // check for Emerald Mine level file (V2 to V5)
2682 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2683 if (fileExists(lfi->filename))
2686 // check for Emerald Mine level file (V6 / single mode)
2687 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2688 if (fileExists(lfi->filename))
2690 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2691 if (fileExists(lfi->filename))
2694 // check for Emerald Mine level file (V6 / teamwork mode)
2695 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2696 if (fileExists(lfi->filename))
2698 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2699 if (fileExists(lfi->filename))
2702 // check for various packed level file formats
2703 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2704 if (fileExists(lfi->filename))
2707 // no known level file found -- use default values (and fail later)
2708 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2709 "%03d.%s", nr, LEVELFILE_EXTENSION);
2712 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2714 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2715 lfi->type = getFileTypeFromBasename(lfi->basename);
2717 if (lfi->type == LEVEL_FILE_TYPE_RND)
2718 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2721 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2723 // always start with reliable default values
2724 setFileInfoToDefaults(level_file_info);
2726 level_file_info->nr = nr; // set requested level number
2728 determineLevelFileInfo_Filename(level_file_info);
2729 determineLevelFileInfo_Filetype(level_file_info);
2732 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2733 struct LevelFileInfo *lfi_to)
2735 lfi_to->nr = lfi_from->nr;
2736 lfi_to->type = lfi_from->type;
2737 lfi_to->packed = lfi_from->packed;
2739 setString(&lfi_to->basename, lfi_from->basename);
2740 setString(&lfi_to->filename, lfi_from->filename);
2743 // ----------------------------------------------------------------------------
2744 // functions for loading R'n'D level
2745 // ----------------------------------------------------------------------------
2747 int getMappedElement(int element)
2749 // remap some (historic, now obsolete) elements
2753 case EL_PLAYER_OBSOLETE:
2754 element = EL_PLAYER_1;
2757 case EL_KEY_OBSOLETE:
2761 case EL_EM_KEY_1_FILE_OBSOLETE:
2762 element = EL_EM_KEY_1;
2765 case EL_EM_KEY_2_FILE_OBSOLETE:
2766 element = EL_EM_KEY_2;
2769 case EL_EM_KEY_3_FILE_OBSOLETE:
2770 element = EL_EM_KEY_3;
2773 case EL_EM_KEY_4_FILE_OBSOLETE:
2774 element = EL_EM_KEY_4;
2777 case EL_ENVELOPE_OBSOLETE:
2778 element = EL_ENVELOPE_1;
2786 if (element >= NUM_FILE_ELEMENTS)
2788 Warn("invalid level element %d", element);
2790 element = EL_UNKNOWN;
2798 static int getMappedElementByVersion(int element, int game_version)
2800 // remap some elements due to certain game version
2802 if (game_version <= VERSION_IDENT(2,2,0,0))
2804 // map game font elements
2805 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2806 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2807 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2808 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2811 if (game_version < VERSION_IDENT(3,0,0,0))
2813 // map Supaplex gravity tube elements
2814 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2815 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2816 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2817 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2824 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2826 level->file_version = getFileVersion(file);
2827 level->game_version = getFileVersion(file);
2832 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2834 level->creation_date.year = getFile16BitBE(file);
2835 level->creation_date.month = getFile8Bit(file);
2836 level->creation_date.day = getFile8Bit(file);
2838 level->creation_date.src = DATE_SRC_LEVELFILE;
2843 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2845 int initial_player_stepsize;
2846 int initial_player_gravity;
2849 level->fieldx = getFile8Bit(file);
2850 level->fieldy = getFile8Bit(file);
2852 level->time = getFile16BitBE(file);
2853 level->gems_needed = getFile16BitBE(file);
2855 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2856 level->name[i] = getFile8Bit(file);
2857 level->name[MAX_LEVEL_NAME_LEN] = 0;
2859 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2860 level->score[i] = getFile8Bit(file);
2862 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2863 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2864 for (y = 0; y < 3; y++)
2865 for (x = 0; x < 3; x++)
2866 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2868 level->amoeba_speed = getFile8Bit(file);
2869 level->time_magic_wall = getFile8Bit(file);
2870 level->time_wheel = getFile8Bit(file);
2871 level->amoeba_content = getMappedElement(getFile8Bit(file));
2873 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2876 for (i = 0; i < MAX_PLAYERS; i++)
2877 level->initial_player_stepsize[i] = initial_player_stepsize;
2879 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2881 for (i = 0; i < MAX_PLAYERS; i++)
2882 level->initial_player_gravity[i] = initial_player_gravity;
2884 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2885 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2887 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2889 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2890 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2891 level->can_move_into_acid_bits = getFile32BitBE(file);
2892 level->dont_collide_with_bits = getFile8Bit(file);
2894 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2895 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2897 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2898 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2899 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2901 level->game_engine_type = getFile8Bit(file);
2903 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2908 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2912 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2913 level->name[i] = getFile8Bit(file);
2914 level->name[MAX_LEVEL_NAME_LEN] = 0;
2919 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2923 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2924 level->author[i] = getFile8Bit(file);
2925 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2930 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2933 int chunk_size_expected = level->fieldx * level->fieldy;
2935 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2936 stored with 16-bit encoding (and should be twice as big then).
2937 Even worse, playfield data was stored 16-bit when only yamyam content
2938 contained 16-bit elements and vice versa. */
2940 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2941 chunk_size_expected *= 2;
2943 if (chunk_size_expected != chunk_size)
2945 ReadUnusedBytesFromFile(file, chunk_size);
2946 return chunk_size_expected;
2949 for (y = 0; y < level->fieldy; y++)
2950 for (x = 0; x < level->fieldx; x++)
2951 level->field[x][y] =
2952 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2957 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2960 int header_size = 4;
2961 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2962 int chunk_size_expected = header_size + content_size;
2964 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2965 stored with 16-bit encoding (and should be twice as big then).
2966 Even worse, playfield data was stored 16-bit when only yamyam content
2967 contained 16-bit elements and vice versa. */
2969 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2970 chunk_size_expected += content_size;
2972 if (chunk_size_expected != chunk_size)
2974 ReadUnusedBytesFromFile(file, chunk_size);
2975 return chunk_size_expected;
2979 level->num_yamyam_contents = getFile8Bit(file);
2983 // correct invalid number of content fields -- should never happen
2984 if (level->num_yamyam_contents < 1 ||
2985 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2986 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2988 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2989 for (y = 0; y < 3; y++)
2990 for (x = 0; x < 3; x++)
2991 level->yamyam_content[i].e[x][y] =
2992 getMappedElement(level->encoding_16bit_field ?
2993 getFile16BitBE(file) : getFile8Bit(file));
2997 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3002 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3004 element = getMappedElement(getFile16BitBE(file));
3005 num_contents = getFile8Bit(file);
3007 getFile8Bit(file); // content x size (unused)
3008 getFile8Bit(file); // content y size (unused)
3010 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3012 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3013 for (y = 0; y < 3; y++)
3014 for (x = 0; x < 3; x++)
3015 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3017 // correct invalid number of content fields -- should never happen
3018 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3019 num_contents = STD_ELEMENT_CONTENTS;
3021 if (element == EL_YAMYAM)
3023 level->num_yamyam_contents = num_contents;
3025 for (i = 0; i < num_contents; i++)
3026 for (y = 0; y < 3; y++)
3027 for (x = 0; x < 3; x++)
3028 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3030 else if (element == EL_BD_AMOEBA)
3032 level->amoeba_content = content_array[0][0][0];
3036 Warn("cannot load content for element '%d'", element);
3042 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3048 int chunk_size_expected;
3050 element = getMappedElement(getFile16BitBE(file));
3051 if (!IS_ENVELOPE(element))
3052 element = EL_ENVELOPE_1;
3054 envelope_nr = element - EL_ENVELOPE_1;
3056 envelope_len = getFile16BitBE(file);
3058 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3059 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3061 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3063 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3064 if (chunk_size_expected != chunk_size)
3066 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3067 return chunk_size_expected;
3070 for (i = 0; i < envelope_len; i++)
3071 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3076 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3078 int num_changed_custom_elements = getFile16BitBE(file);
3079 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3082 if (chunk_size_expected != chunk_size)
3084 ReadUnusedBytesFromFile(file, chunk_size - 2);
3085 return chunk_size_expected;
3088 for (i = 0; i < num_changed_custom_elements; i++)
3090 int element = getMappedElement(getFile16BitBE(file));
3091 int properties = getFile32BitBE(file);
3093 if (IS_CUSTOM_ELEMENT(element))
3094 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3096 Warn("invalid custom element number %d", element);
3098 // older game versions that wrote level files with CUS1 chunks used
3099 // different default push delay values (not yet stored in level file)
3100 element_info[element].push_delay_fixed = 2;
3101 element_info[element].push_delay_random = 8;
3104 level->file_has_custom_elements = TRUE;
3109 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3111 int num_changed_custom_elements = getFile16BitBE(file);
3112 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3115 if (chunk_size_expected != chunk_size)
3117 ReadUnusedBytesFromFile(file, chunk_size - 2);
3118 return chunk_size_expected;
3121 for (i = 0; i < num_changed_custom_elements; i++)
3123 int element = getMappedElement(getFile16BitBE(file));
3124 int custom_target_element = getMappedElement(getFile16BitBE(file));
3126 if (IS_CUSTOM_ELEMENT(element))
3127 element_info[element].change->target_element = custom_target_element;
3129 Warn("invalid custom element number %d", element);
3132 level->file_has_custom_elements = TRUE;
3137 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3139 int num_changed_custom_elements = getFile16BitBE(file);
3140 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3143 if (chunk_size_expected != chunk_size)
3145 ReadUnusedBytesFromFile(file, chunk_size - 2);
3146 return chunk_size_expected;
3149 for (i = 0; i < num_changed_custom_elements; i++)
3151 int element = getMappedElement(getFile16BitBE(file));
3152 struct ElementInfo *ei = &element_info[element];
3153 unsigned int event_bits;
3155 if (!IS_CUSTOM_ELEMENT(element))
3157 Warn("invalid custom element number %d", element);
3159 element = EL_INTERNAL_DUMMY;
3162 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3163 ei->description[j] = getFile8Bit(file);
3164 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3166 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3168 // some free bytes for future properties and padding
3169 ReadUnusedBytesFromFile(file, 7);
3171 ei->use_gfx_element = getFile8Bit(file);
3172 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3174 ei->collect_score_initial = getFile8Bit(file);
3175 ei->collect_count_initial = getFile8Bit(file);
3177 ei->push_delay_fixed = getFile16BitBE(file);
3178 ei->push_delay_random = getFile16BitBE(file);
3179 ei->move_delay_fixed = getFile16BitBE(file);
3180 ei->move_delay_random = getFile16BitBE(file);
3182 ei->move_pattern = getFile16BitBE(file);
3183 ei->move_direction_initial = getFile8Bit(file);
3184 ei->move_stepsize = getFile8Bit(file);
3186 for (y = 0; y < 3; y++)
3187 for (x = 0; x < 3; x++)
3188 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3190 // bits 0 - 31 of "has_event[]"
3191 event_bits = getFile32BitBE(file);
3192 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3193 if (event_bits & (1u << j))
3194 ei->change->has_event[j] = TRUE;
3196 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3198 ei->change->delay_fixed = getFile16BitBE(file);
3199 ei->change->delay_random = getFile16BitBE(file);
3200 ei->change->delay_frames = getFile16BitBE(file);
3202 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3204 ei->change->explode = getFile8Bit(file);
3205 ei->change->use_target_content = getFile8Bit(file);
3206 ei->change->only_if_complete = getFile8Bit(file);
3207 ei->change->use_random_replace = getFile8Bit(file);
3209 ei->change->random_percentage = getFile8Bit(file);
3210 ei->change->replace_when = getFile8Bit(file);
3212 for (y = 0; y < 3; y++)
3213 for (x = 0; x < 3; x++)
3214 ei->change->target_content.e[x][y] =
3215 getMappedElement(getFile16BitBE(file));
3217 ei->slippery_type = getFile8Bit(file);
3219 // some free bytes for future properties and padding
3220 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3222 // mark that this custom element has been modified
3223 ei->modified_settings = TRUE;
3226 level->file_has_custom_elements = TRUE;
3231 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3233 struct ElementInfo *ei;
3234 int chunk_size_expected;
3238 // ---------- custom element base property values (96 bytes) ----------------
3240 element = getMappedElement(getFile16BitBE(file));
3242 if (!IS_CUSTOM_ELEMENT(element))
3244 Warn("invalid custom element number %d", element);
3246 ReadUnusedBytesFromFile(file, chunk_size - 2);
3251 ei = &element_info[element];
3253 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3254 ei->description[i] = getFile8Bit(file);
3255 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3257 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3259 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3261 ei->num_change_pages = getFile8Bit(file);
3263 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3264 if (chunk_size_expected != chunk_size)
3266 ReadUnusedBytesFromFile(file, chunk_size - 43);
3267 return chunk_size_expected;
3270 ei->ce_value_fixed_initial = getFile16BitBE(file);
3271 ei->ce_value_random_initial = getFile16BitBE(file);
3272 ei->use_last_ce_value = getFile8Bit(file);
3274 ei->use_gfx_element = getFile8Bit(file);
3275 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3277 ei->collect_score_initial = getFile8Bit(file);
3278 ei->collect_count_initial = getFile8Bit(file);
3280 ei->drop_delay_fixed = getFile8Bit(file);
3281 ei->push_delay_fixed = getFile8Bit(file);
3282 ei->drop_delay_random = getFile8Bit(file);
3283 ei->push_delay_random = getFile8Bit(file);
3284 ei->move_delay_fixed = getFile16BitBE(file);
3285 ei->move_delay_random = getFile16BitBE(file);
3287 // bits 0 - 15 of "move_pattern" ...
3288 ei->move_pattern = getFile16BitBE(file);
3289 ei->move_direction_initial = getFile8Bit(file);
3290 ei->move_stepsize = getFile8Bit(file);
3292 ei->slippery_type = getFile8Bit(file);
3294 for (y = 0; y < 3; y++)
3295 for (x = 0; x < 3; x++)
3296 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3298 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3299 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3300 ei->move_leave_type = getFile8Bit(file);
3302 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3303 ei->move_pattern |= (getFile16BitBE(file) << 16);
3305 ei->access_direction = getFile8Bit(file);
3307 ei->explosion_delay = getFile8Bit(file);
3308 ei->ignition_delay = getFile8Bit(file);
3309 ei->explosion_type = getFile8Bit(file);
3311 // some free bytes for future custom property values and padding
3312 ReadUnusedBytesFromFile(file, 1);
3314 // ---------- change page property values (48 bytes) ------------------------
3316 setElementChangePages(ei, ei->num_change_pages);
3318 for (i = 0; i < ei->num_change_pages; i++)
3320 struct ElementChangeInfo *change = &ei->change_page[i];
3321 unsigned int event_bits;
3323 // always start with reliable default values
3324 setElementChangeInfoToDefaults(change);
3326 // bits 0 - 31 of "has_event[]" ...
3327 event_bits = getFile32BitBE(file);
3328 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3329 if (event_bits & (1u << j))
3330 change->has_event[j] = TRUE;
3332 change->target_element = getMappedElement(getFile16BitBE(file));
3334 change->delay_fixed = getFile16BitBE(file);
3335 change->delay_random = getFile16BitBE(file);
3336 change->delay_frames = getFile16BitBE(file);
3338 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3340 change->explode = getFile8Bit(file);
3341 change->use_target_content = getFile8Bit(file);
3342 change->only_if_complete = getFile8Bit(file);
3343 change->use_random_replace = getFile8Bit(file);
3345 change->random_percentage = getFile8Bit(file);
3346 change->replace_when = getFile8Bit(file);
3348 for (y = 0; y < 3; y++)
3349 for (x = 0; x < 3; x++)
3350 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3352 change->can_change = getFile8Bit(file);
3354 change->trigger_side = getFile8Bit(file);
3356 change->trigger_player = getFile8Bit(file);
3357 change->trigger_page = getFile8Bit(file);
3359 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3360 CH_PAGE_ANY : (1 << change->trigger_page));
3362 change->has_action = getFile8Bit(file);
3363 change->action_type = getFile8Bit(file);
3364 change->action_mode = getFile8Bit(file);
3365 change->action_arg = getFile16BitBE(file);
3367 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3368 event_bits = getFile8Bit(file);
3369 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3370 if (event_bits & (1u << (j - 32)))
3371 change->has_event[j] = TRUE;
3374 // mark this custom element as modified
3375 ei->modified_settings = TRUE;
3377 level->file_has_custom_elements = TRUE;
3382 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3384 struct ElementInfo *ei;
3385 struct ElementGroupInfo *group;
3389 element = getMappedElement(getFile16BitBE(file));
3391 if (!IS_GROUP_ELEMENT(element))
3393 Warn("invalid group element number %d", element);
3395 ReadUnusedBytesFromFile(file, chunk_size - 2);
3400 ei = &element_info[element];
3402 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3403 ei->description[i] = getFile8Bit(file);
3404 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3406 group = element_info[element].group;
3408 group->num_elements = getFile8Bit(file);
3410 ei->use_gfx_element = getFile8Bit(file);
3411 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3413 group->choice_mode = getFile8Bit(file);
3415 // some free bytes for future values and padding
3416 ReadUnusedBytesFromFile(file, 3);
3418 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3419 group->element[i] = getMappedElement(getFile16BitBE(file));
3421 // mark this group element as modified
3422 element_info[element].modified_settings = TRUE;
3424 level->file_has_custom_elements = TRUE;
3429 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3430 int element, int real_element)
3432 int micro_chunk_size = 0;
3433 int conf_type = getFile8Bit(file);
3434 int byte_mask = conf_type & CONF_MASK_BYTES;
3435 boolean element_found = FALSE;
3438 micro_chunk_size += 1;
3440 if (byte_mask == CONF_MASK_MULTI_BYTES)
3442 int num_bytes = getFile16BitBE(file);
3443 byte *buffer = checked_malloc(num_bytes);
3445 ReadBytesFromFile(file, buffer, num_bytes);
3447 for (i = 0; conf[i].data_type != -1; i++)
3449 if (conf[i].element == element &&
3450 conf[i].conf_type == conf_type)
3452 int data_type = conf[i].data_type;
3453 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3454 int max_num_entities = conf[i].max_num_entities;
3456 if (num_entities > max_num_entities)
3458 Warn("truncating number of entities for element %d from %d to %d",
3459 element, num_entities, max_num_entities);
3461 num_entities = max_num_entities;
3464 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3465 data_type == TYPE_CONTENT_LIST))
3467 // for element and content lists, zero entities are not allowed
3468 Warn("found empty list of entities for element %d", element);
3470 // do not set "num_entities" here to prevent reading behind buffer
3472 *(int *)(conf[i].num_entities) = 1; // at least one is required
3476 *(int *)(conf[i].num_entities) = num_entities;
3479 element_found = TRUE;
3481 if (data_type == TYPE_STRING)
3483 char *string = (char *)(conf[i].value);
3486 for (j = 0; j < max_num_entities; j++)
3487 string[j] = (j < num_entities ? buffer[j] : '\0');
3489 else if (data_type == TYPE_ELEMENT_LIST)
3491 int *element_array = (int *)(conf[i].value);
3494 for (j = 0; j < num_entities; j++)
3496 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3498 else if (data_type == TYPE_CONTENT_LIST)
3500 struct Content *content= (struct Content *)(conf[i].value);
3503 for (c = 0; c < num_entities; c++)
3504 for (y = 0; y < 3; y++)
3505 for (x = 0; x < 3; x++)
3506 content[c].e[x][y] =
3507 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3510 element_found = FALSE;
3516 checked_free(buffer);
3518 micro_chunk_size += 2 + num_bytes;
3520 else // constant size configuration data (1, 2 or 4 bytes)
3522 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3523 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3524 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3526 for (i = 0; conf[i].data_type != -1; i++)
3528 if (conf[i].element == element &&
3529 conf[i].conf_type == conf_type)
3531 int data_type = conf[i].data_type;
3533 if (data_type == TYPE_ELEMENT)
3534 value = getMappedElement(value);
3536 if (data_type == TYPE_BOOLEAN)
3537 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3539 *(int *) (conf[i].value) = value;
3541 element_found = TRUE;
3547 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3552 char *error_conf_chunk_bytes =
3553 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3554 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3555 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3556 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3557 int error_element = real_element;
3559 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3560 error_conf_chunk_bytes, error_conf_chunk_token,
3561 error_element, EL_NAME(error_element));
3564 return micro_chunk_size;
3567 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3569 int real_chunk_size = 0;
3571 li = *level; // copy level data into temporary buffer
3573 while (!checkEndOfFile(file))
3575 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3577 if (real_chunk_size >= chunk_size)
3581 *level = li; // copy temporary buffer back to level data
3583 return real_chunk_size;
3586 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3588 int real_chunk_size = 0;
3590 li = *level; // copy level data into temporary buffer
3592 while (!checkEndOfFile(file))
3594 int element = getMappedElement(getFile16BitBE(file));
3596 real_chunk_size += 2;
3597 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3599 if (real_chunk_size >= chunk_size)
3603 *level = li; // copy temporary buffer back to level data
3605 return real_chunk_size;
3608 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3610 int real_chunk_size = 0;
3612 li = *level; // copy level data into temporary buffer
3614 while (!checkEndOfFile(file))
3616 int element = getMappedElement(getFile16BitBE(file));
3618 real_chunk_size += 2;
3619 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3621 if (real_chunk_size >= chunk_size)
3625 *level = li; // copy temporary buffer back to level data
3627 return real_chunk_size;
3630 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3632 int element = getMappedElement(getFile16BitBE(file));
3633 int envelope_nr = element - EL_ENVELOPE_1;
3634 int real_chunk_size = 2;
3636 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3638 while (!checkEndOfFile(file))
3640 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3643 if (real_chunk_size >= chunk_size)
3647 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3649 return real_chunk_size;
3652 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3654 int element = getMappedElement(getFile16BitBE(file));
3655 int real_chunk_size = 2;
3656 struct ElementInfo *ei = &element_info[element];
3659 xx_ei = *ei; // copy element data into temporary buffer
3661 xx_ei.num_change_pages = -1;
3663 while (!checkEndOfFile(file))
3665 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3667 if (xx_ei.num_change_pages != -1)
3670 if (real_chunk_size >= chunk_size)
3676 if (ei->num_change_pages == -1)
3678 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3681 ei->num_change_pages = 1;
3683 setElementChangePages(ei, 1);
3684 setElementChangeInfoToDefaults(ei->change);
3686 return real_chunk_size;
3689 // initialize number of change pages stored for this custom element
3690 setElementChangePages(ei, ei->num_change_pages);
3691 for (i = 0; i < ei->num_change_pages; i++)
3692 setElementChangeInfoToDefaults(&ei->change_page[i]);
3694 // start with reading properties for the first change page
3695 xx_current_change_page = 0;
3697 while (!checkEndOfFile(file))
3699 // level file might contain invalid change page number
3700 if (xx_current_change_page >= ei->num_change_pages)
3703 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3705 xx_change = *change; // copy change data into temporary buffer
3707 resetEventBits(); // reset bits; change page might have changed
3709 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3712 *change = xx_change;
3714 setEventFlagsFromEventBits(change);
3716 if (real_chunk_size >= chunk_size)
3720 level->file_has_custom_elements = TRUE;
3722 return real_chunk_size;
3725 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3727 int element = getMappedElement(getFile16BitBE(file));
3728 int real_chunk_size = 2;
3729 struct ElementInfo *ei = &element_info[element];
3730 struct ElementGroupInfo *group = ei->group;
3735 xx_ei = *ei; // copy element data into temporary buffer
3736 xx_group = *group; // copy group data into temporary buffer
3738 while (!checkEndOfFile(file))
3740 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3743 if (real_chunk_size >= chunk_size)
3750 level->file_has_custom_elements = TRUE;
3752 return real_chunk_size;
3755 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3757 int element = getMappedElement(getFile16BitBE(file));
3758 int real_chunk_size = 2;
3759 struct ElementInfo *ei = &element_info[element];
3761 xx_ei = *ei; // copy element data into temporary buffer
3763 while (!checkEndOfFile(file))
3765 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3768 if (real_chunk_size >= chunk_size)
3774 level->file_has_custom_elements = TRUE;
3776 return real_chunk_size;
3779 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3780 struct LevelFileInfo *level_file_info,
3781 boolean level_info_only)
3783 char *filename = level_file_info->filename;
3784 char cookie[MAX_LINE_LEN];
3785 char chunk_name[CHUNK_ID_LEN + 1];
3789 if (!(file = openFile(filename, MODE_READ)))
3791 level->no_valid_file = TRUE;
3792 level->no_level_file = TRUE;
3794 if (level_info_only)
3797 Warn("cannot read level '%s' -- using empty level", filename);
3799 if (!setup.editor.use_template_for_new_levels)
3802 // if level file not found, try to initialize level data from template
3803 filename = getGlobalLevelTemplateFilename();
3805 if (!(file = openFile(filename, MODE_READ)))
3808 // default: for empty levels, use level template for custom elements
3809 level->use_custom_template = TRUE;
3811 level->no_valid_file = FALSE;
3814 getFileChunkBE(file, chunk_name, NULL);
3815 if (strEqual(chunk_name, "RND1"))
3817 getFile32BitBE(file); // not used
3819 getFileChunkBE(file, chunk_name, NULL);
3820 if (!strEqual(chunk_name, "CAVE"))
3822 level->no_valid_file = TRUE;
3824 Warn("unknown format of level file '%s'", filename);
3831 else // check for pre-2.0 file format with cookie string
3833 strcpy(cookie, chunk_name);
3834 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3836 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3837 cookie[strlen(cookie) - 1] = '\0';
3839 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3841 level->no_valid_file = TRUE;
3843 Warn("unknown format of level file '%s'", filename);
3850 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3852 level->no_valid_file = TRUE;
3854 Warn("unsupported version of level file '%s'", filename);
3861 // pre-2.0 level files have no game version, so use file version here
3862 level->game_version = level->file_version;
3865 if (level->file_version < FILE_VERSION_1_2)
3867 // level files from versions before 1.2.0 without chunk structure
3868 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3869 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3877 int (*loader)(File *, int, struct LevelInfo *);
3881 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3882 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3883 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3884 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3885 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3886 { "INFO", -1, LoadLevel_INFO },
3887 { "BODY", -1, LoadLevel_BODY },
3888 { "CONT", -1, LoadLevel_CONT },
3889 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3890 { "CNT3", -1, LoadLevel_CNT3 },
3891 { "CUS1", -1, LoadLevel_CUS1 },
3892 { "CUS2", -1, LoadLevel_CUS2 },
3893 { "CUS3", -1, LoadLevel_CUS3 },
3894 { "CUS4", -1, LoadLevel_CUS4 },
3895 { "GRP1", -1, LoadLevel_GRP1 },
3896 { "CONF", -1, LoadLevel_CONF },
3897 { "ELEM", -1, LoadLevel_ELEM },
3898 { "NOTE", -1, LoadLevel_NOTE },
3899 { "CUSX", -1, LoadLevel_CUSX },
3900 { "GRPX", -1, LoadLevel_GRPX },
3901 { "EMPX", -1, LoadLevel_EMPX },
3906 while (getFileChunkBE(file, chunk_name, &chunk_size))
3910 while (chunk_info[i].name != NULL &&
3911 !strEqual(chunk_name, chunk_info[i].name))
3914 if (chunk_info[i].name == NULL)
3916 Warn("unknown chunk '%s' in level file '%s'",
3917 chunk_name, filename);
3919 ReadUnusedBytesFromFile(file, chunk_size);
3921 else if (chunk_info[i].size != -1 &&
3922 chunk_info[i].size != chunk_size)
3924 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3925 chunk_size, chunk_name, filename);
3927 ReadUnusedBytesFromFile(file, chunk_size);
3931 // call function to load this level chunk
3932 int chunk_size_expected =
3933 (chunk_info[i].loader)(file, chunk_size, level);
3935 if (chunk_size_expected < 0)
3937 Warn("error reading chunk '%s' in level file '%s'",
3938 chunk_name, filename);
3943 // the size of some chunks cannot be checked before reading other
3944 // chunks first (like "HEAD" and "BODY") that contain some header
3945 // information, so check them here
3946 if (chunk_size_expected != chunk_size)
3948 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3949 chunk_size, chunk_name, filename);
3961 // ----------------------------------------------------------------------------
3962 // functions for loading BD level
3963 // ----------------------------------------------------------------------------
3965 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3967 struct LevelInfo_BD *level_bd = level->native_bd_level;
3968 GdCave *cave = NULL; // will be changed below
3969 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3970 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3973 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3975 // cave and map newly allocated when set to defaults above
3976 cave = level_bd->cave;
3979 cave->intermission = level->bd_intermission;
3982 cave->level_time[0] = level->time;
3983 cave->level_diamonds[0] = level->gems_needed;
3986 cave->scheduling = level->bd_scheduling_type;
3987 cave->pal_timing = level->bd_pal_timing;
3988 cave->level_speed[0] = level->bd_cycle_delay_ms;
3989 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3990 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3991 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3994 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3995 cave->diamond_value = level->score[SC_EMERALD];
3996 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3998 // compatibility settings
3999 cave->lineshift = level->bd_line_shifting_borders;
4000 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4001 cave->short_explosions = level->bd_short_explosions;
4002 cave->gravity_affects_all = level->bd_gravity_affects_all;
4004 // player properties
4005 cave->diagonal_movements = level->bd_diagonal_movements;
4006 cave->active_is_first_found = level->bd_topmost_player_active;
4007 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4008 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4009 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4010 cave->snap_element = map_element_RND_to_BD(level->bd_snap_element);
4012 // element properties
4013 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4014 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4015 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4016 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4017 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4018 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4019 cave->level_magic_wall_time[0] = level->time_magic_wall;
4020 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4021 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4022 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4023 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4024 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4025 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4026 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4027 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4028 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4029 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4030 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4031 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4032 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4034 cave->amoeba_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
4035 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
4036 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
4037 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
4038 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
4039 cave->amoeba_2_looks_like = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
4041 cave->slime_predictable = level->bd_slime_is_predictable;
4042 cave->slime_correct_random = level->bd_slime_correct_random;
4043 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4044 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4045 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4046 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4048 cave->acid_eats_this = map_element_RND_to_BD(level->bd_acid_eats_element);
4049 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4050 cave->acid_turns_to = map_element_RND_to_BD(level->bd_acid_turns_to_element);
4052 cave->biter_delay_frame = level->bd_biter_move_delay;
4053 cave->biter_eat = map_element_RND_to_BD(level->bd_biter_eats_element);
4055 cave->bladder_converts_by = map_element_RND_to_BD(level->bd_bladder_converts_by_element);
4057 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4059 cave->replicators_active = level->bd_replicators_active;
4060 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4063 strncpy(cave->name, level->name, sizeof(GdString));
4064 cave->name[sizeof(GdString) - 1] = '\0';
4066 // playfield elements
4067 for (x = 0; x < cave->w; x++)
4068 for (y = 0; y < cave->h; y++)
4069 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
4072 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4074 struct LevelInfo_BD *level_bd = level->native_bd_level;
4075 GdCave *cave = level_bd->cave;
4076 int bd_level_nr = level_bd->level_nr;
4079 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4080 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4083 level->bd_intermission = cave->intermission;
4086 level->time = cave->level_time[bd_level_nr];
4087 level->gems_needed = cave->level_diamonds[bd_level_nr];
4090 level->bd_scheduling_type = cave->scheduling;
4091 level->bd_pal_timing = cave->pal_timing;
4092 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4093 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4094 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4095 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4098 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4099 level->score[SC_EMERALD] = cave->diamond_value;
4100 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4102 // compatibility settings
4103 level->bd_line_shifting_borders = cave->lineshift;
4104 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4105 level->bd_short_explosions = cave->short_explosions;
4106 level->bd_gravity_affects_all = cave->gravity_affects_all;
4108 // player properties
4109 level->bd_diagonal_movements = cave->diagonal_movements;
4110 level->bd_topmost_player_active = cave->active_is_first_found;
4111 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4112 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4113 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4114 level->bd_snap_element = map_element_BD_to_RND(cave->snap_element);
4116 // element properties
4117 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4118 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4119 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4120 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4121 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4122 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4123 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4124 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4125 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4126 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4127 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4128 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4129 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4130 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4131 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4132 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4133 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4134 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4135 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4136 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4138 level->bd_amoeba_content_too_big = map_element_BD_to_RND(cave->amoeba_too_big_effect);
4139 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4140 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4141 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4142 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4143 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4145 level->bd_slime_is_predictable = cave->slime_predictable;
4146 level->bd_slime_correct_random = cave->slime_correct_random;
4147 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4148 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4149 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4150 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4152 level->bd_acid_eats_element = map_element_BD_to_RND(cave->acid_eats_this);
4153 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4154 level->bd_acid_turns_to_element = map_element_BD_to_RND(cave->acid_turns_to);
4156 level->bd_biter_move_delay = cave->biter_delay_frame;
4157 level->bd_biter_eats_element = map_element_BD_to_RND(cave->biter_eat);
4159 level->bd_bladder_converts_by_element = map_element_BD_to_RND(cave->bladder_converts_by);
4161 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4163 level->bd_replicators_active = cave->replicators_active;
4164 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4167 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4169 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4170 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4172 // playfield elements
4173 for (x = 0; x < level->fieldx; x++)
4174 for (y = 0; y < level->fieldy; y++)
4175 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4177 checked_free(cave_name);
4180 static void setTapeInfoToDefaults(void);
4182 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4184 struct LevelInfo_BD *level_bd = level->native_bd_level;
4185 GdCave *cave = level_bd->cave;
4186 GdReplay *replay = level_bd->replay;
4192 // always start with reliable default values
4193 setTapeInfoToDefaults();
4195 tape.level_nr = level_nr; // (currently not used)
4196 tape.random_seed = replay->seed;
4198 TapeSetDateFromIsoDateString(replay->date);
4201 tape.pos[tape.counter].delay = 0;
4203 tape.bd_replay = TRUE;
4205 // all time calculations only used to display approximate tape time
4206 int cave_speed = cave->speed;
4207 int milliseconds_game = 0;
4208 int milliseconds_elapsed = 20;
4210 for (i = 0; i < replay->movements->len; i++)
4212 int replay_action = replay->movements->data[i];
4213 int tape_action = map_action_BD_to_RND(replay_action);
4214 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4215 boolean success = 0;
4219 success = TapeAddAction(action);
4221 milliseconds_game += milliseconds_elapsed;
4223 if (milliseconds_game >= cave_speed)
4225 milliseconds_game -= cave_speed;
4232 tape.pos[tape.counter].delay = 0;
4233 tape.pos[tape.counter].action[0] = 0;
4237 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4243 TapeHaltRecording();
4247 // ----------------------------------------------------------------------------
4248 // functions for loading EM level
4249 // ----------------------------------------------------------------------------
4251 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4253 static int ball_xy[8][2] =
4264 struct LevelInfo_EM *level_em = level->native_em_level;
4265 struct CAVE *cav = level_em->cav;
4268 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4269 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4271 cav->time_seconds = level->time;
4272 cav->gems_needed = level->gems_needed;
4274 cav->emerald_score = level->score[SC_EMERALD];
4275 cav->diamond_score = level->score[SC_DIAMOND];
4276 cav->alien_score = level->score[SC_ROBOT];
4277 cav->tank_score = level->score[SC_SPACESHIP];
4278 cav->bug_score = level->score[SC_BUG];
4279 cav->eater_score = level->score[SC_YAMYAM];
4280 cav->nut_score = level->score[SC_NUT];
4281 cav->dynamite_score = level->score[SC_DYNAMITE];
4282 cav->key_score = level->score[SC_KEY];
4283 cav->exit_score = level->score[SC_TIME_BONUS];
4285 cav->num_eater_arrays = level->num_yamyam_contents;
4287 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4288 for (y = 0; y < 3; y++)
4289 for (x = 0; x < 3; x++)
4290 cav->eater_array[i][y * 3 + x] =
4291 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4293 cav->amoeba_time = level->amoeba_speed;
4294 cav->wonderwall_time = level->time_magic_wall;
4295 cav->wheel_time = level->time_wheel;
4297 cav->android_move_time = level->android_move_time;
4298 cav->android_clone_time = level->android_clone_time;
4299 cav->ball_random = level->ball_random;
4300 cav->ball_active = level->ball_active_initial;
4301 cav->ball_time = level->ball_time;
4302 cav->num_ball_arrays = level->num_ball_contents;
4304 cav->lenses_score = level->lenses_score;
4305 cav->magnify_score = level->magnify_score;
4306 cav->slurp_score = level->slurp_score;
4308 cav->lenses_time = level->lenses_time;
4309 cav->magnify_time = level->magnify_time;
4311 cav->wind_time = 9999;
4312 cav->wind_direction =
4313 map_direction_RND_to_EM(level->wind_direction_initial);
4315 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4316 for (j = 0; j < 8; j++)
4317 cav->ball_array[i][j] =
4318 map_element_RND_to_EM_cave(level->ball_content[i].
4319 e[ball_xy[j][0]][ball_xy[j][1]]);
4321 map_android_clone_elements_RND_to_EM(level);
4323 // first fill the complete playfield with the empty space element
4324 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4325 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4326 cav->cave[x][y] = Cblank;
4328 // then copy the real level contents from level file into the playfield
4329 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4331 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4333 if (level->field[x][y] == EL_AMOEBA_DEAD)
4334 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4336 cav->cave[x][y] = new_element;
4339 for (i = 0; i < MAX_PLAYERS; i++)
4341 cav->player_x[i] = -1;
4342 cav->player_y[i] = -1;
4345 // initialize player positions and delete players from the playfield
4346 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4348 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4350 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4352 cav->player_x[player_nr] = x;
4353 cav->player_y[player_nr] = y;
4355 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4360 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4362 static int ball_xy[8][2] =
4373 struct LevelInfo_EM *level_em = level->native_em_level;
4374 struct CAVE *cav = level_em->cav;
4377 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4378 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4380 level->time = cav->time_seconds;
4381 level->gems_needed = cav->gems_needed;
4383 sprintf(level->name, "Level %d", level->file_info.nr);
4385 level->score[SC_EMERALD] = cav->emerald_score;
4386 level->score[SC_DIAMOND] = cav->diamond_score;
4387 level->score[SC_ROBOT] = cav->alien_score;
4388 level->score[SC_SPACESHIP] = cav->tank_score;
4389 level->score[SC_BUG] = cav->bug_score;
4390 level->score[SC_YAMYAM] = cav->eater_score;
4391 level->score[SC_NUT] = cav->nut_score;
4392 level->score[SC_DYNAMITE] = cav->dynamite_score;
4393 level->score[SC_KEY] = cav->key_score;
4394 level->score[SC_TIME_BONUS] = cav->exit_score;
4396 level->num_yamyam_contents = cav->num_eater_arrays;
4398 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4399 for (y = 0; y < 3; y++)
4400 for (x = 0; x < 3; x++)
4401 level->yamyam_content[i].e[x][y] =
4402 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4404 level->amoeba_speed = cav->amoeba_time;
4405 level->time_magic_wall = cav->wonderwall_time;
4406 level->time_wheel = cav->wheel_time;
4408 level->android_move_time = cav->android_move_time;
4409 level->android_clone_time = cav->android_clone_time;
4410 level->ball_random = cav->ball_random;
4411 level->ball_active_initial = cav->ball_active;
4412 level->ball_time = cav->ball_time;
4413 level->num_ball_contents = cav->num_ball_arrays;
4415 level->lenses_score = cav->lenses_score;
4416 level->magnify_score = cav->magnify_score;
4417 level->slurp_score = cav->slurp_score;
4419 level->lenses_time = cav->lenses_time;
4420 level->magnify_time = cav->magnify_time;
4422 level->wind_direction_initial =
4423 map_direction_EM_to_RND(cav->wind_direction);
4425 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4426 for (j = 0; j < 8; j++)
4427 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4428 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4430 map_android_clone_elements_EM_to_RND(level);
4432 // convert the playfield (some elements need special treatment)
4433 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4435 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4437 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4438 new_element = EL_AMOEBA_DEAD;
4440 level->field[x][y] = new_element;
4443 for (i = 0; i < MAX_PLAYERS; i++)
4445 // in case of all players set to the same field, use the first player
4446 int nr = MAX_PLAYERS - i - 1;
4447 int jx = cav->player_x[nr];
4448 int jy = cav->player_y[nr];
4450 if (jx != -1 && jy != -1)
4451 level->field[jx][jy] = EL_PLAYER_1 + nr;
4454 // time score is counted for each 10 seconds left in Emerald Mine levels
4455 level->time_score_base = 10;
4459 // ----------------------------------------------------------------------------
4460 // functions for loading SP level
4461 // ----------------------------------------------------------------------------
4463 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4465 struct LevelInfo_SP *level_sp = level->native_sp_level;
4466 LevelInfoType *header = &level_sp->header;
4469 level_sp->width = level->fieldx;
4470 level_sp->height = level->fieldy;
4472 for (x = 0; x < level->fieldx; x++)
4473 for (y = 0; y < level->fieldy; y++)
4474 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4476 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4478 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4479 header->LevelTitle[i] = level->name[i];
4480 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4482 header->InfotronsNeeded = level->gems_needed;
4484 header->SpecialPortCount = 0;
4486 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4488 boolean gravity_port_found = FALSE;
4489 boolean gravity_port_valid = FALSE;
4490 int gravity_port_flag;
4491 int gravity_port_base_element;
4492 int element = level->field[x][y];
4494 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4495 element <= EL_SP_GRAVITY_ON_PORT_UP)
4497 gravity_port_found = TRUE;
4498 gravity_port_valid = TRUE;
4499 gravity_port_flag = 1;
4500 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4502 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4503 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4505 gravity_port_found = TRUE;
4506 gravity_port_valid = TRUE;
4507 gravity_port_flag = 0;
4508 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4510 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4511 element <= EL_SP_GRAVITY_PORT_UP)
4513 // change R'n'D style gravity inverting special port to normal port
4514 // (there are no gravity inverting ports in native Supaplex engine)
4516 gravity_port_found = TRUE;
4517 gravity_port_valid = FALSE;
4518 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4521 if (gravity_port_found)
4523 if (gravity_port_valid &&
4524 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4526 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4528 port->PortLocation = (y * level->fieldx + x) * 2;
4529 port->Gravity = gravity_port_flag;
4531 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4533 header->SpecialPortCount++;
4537 // change special gravity port to normal port
4539 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4542 level_sp->playfield[x][y] = element - EL_SP_START;
4547 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4549 struct LevelInfo_SP *level_sp = level->native_sp_level;
4550 LevelInfoType *header = &level_sp->header;
4551 boolean num_invalid_elements = 0;
4554 level->fieldx = level_sp->width;
4555 level->fieldy = level_sp->height;
4557 for (x = 0; x < level->fieldx; x++)
4559 for (y = 0; y < level->fieldy; y++)
4561 int element_old = level_sp->playfield[x][y];
4562 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4564 if (element_new == EL_UNKNOWN)
4566 num_invalid_elements++;
4568 Debug("level:native:SP", "invalid element %d at position %d, %d",
4572 level->field[x][y] = element_new;
4576 if (num_invalid_elements > 0)
4577 Warn("found %d invalid elements%s", num_invalid_elements,
4578 (!options.debug ? " (use '--debug' for more details)" : ""));
4580 for (i = 0; i < MAX_PLAYERS; i++)
4581 level->initial_player_gravity[i] =
4582 (header->InitialGravity == 1 ? TRUE : FALSE);
4584 // skip leading spaces
4585 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4586 if (header->LevelTitle[i] != ' ')
4590 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4591 level->name[j] = header->LevelTitle[i];
4592 level->name[j] = '\0';
4594 // cut trailing spaces
4596 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4597 level->name[j - 1] = '\0';
4599 level->gems_needed = header->InfotronsNeeded;
4601 for (i = 0; i < header->SpecialPortCount; i++)
4603 SpecialPortType *port = &header->SpecialPort[i];
4604 int port_location = port->PortLocation;
4605 int gravity = port->Gravity;
4606 int port_x, port_y, port_element;
4608 port_x = (port_location / 2) % level->fieldx;
4609 port_y = (port_location / 2) / level->fieldx;
4611 if (port_x < 0 || port_x >= level->fieldx ||
4612 port_y < 0 || port_y >= level->fieldy)
4614 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4619 port_element = level->field[port_x][port_y];
4621 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4622 port_element > EL_SP_GRAVITY_PORT_UP)
4624 Warn("no special port at position (%d, %d)", port_x, port_y);
4629 // change previous (wrong) gravity inverting special port to either
4630 // gravity enabling special port or gravity disabling special port
4631 level->field[port_x][port_y] +=
4632 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4633 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4636 // change special gravity ports without database entries to normal ports
4637 for (x = 0; x < level->fieldx; x++)
4638 for (y = 0; y < level->fieldy; y++)
4639 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4640 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4641 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4643 level->time = 0; // no time limit
4644 level->amoeba_speed = 0;
4645 level->time_magic_wall = 0;
4646 level->time_wheel = 0;
4647 level->amoeba_content = EL_EMPTY;
4649 // original Supaplex does not use score values -- rate by playing time
4650 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4651 level->score[i] = 0;
4653 level->rate_time_over_score = TRUE;
4655 // there are no yamyams in supaplex levels
4656 for (i = 0; i < level->num_yamyam_contents; i++)
4657 for (x = 0; x < 3; x++)
4658 for (y = 0; y < 3; y++)
4659 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4662 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4664 struct LevelInfo_SP *level_sp = level->native_sp_level;
4665 struct DemoInfo_SP *demo = &level_sp->demo;
4668 // always start with reliable default values
4669 demo->is_available = FALSE;
4672 if (TAPE_IS_EMPTY(tape))
4675 demo->level_nr = tape.level_nr; // (currently not used)
4677 level_sp->header.DemoRandomSeed = tape.random_seed;
4681 for (i = 0; i < tape.length; i++)
4683 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4684 int demo_repeat = tape.pos[i].delay;
4685 int demo_entries = (demo_repeat + 15) / 16;
4687 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4689 Warn("tape truncated: size exceeds maximum SP demo size %d",
4695 for (j = 0; j < demo_repeat / 16; j++)
4696 demo->data[demo->length++] = 0xf0 | demo_action;
4698 if (demo_repeat % 16)
4699 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4702 demo->is_available = TRUE;
4705 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4707 struct LevelInfo_SP *level_sp = level->native_sp_level;
4708 struct DemoInfo_SP *demo = &level_sp->demo;
4709 char *filename = level->file_info.filename;
4712 // always start with reliable default values
4713 setTapeInfoToDefaults();
4715 if (!demo->is_available)
4718 tape.level_nr = demo->level_nr; // (currently not used)
4719 tape.random_seed = level_sp->header.DemoRandomSeed;
4721 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4724 tape.pos[tape.counter].delay = 0;
4726 for (i = 0; i < demo->length; i++)
4728 int demo_action = demo->data[i] & 0x0f;
4729 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4730 int tape_action = map_key_SP_to_RND(demo_action);
4731 int tape_repeat = demo_repeat + 1;
4732 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4733 boolean success = 0;
4736 for (j = 0; j < tape_repeat; j++)
4737 success = TapeAddAction(action);
4741 Warn("SP demo truncated: size exceeds maximum tape size %d",
4748 TapeHaltRecording();
4752 // ----------------------------------------------------------------------------
4753 // functions for loading MM level
4754 // ----------------------------------------------------------------------------
4756 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4758 struct LevelInfo_MM *level_mm = level->native_mm_level;
4761 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4762 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4764 level_mm->time = level->time;
4765 level_mm->kettles_needed = level->gems_needed;
4766 level_mm->auto_count_kettles = level->auto_count_gems;
4768 level_mm->mm_laser_red = level->mm_laser_red;
4769 level_mm->mm_laser_green = level->mm_laser_green;
4770 level_mm->mm_laser_blue = level->mm_laser_blue;
4772 level_mm->df_laser_red = level->df_laser_red;
4773 level_mm->df_laser_green = level->df_laser_green;
4774 level_mm->df_laser_blue = level->df_laser_blue;
4776 strcpy(level_mm->name, level->name);
4777 strcpy(level_mm->author, level->author);
4779 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4780 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4781 level_mm->score[SC_KEY] = level->score[SC_KEY];
4782 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4783 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4785 level_mm->amoeba_speed = level->amoeba_speed;
4786 level_mm->time_fuse = level->mm_time_fuse;
4787 level_mm->time_bomb = level->mm_time_bomb;
4788 level_mm->time_ball = level->mm_time_ball;
4789 level_mm->time_block = level->mm_time_block;
4791 level_mm->num_ball_contents = level->num_mm_ball_contents;
4792 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4793 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4794 level_mm->explode_ball = level->explode_mm_ball;
4796 for (i = 0; i < level->num_mm_ball_contents; i++)
4797 level_mm->ball_content[i] =
4798 map_element_RND_to_MM(level->mm_ball_content[i]);
4800 for (x = 0; x < level->fieldx; x++)
4801 for (y = 0; y < level->fieldy; y++)
4803 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4806 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4808 struct LevelInfo_MM *level_mm = level->native_mm_level;
4811 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4812 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4814 level->time = level_mm->time;
4815 level->gems_needed = level_mm->kettles_needed;
4816 level->auto_count_gems = level_mm->auto_count_kettles;
4818 level->mm_laser_red = level_mm->mm_laser_red;
4819 level->mm_laser_green = level_mm->mm_laser_green;
4820 level->mm_laser_blue = level_mm->mm_laser_blue;
4822 level->df_laser_red = level_mm->df_laser_red;
4823 level->df_laser_green = level_mm->df_laser_green;
4824 level->df_laser_blue = level_mm->df_laser_blue;
4826 strcpy(level->name, level_mm->name);
4828 // only overwrite author from 'levelinfo.conf' if author defined in level
4829 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4830 strcpy(level->author, level_mm->author);
4832 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4833 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4834 level->score[SC_KEY] = level_mm->score[SC_KEY];
4835 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4836 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4838 level->amoeba_speed = level_mm->amoeba_speed;
4839 level->mm_time_fuse = level_mm->time_fuse;
4840 level->mm_time_bomb = level_mm->time_bomb;
4841 level->mm_time_ball = level_mm->time_ball;
4842 level->mm_time_block = level_mm->time_block;
4844 level->num_mm_ball_contents = level_mm->num_ball_contents;
4845 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4846 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4847 level->explode_mm_ball = level_mm->explode_ball;
4849 for (i = 0; i < level->num_mm_ball_contents; i++)
4850 level->mm_ball_content[i] =
4851 map_element_MM_to_RND(level_mm->ball_content[i]);
4853 for (x = 0; x < level->fieldx; x++)
4854 for (y = 0; y < level->fieldy; y++)
4855 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4859 // ----------------------------------------------------------------------------
4860 // functions for loading DC level
4861 // ----------------------------------------------------------------------------
4863 #define DC_LEVEL_HEADER_SIZE 344
4865 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4868 static int last_data_encoded;
4872 int diff_hi, diff_lo;
4873 int data_hi, data_lo;
4874 unsigned short data_decoded;
4878 last_data_encoded = 0;
4885 diff = data_encoded - last_data_encoded;
4886 diff_hi = diff & ~0xff;
4887 diff_lo = diff & 0xff;
4891 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4892 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4893 data_hi = data_hi & 0xff00;
4895 data_decoded = data_hi | data_lo;
4897 last_data_encoded = data_encoded;
4899 offset1 = (offset1 + 1) % 31;
4900 offset2 = offset2 & 0xff;
4902 return data_decoded;
4905 static int getMappedElement_DC(int element)
4913 // 0x0117 - 0x036e: (?)
4916 // 0x042d - 0x0684: (?)
4932 element = EL_CRYSTAL;
4935 case 0x0e77: // quicksand (boulder)
4936 element = EL_QUICKSAND_FAST_FULL;
4939 case 0x0e99: // slow quicksand (boulder)
4940 element = EL_QUICKSAND_FULL;
4944 element = EL_EM_EXIT_OPEN;
4948 element = EL_EM_EXIT_CLOSED;
4952 element = EL_EM_STEEL_EXIT_OPEN;
4956 element = EL_EM_STEEL_EXIT_CLOSED;
4959 case 0x0f4f: // dynamite (lit 1)
4960 element = EL_EM_DYNAMITE_ACTIVE;
4963 case 0x0f57: // dynamite (lit 2)
4964 element = EL_EM_DYNAMITE_ACTIVE;
4967 case 0x0f5f: // dynamite (lit 3)
4968 element = EL_EM_DYNAMITE_ACTIVE;
4971 case 0x0f67: // dynamite (lit 4)
4972 element = EL_EM_DYNAMITE_ACTIVE;
4979 element = EL_AMOEBA_WET;
4983 element = EL_AMOEBA_DROP;
4987 element = EL_DC_MAGIC_WALL;
4991 element = EL_SPACESHIP_UP;
4995 element = EL_SPACESHIP_DOWN;
4999 element = EL_SPACESHIP_LEFT;
5003 element = EL_SPACESHIP_RIGHT;
5007 element = EL_BUG_UP;
5011 element = EL_BUG_DOWN;
5015 element = EL_BUG_LEFT;
5019 element = EL_BUG_RIGHT;
5023 element = EL_MOLE_UP;
5027 element = EL_MOLE_DOWN;
5031 element = EL_MOLE_LEFT;
5035 element = EL_MOLE_RIGHT;
5043 element = EL_YAMYAM_UP;
5047 element = EL_SWITCHGATE_OPEN;
5051 element = EL_SWITCHGATE_CLOSED;
5055 element = EL_DC_SWITCHGATE_SWITCH_UP;
5059 element = EL_TIMEGATE_CLOSED;
5062 case 0x144c: // conveyor belt switch (green)
5063 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5066 case 0x144f: // conveyor belt switch (red)
5067 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5070 case 0x1452: // conveyor belt switch (blue)
5071 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5075 element = EL_CONVEYOR_BELT_3_MIDDLE;
5079 element = EL_CONVEYOR_BELT_3_LEFT;
5083 element = EL_CONVEYOR_BELT_3_RIGHT;
5087 element = EL_CONVEYOR_BELT_1_MIDDLE;
5091 element = EL_CONVEYOR_BELT_1_LEFT;
5095 element = EL_CONVEYOR_BELT_1_RIGHT;
5099 element = EL_CONVEYOR_BELT_4_MIDDLE;
5103 element = EL_CONVEYOR_BELT_4_LEFT;
5107 element = EL_CONVEYOR_BELT_4_RIGHT;
5111 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5115 element = EL_EXPANDABLE_WALL_VERTICAL;
5119 element = EL_EXPANDABLE_WALL_ANY;
5122 case 0x14ce: // growing steel wall (left/right)
5123 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5126 case 0x14df: // growing steel wall (up/down)
5127 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5130 case 0x14e8: // growing steel wall (up/down/left/right)
5131 element = EL_EXPANDABLE_STEELWALL_ANY;
5135 element = EL_SHIELD_DEADLY;
5139 element = EL_EXTRA_TIME;
5147 element = EL_EMPTY_SPACE;
5150 case 0x1578: // quicksand (empty)
5151 element = EL_QUICKSAND_FAST_EMPTY;
5154 case 0x1579: // slow quicksand (empty)
5155 element = EL_QUICKSAND_EMPTY;
5165 element = EL_EM_DYNAMITE;
5168 case 0x15a1: // key (red)
5169 element = EL_EM_KEY_1;
5172 case 0x15a2: // key (yellow)
5173 element = EL_EM_KEY_2;
5176 case 0x15a3: // key (blue)
5177 element = EL_EM_KEY_4;
5180 case 0x15a4: // key (green)
5181 element = EL_EM_KEY_3;
5184 case 0x15a5: // key (white)
5185 element = EL_DC_KEY_WHITE;
5189 element = EL_WALL_SLIPPERY;
5196 case 0x15a8: // wall (not round)
5200 case 0x15a9: // (blue)
5201 element = EL_CHAR_A;
5204 case 0x15aa: // (blue)
5205 element = EL_CHAR_B;
5208 case 0x15ab: // (blue)
5209 element = EL_CHAR_C;
5212 case 0x15ac: // (blue)
5213 element = EL_CHAR_D;
5216 case 0x15ad: // (blue)
5217 element = EL_CHAR_E;
5220 case 0x15ae: // (blue)
5221 element = EL_CHAR_F;
5224 case 0x15af: // (blue)
5225 element = EL_CHAR_G;
5228 case 0x15b0: // (blue)
5229 element = EL_CHAR_H;
5232 case 0x15b1: // (blue)
5233 element = EL_CHAR_I;
5236 case 0x15b2: // (blue)
5237 element = EL_CHAR_J;
5240 case 0x15b3: // (blue)
5241 element = EL_CHAR_K;
5244 case 0x15b4: // (blue)
5245 element = EL_CHAR_L;
5248 case 0x15b5: // (blue)
5249 element = EL_CHAR_M;
5252 case 0x15b6: // (blue)
5253 element = EL_CHAR_N;
5256 case 0x15b7: // (blue)
5257 element = EL_CHAR_O;
5260 case 0x15b8: // (blue)
5261 element = EL_CHAR_P;
5264 case 0x15b9: // (blue)
5265 element = EL_CHAR_Q;
5268 case 0x15ba: // (blue)
5269 element = EL_CHAR_R;
5272 case 0x15bb: // (blue)
5273 element = EL_CHAR_S;
5276 case 0x15bc: // (blue)
5277 element = EL_CHAR_T;
5280 case 0x15bd: // (blue)
5281 element = EL_CHAR_U;
5284 case 0x15be: // (blue)
5285 element = EL_CHAR_V;
5288 case 0x15bf: // (blue)
5289 element = EL_CHAR_W;
5292 case 0x15c0: // (blue)
5293 element = EL_CHAR_X;
5296 case 0x15c1: // (blue)
5297 element = EL_CHAR_Y;
5300 case 0x15c2: // (blue)
5301 element = EL_CHAR_Z;
5304 case 0x15c3: // (blue)
5305 element = EL_CHAR_AUMLAUT;
5308 case 0x15c4: // (blue)
5309 element = EL_CHAR_OUMLAUT;
5312 case 0x15c5: // (blue)
5313 element = EL_CHAR_UUMLAUT;
5316 case 0x15c6: // (blue)
5317 element = EL_CHAR_0;
5320 case 0x15c7: // (blue)
5321 element = EL_CHAR_1;
5324 case 0x15c8: // (blue)
5325 element = EL_CHAR_2;
5328 case 0x15c9: // (blue)
5329 element = EL_CHAR_3;
5332 case 0x15ca: // (blue)
5333 element = EL_CHAR_4;
5336 case 0x15cb: // (blue)
5337 element = EL_CHAR_5;
5340 case 0x15cc: // (blue)
5341 element = EL_CHAR_6;
5344 case 0x15cd: // (blue)
5345 element = EL_CHAR_7;
5348 case 0x15ce: // (blue)
5349 element = EL_CHAR_8;
5352 case 0x15cf: // (blue)
5353 element = EL_CHAR_9;
5356 case 0x15d0: // (blue)
5357 element = EL_CHAR_PERIOD;
5360 case 0x15d1: // (blue)
5361 element = EL_CHAR_EXCLAM;
5364 case 0x15d2: // (blue)
5365 element = EL_CHAR_COLON;
5368 case 0x15d3: // (blue)
5369 element = EL_CHAR_LESS;
5372 case 0x15d4: // (blue)
5373 element = EL_CHAR_GREATER;
5376 case 0x15d5: // (blue)
5377 element = EL_CHAR_QUESTION;
5380 case 0x15d6: // (blue)
5381 element = EL_CHAR_COPYRIGHT;
5384 case 0x15d7: // (blue)
5385 element = EL_CHAR_UP;
5388 case 0x15d8: // (blue)
5389 element = EL_CHAR_DOWN;
5392 case 0x15d9: // (blue)
5393 element = EL_CHAR_BUTTON;
5396 case 0x15da: // (blue)
5397 element = EL_CHAR_PLUS;
5400 case 0x15db: // (blue)
5401 element = EL_CHAR_MINUS;
5404 case 0x15dc: // (blue)
5405 element = EL_CHAR_APOSTROPHE;
5408 case 0x15dd: // (blue)
5409 element = EL_CHAR_PARENLEFT;
5412 case 0x15de: // (blue)
5413 element = EL_CHAR_PARENRIGHT;
5416 case 0x15df: // (green)
5417 element = EL_CHAR_A;
5420 case 0x15e0: // (green)
5421 element = EL_CHAR_B;
5424 case 0x15e1: // (green)
5425 element = EL_CHAR_C;
5428 case 0x15e2: // (green)
5429 element = EL_CHAR_D;
5432 case 0x15e3: // (green)
5433 element = EL_CHAR_E;
5436 case 0x15e4: // (green)
5437 element = EL_CHAR_F;
5440 case 0x15e5: // (green)
5441 element = EL_CHAR_G;
5444 case 0x15e6: // (green)
5445 element = EL_CHAR_H;
5448 case 0x15e7: // (green)
5449 element = EL_CHAR_I;
5452 case 0x15e8: // (green)
5453 element = EL_CHAR_J;
5456 case 0x15e9: // (green)
5457 element = EL_CHAR_K;
5460 case 0x15ea: // (green)
5461 element = EL_CHAR_L;
5464 case 0x15eb: // (green)
5465 element = EL_CHAR_M;
5468 case 0x15ec: // (green)
5469 element = EL_CHAR_N;
5472 case 0x15ed: // (green)
5473 element = EL_CHAR_O;
5476 case 0x15ee: // (green)
5477 element = EL_CHAR_P;
5480 case 0x15ef: // (green)
5481 element = EL_CHAR_Q;
5484 case 0x15f0: // (green)
5485 element = EL_CHAR_R;
5488 case 0x15f1: // (green)
5489 element = EL_CHAR_S;
5492 case 0x15f2: // (green)
5493 element = EL_CHAR_T;
5496 case 0x15f3: // (green)
5497 element = EL_CHAR_U;
5500 case 0x15f4: // (green)
5501 element = EL_CHAR_V;
5504 case 0x15f5: // (green)
5505 element = EL_CHAR_W;
5508 case 0x15f6: // (green)
5509 element = EL_CHAR_X;
5512 case 0x15f7: // (green)
5513 element = EL_CHAR_Y;
5516 case 0x15f8: // (green)
5517 element = EL_CHAR_Z;
5520 case 0x15f9: // (green)
5521 element = EL_CHAR_AUMLAUT;
5524 case 0x15fa: // (green)
5525 element = EL_CHAR_OUMLAUT;
5528 case 0x15fb: // (green)
5529 element = EL_CHAR_UUMLAUT;
5532 case 0x15fc: // (green)
5533 element = EL_CHAR_0;
5536 case 0x15fd: // (green)
5537 element = EL_CHAR_1;
5540 case 0x15fe: // (green)
5541 element = EL_CHAR_2;
5544 case 0x15ff: // (green)
5545 element = EL_CHAR_3;
5548 case 0x1600: // (green)
5549 element = EL_CHAR_4;
5552 case 0x1601: // (green)
5553 element = EL_CHAR_5;
5556 case 0x1602: // (green)
5557 element = EL_CHAR_6;
5560 case 0x1603: // (green)
5561 element = EL_CHAR_7;
5564 case 0x1604: // (green)
5565 element = EL_CHAR_8;
5568 case 0x1605: // (green)
5569 element = EL_CHAR_9;
5572 case 0x1606: // (green)
5573 element = EL_CHAR_PERIOD;
5576 case 0x1607: // (green)
5577 element = EL_CHAR_EXCLAM;
5580 case 0x1608: // (green)
5581 element = EL_CHAR_COLON;
5584 case 0x1609: // (green)
5585 element = EL_CHAR_LESS;
5588 case 0x160a: // (green)
5589 element = EL_CHAR_GREATER;
5592 case 0x160b: // (green)
5593 element = EL_CHAR_QUESTION;
5596 case 0x160c: // (green)
5597 element = EL_CHAR_COPYRIGHT;
5600 case 0x160d: // (green)
5601 element = EL_CHAR_UP;
5604 case 0x160e: // (green)
5605 element = EL_CHAR_DOWN;
5608 case 0x160f: // (green)
5609 element = EL_CHAR_BUTTON;
5612 case 0x1610: // (green)
5613 element = EL_CHAR_PLUS;
5616 case 0x1611: // (green)
5617 element = EL_CHAR_MINUS;
5620 case 0x1612: // (green)
5621 element = EL_CHAR_APOSTROPHE;
5624 case 0x1613: // (green)
5625 element = EL_CHAR_PARENLEFT;
5628 case 0x1614: // (green)
5629 element = EL_CHAR_PARENRIGHT;
5632 case 0x1615: // (blue steel)
5633 element = EL_STEEL_CHAR_A;
5636 case 0x1616: // (blue steel)
5637 element = EL_STEEL_CHAR_B;
5640 case 0x1617: // (blue steel)
5641 element = EL_STEEL_CHAR_C;
5644 case 0x1618: // (blue steel)
5645 element = EL_STEEL_CHAR_D;
5648 case 0x1619: // (blue steel)
5649 element = EL_STEEL_CHAR_E;
5652 case 0x161a: // (blue steel)
5653 element = EL_STEEL_CHAR_F;
5656 case 0x161b: // (blue steel)
5657 element = EL_STEEL_CHAR_G;
5660 case 0x161c: // (blue steel)
5661 element = EL_STEEL_CHAR_H;
5664 case 0x161d: // (blue steel)
5665 element = EL_STEEL_CHAR_I;
5668 case 0x161e: // (blue steel)
5669 element = EL_STEEL_CHAR_J;
5672 case 0x161f: // (blue steel)
5673 element = EL_STEEL_CHAR_K;
5676 case 0x1620: // (blue steel)
5677 element = EL_STEEL_CHAR_L;
5680 case 0x1621: // (blue steel)
5681 element = EL_STEEL_CHAR_M;
5684 case 0x1622: // (blue steel)
5685 element = EL_STEEL_CHAR_N;
5688 case 0x1623: // (blue steel)
5689 element = EL_STEEL_CHAR_O;
5692 case 0x1624: // (blue steel)
5693 element = EL_STEEL_CHAR_P;
5696 case 0x1625: // (blue steel)
5697 element = EL_STEEL_CHAR_Q;
5700 case 0x1626: // (blue steel)
5701 element = EL_STEEL_CHAR_R;
5704 case 0x1627: // (blue steel)
5705 element = EL_STEEL_CHAR_S;
5708 case 0x1628: // (blue steel)
5709 element = EL_STEEL_CHAR_T;
5712 case 0x1629: // (blue steel)
5713 element = EL_STEEL_CHAR_U;
5716 case 0x162a: // (blue steel)
5717 element = EL_STEEL_CHAR_V;
5720 case 0x162b: // (blue steel)
5721 element = EL_STEEL_CHAR_W;
5724 case 0x162c: // (blue steel)
5725 element = EL_STEEL_CHAR_X;
5728 case 0x162d: // (blue steel)
5729 element = EL_STEEL_CHAR_Y;
5732 case 0x162e: // (blue steel)
5733 element = EL_STEEL_CHAR_Z;
5736 case 0x162f: // (blue steel)
5737 element = EL_STEEL_CHAR_AUMLAUT;
5740 case 0x1630: // (blue steel)
5741 element = EL_STEEL_CHAR_OUMLAUT;
5744 case 0x1631: // (blue steel)
5745 element = EL_STEEL_CHAR_UUMLAUT;
5748 case 0x1632: // (blue steel)
5749 element = EL_STEEL_CHAR_0;
5752 case 0x1633: // (blue steel)
5753 element = EL_STEEL_CHAR_1;
5756 case 0x1634: // (blue steel)
5757 element = EL_STEEL_CHAR_2;
5760 case 0x1635: // (blue steel)
5761 element = EL_STEEL_CHAR_3;
5764 case 0x1636: // (blue steel)
5765 element = EL_STEEL_CHAR_4;
5768 case 0x1637: // (blue steel)
5769 element = EL_STEEL_CHAR_5;
5772 case 0x1638: // (blue steel)
5773 element = EL_STEEL_CHAR_6;
5776 case 0x1639: // (blue steel)
5777 element = EL_STEEL_CHAR_7;
5780 case 0x163a: // (blue steel)
5781 element = EL_STEEL_CHAR_8;
5784 case 0x163b: // (blue steel)
5785 element = EL_STEEL_CHAR_9;
5788 case 0x163c: // (blue steel)
5789 element = EL_STEEL_CHAR_PERIOD;
5792 case 0x163d: // (blue steel)
5793 element = EL_STEEL_CHAR_EXCLAM;
5796 case 0x163e: // (blue steel)
5797 element = EL_STEEL_CHAR_COLON;
5800 case 0x163f: // (blue steel)
5801 element = EL_STEEL_CHAR_LESS;
5804 case 0x1640: // (blue steel)
5805 element = EL_STEEL_CHAR_GREATER;
5808 case 0x1641: // (blue steel)
5809 element = EL_STEEL_CHAR_QUESTION;
5812 case 0x1642: // (blue steel)
5813 element = EL_STEEL_CHAR_COPYRIGHT;
5816 case 0x1643: // (blue steel)
5817 element = EL_STEEL_CHAR_UP;
5820 case 0x1644: // (blue steel)
5821 element = EL_STEEL_CHAR_DOWN;
5824 case 0x1645: // (blue steel)
5825 element = EL_STEEL_CHAR_BUTTON;
5828 case 0x1646: // (blue steel)
5829 element = EL_STEEL_CHAR_PLUS;
5832 case 0x1647: // (blue steel)
5833 element = EL_STEEL_CHAR_MINUS;
5836 case 0x1648: // (blue steel)
5837 element = EL_STEEL_CHAR_APOSTROPHE;
5840 case 0x1649: // (blue steel)
5841 element = EL_STEEL_CHAR_PARENLEFT;
5844 case 0x164a: // (blue steel)
5845 element = EL_STEEL_CHAR_PARENRIGHT;
5848 case 0x164b: // (green steel)
5849 element = EL_STEEL_CHAR_A;
5852 case 0x164c: // (green steel)
5853 element = EL_STEEL_CHAR_B;
5856 case 0x164d: // (green steel)
5857 element = EL_STEEL_CHAR_C;
5860 case 0x164e: // (green steel)
5861 element = EL_STEEL_CHAR_D;
5864 case 0x164f: // (green steel)
5865 element = EL_STEEL_CHAR_E;
5868 case 0x1650: // (green steel)
5869 element = EL_STEEL_CHAR_F;
5872 case 0x1651: // (green steel)
5873 element = EL_STEEL_CHAR_G;
5876 case 0x1652: // (green steel)
5877 element = EL_STEEL_CHAR_H;
5880 case 0x1653: // (green steel)
5881 element = EL_STEEL_CHAR_I;
5884 case 0x1654: // (green steel)
5885 element = EL_STEEL_CHAR_J;
5888 case 0x1655: // (green steel)
5889 element = EL_STEEL_CHAR_K;
5892 case 0x1656: // (green steel)
5893 element = EL_STEEL_CHAR_L;
5896 case 0x1657: // (green steel)
5897 element = EL_STEEL_CHAR_M;
5900 case 0x1658: // (green steel)
5901 element = EL_STEEL_CHAR_N;
5904 case 0x1659: // (green steel)
5905 element = EL_STEEL_CHAR_O;
5908 case 0x165a: // (green steel)
5909 element = EL_STEEL_CHAR_P;
5912 case 0x165b: // (green steel)
5913 element = EL_STEEL_CHAR_Q;
5916 case 0x165c: // (green steel)
5917 element = EL_STEEL_CHAR_R;
5920 case 0x165d: // (green steel)
5921 element = EL_STEEL_CHAR_S;
5924 case 0x165e: // (green steel)
5925 element = EL_STEEL_CHAR_T;
5928 case 0x165f: // (green steel)
5929 element = EL_STEEL_CHAR_U;
5932 case 0x1660: // (green steel)
5933 element = EL_STEEL_CHAR_V;
5936 case 0x1661: // (green steel)
5937 element = EL_STEEL_CHAR_W;
5940 case 0x1662: // (green steel)
5941 element = EL_STEEL_CHAR_X;
5944 case 0x1663: // (green steel)
5945 element = EL_STEEL_CHAR_Y;
5948 case 0x1664: // (green steel)
5949 element = EL_STEEL_CHAR_Z;
5952 case 0x1665: // (green steel)
5953 element = EL_STEEL_CHAR_AUMLAUT;
5956 case 0x1666: // (green steel)
5957 element = EL_STEEL_CHAR_OUMLAUT;
5960 case 0x1667: // (green steel)
5961 element = EL_STEEL_CHAR_UUMLAUT;
5964 case 0x1668: // (green steel)
5965 element = EL_STEEL_CHAR_0;
5968 case 0x1669: // (green steel)
5969 element = EL_STEEL_CHAR_1;
5972 case 0x166a: // (green steel)
5973 element = EL_STEEL_CHAR_2;
5976 case 0x166b: // (green steel)
5977 element = EL_STEEL_CHAR_3;
5980 case 0x166c: // (green steel)
5981 element = EL_STEEL_CHAR_4;
5984 case 0x166d: // (green steel)
5985 element = EL_STEEL_CHAR_5;
5988 case 0x166e: // (green steel)
5989 element = EL_STEEL_CHAR_6;
5992 case 0x166f: // (green steel)
5993 element = EL_STEEL_CHAR_7;
5996 case 0x1670: // (green steel)
5997 element = EL_STEEL_CHAR_8;
6000 case 0x1671: // (green steel)
6001 element = EL_STEEL_CHAR_9;
6004 case 0x1672: // (green steel)
6005 element = EL_STEEL_CHAR_PERIOD;
6008 case 0x1673: // (green steel)
6009 element = EL_STEEL_CHAR_EXCLAM;
6012 case 0x1674: // (green steel)
6013 element = EL_STEEL_CHAR_COLON;
6016 case 0x1675: // (green steel)
6017 element = EL_STEEL_CHAR_LESS;
6020 case 0x1676: // (green steel)
6021 element = EL_STEEL_CHAR_GREATER;
6024 case 0x1677: // (green steel)
6025 element = EL_STEEL_CHAR_QUESTION;
6028 case 0x1678: // (green steel)
6029 element = EL_STEEL_CHAR_COPYRIGHT;
6032 case 0x1679: // (green steel)
6033 element = EL_STEEL_CHAR_UP;
6036 case 0x167a: // (green steel)
6037 element = EL_STEEL_CHAR_DOWN;
6040 case 0x167b: // (green steel)
6041 element = EL_STEEL_CHAR_BUTTON;
6044 case 0x167c: // (green steel)
6045 element = EL_STEEL_CHAR_PLUS;
6048 case 0x167d: // (green steel)
6049 element = EL_STEEL_CHAR_MINUS;
6052 case 0x167e: // (green steel)
6053 element = EL_STEEL_CHAR_APOSTROPHE;
6056 case 0x167f: // (green steel)
6057 element = EL_STEEL_CHAR_PARENLEFT;
6060 case 0x1680: // (green steel)
6061 element = EL_STEEL_CHAR_PARENRIGHT;
6064 case 0x1681: // gate (red)
6065 element = EL_EM_GATE_1;
6068 case 0x1682: // secret gate (red)
6069 element = EL_EM_GATE_1_GRAY;
6072 case 0x1683: // gate (yellow)
6073 element = EL_EM_GATE_2;
6076 case 0x1684: // secret gate (yellow)
6077 element = EL_EM_GATE_2_GRAY;
6080 case 0x1685: // gate (blue)
6081 element = EL_EM_GATE_4;
6084 case 0x1686: // secret gate (blue)
6085 element = EL_EM_GATE_4_GRAY;
6088 case 0x1687: // gate (green)
6089 element = EL_EM_GATE_3;
6092 case 0x1688: // secret gate (green)
6093 element = EL_EM_GATE_3_GRAY;
6096 case 0x1689: // gate (white)
6097 element = EL_DC_GATE_WHITE;
6100 case 0x168a: // secret gate (white)
6101 element = EL_DC_GATE_WHITE_GRAY;
6104 case 0x168b: // secret gate (no key)
6105 element = EL_DC_GATE_FAKE_GRAY;
6109 element = EL_ROBOT_WHEEL;
6113 element = EL_DC_TIMEGATE_SWITCH;
6117 element = EL_ACID_POOL_BOTTOM;
6121 element = EL_ACID_POOL_TOPLEFT;
6125 element = EL_ACID_POOL_TOPRIGHT;
6129 element = EL_ACID_POOL_BOTTOMLEFT;
6133 element = EL_ACID_POOL_BOTTOMRIGHT;
6137 element = EL_STEELWALL;
6141 element = EL_STEELWALL_SLIPPERY;
6144 case 0x1695: // steel wall (not round)
6145 element = EL_STEELWALL;
6148 case 0x1696: // steel wall (left)
6149 element = EL_DC_STEELWALL_1_LEFT;
6152 case 0x1697: // steel wall (bottom)
6153 element = EL_DC_STEELWALL_1_BOTTOM;
6156 case 0x1698: // steel wall (right)
6157 element = EL_DC_STEELWALL_1_RIGHT;
6160 case 0x1699: // steel wall (top)
6161 element = EL_DC_STEELWALL_1_TOP;
6164 case 0x169a: // steel wall (left/bottom)
6165 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6168 case 0x169b: // steel wall (right/bottom)
6169 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6172 case 0x169c: // steel wall (right/top)
6173 element = EL_DC_STEELWALL_1_TOPRIGHT;
6176 case 0x169d: // steel wall (left/top)
6177 element = EL_DC_STEELWALL_1_TOPLEFT;
6180 case 0x169e: // steel wall (right/bottom small)
6181 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6184 case 0x169f: // steel wall (left/bottom small)
6185 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6188 case 0x16a0: // steel wall (right/top small)
6189 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6192 case 0x16a1: // steel wall (left/top small)
6193 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6196 case 0x16a2: // steel wall (left/right)
6197 element = EL_DC_STEELWALL_1_VERTICAL;
6200 case 0x16a3: // steel wall (top/bottom)
6201 element = EL_DC_STEELWALL_1_HORIZONTAL;
6204 case 0x16a4: // steel wall 2 (left end)
6205 element = EL_DC_STEELWALL_2_LEFT;
6208 case 0x16a5: // steel wall 2 (right end)
6209 element = EL_DC_STEELWALL_2_RIGHT;
6212 case 0x16a6: // steel wall 2 (top end)
6213 element = EL_DC_STEELWALL_2_TOP;
6216 case 0x16a7: // steel wall 2 (bottom end)
6217 element = EL_DC_STEELWALL_2_BOTTOM;
6220 case 0x16a8: // steel wall 2 (left/right)
6221 element = EL_DC_STEELWALL_2_HORIZONTAL;
6224 case 0x16a9: // steel wall 2 (up/down)
6225 element = EL_DC_STEELWALL_2_VERTICAL;
6228 case 0x16aa: // steel wall 2 (mid)
6229 element = EL_DC_STEELWALL_2_MIDDLE;
6233 element = EL_SIGN_EXCLAMATION;
6237 element = EL_SIGN_RADIOACTIVITY;
6241 element = EL_SIGN_STOP;
6245 element = EL_SIGN_WHEELCHAIR;
6249 element = EL_SIGN_PARKING;
6253 element = EL_SIGN_NO_ENTRY;
6257 element = EL_SIGN_HEART;
6261 element = EL_SIGN_GIVE_WAY;
6265 element = EL_SIGN_ENTRY_FORBIDDEN;
6269 element = EL_SIGN_EMERGENCY_EXIT;
6273 element = EL_SIGN_YIN_YANG;
6277 element = EL_WALL_EMERALD;
6281 element = EL_WALL_DIAMOND;
6285 element = EL_WALL_PEARL;
6289 element = EL_WALL_CRYSTAL;
6293 element = EL_INVISIBLE_WALL;
6297 element = EL_INVISIBLE_STEELWALL;
6301 // EL_INVISIBLE_SAND
6304 element = EL_LIGHT_SWITCH;
6308 element = EL_ENVELOPE_1;
6312 if (element >= 0x0117 && element <= 0x036e) // (?)
6313 element = EL_DIAMOND;
6314 else if (element >= 0x042d && element <= 0x0684) // (?)
6315 element = EL_EMERALD;
6316 else if (element >= 0x157c && element <= 0x158b)
6318 else if (element >= 0x1590 && element <= 0x159f)
6319 element = EL_DC_LANDMINE;
6320 else if (element >= 0x16bc && element <= 0x16cb)
6321 element = EL_INVISIBLE_SAND;
6324 Warn("unknown Diamond Caves element 0x%04x", element);
6326 element = EL_UNKNOWN;
6331 return getMappedElement(element);
6334 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6336 byte header[DC_LEVEL_HEADER_SIZE];
6338 int envelope_header_pos = 62;
6339 int envelope_content_pos = 94;
6340 int level_name_pos = 251;
6341 int level_author_pos = 292;
6342 int envelope_header_len;
6343 int envelope_content_len;
6345 int level_author_len;
6347 int num_yamyam_contents;
6350 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6352 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6354 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6356 header[i * 2 + 0] = header_word >> 8;
6357 header[i * 2 + 1] = header_word & 0xff;
6360 // read some values from level header to check level decoding integrity
6361 fieldx = header[6] | (header[7] << 8);
6362 fieldy = header[8] | (header[9] << 8);
6363 num_yamyam_contents = header[60] | (header[61] << 8);
6365 // do some simple sanity checks to ensure that level was correctly decoded
6366 if (fieldx < 1 || fieldx > 256 ||
6367 fieldy < 1 || fieldy > 256 ||
6368 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6370 level->no_valid_file = TRUE;
6372 Warn("cannot decode level from stream -- using empty level");
6377 // maximum envelope header size is 31 bytes
6378 envelope_header_len = header[envelope_header_pos];
6379 // maximum envelope content size is 110 (156?) bytes
6380 envelope_content_len = header[envelope_content_pos];
6382 // maximum level title size is 40 bytes
6383 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6384 // maximum level author size is 30 (51?) bytes
6385 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6389 for (i = 0; i < envelope_header_len; i++)
6390 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6391 level->envelope[0].text[envelope_size++] =
6392 header[envelope_header_pos + 1 + i];
6394 if (envelope_header_len > 0 && envelope_content_len > 0)
6396 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6397 level->envelope[0].text[envelope_size++] = '\n';
6398 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6399 level->envelope[0].text[envelope_size++] = '\n';
6402 for (i = 0; i < envelope_content_len; i++)
6403 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6404 level->envelope[0].text[envelope_size++] =
6405 header[envelope_content_pos + 1 + i];
6407 level->envelope[0].text[envelope_size] = '\0';
6409 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6410 level->envelope[0].ysize = 10;
6411 level->envelope[0].autowrap = TRUE;
6412 level->envelope[0].centered = TRUE;
6414 for (i = 0; i < level_name_len; i++)
6415 level->name[i] = header[level_name_pos + 1 + i];
6416 level->name[level_name_len] = '\0';
6418 for (i = 0; i < level_author_len; i++)
6419 level->author[i] = header[level_author_pos + 1 + i];
6420 level->author[level_author_len] = '\0';
6422 num_yamyam_contents = header[60] | (header[61] << 8);
6423 level->num_yamyam_contents =
6424 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6426 for (i = 0; i < num_yamyam_contents; i++)
6428 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6430 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6431 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6433 if (i < MAX_ELEMENT_CONTENTS)
6434 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6438 fieldx = header[6] | (header[7] << 8);
6439 fieldy = header[8] | (header[9] << 8);
6440 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6441 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6443 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6445 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6446 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6448 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6449 level->field[x][y] = getMappedElement_DC(element_dc);
6452 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6453 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6454 level->field[x][y] = EL_PLAYER_1;
6456 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6457 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6458 level->field[x][y] = EL_PLAYER_2;
6460 level->gems_needed = header[18] | (header[19] << 8);
6462 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6463 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6464 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6465 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6466 level->score[SC_NUT] = header[28] | (header[29] << 8);
6467 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6468 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6469 level->score[SC_BUG] = header[34] | (header[35] << 8);
6470 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6471 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6472 level->score[SC_KEY] = header[40] | (header[41] << 8);
6473 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6475 level->time = header[44] | (header[45] << 8);
6477 level->amoeba_speed = header[46] | (header[47] << 8);
6478 level->time_light = header[48] | (header[49] << 8);
6479 level->time_timegate = header[50] | (header[51] << 8);
6480 level->time_wheel = header[52] | (header[53] << 8);
6481 level->time_magic_wall = header[54] | (header[55] << 8);
6482 level->extra_time = header[56] | (header[57] << 8);
6483 level->shield_normal_time = header[58] | (header[59] << 8);
6485 // shield and extra time elements do not have a score
6486 level->score[SC_SHIELD] = 0;
6487 level->extra_time_score = 0;
6489 // set time for normal and deadly shields to the same value
6490 level->shield_deadly_time = level->shield_normal_time;
6492 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6493 // can slip down from flat walls, like normal walls and steel walls
6494 level->em_slippery_gems = TRUE;
6496 // time score is counted for each 10 seconds left in Diamond Caves levels
6497 level->time_score_base = 10;
6500 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6501 struct LevelFileInfo *level_file_info,
6502 boolean level_info_only)
6504 char *filename = level_file_info->filename;
6506 int num_magic_bytes = 8;
6507 char magic_bytes[num_magic_bytes + 1];
6508 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6510 if (!(file = openFile(filename, MODE_READ)))
6512 level->no_valid_file = TRUE;
6514 if (!level_info_only)
6515 Warn("cannot read level '%s' -- using empty level", filename);
6520 // fseek(file, 0x0000, SEEK_SET);
6522 if (level_file_info->packed)
6524 // read "magic bytes" from start of file
6525 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6526 magic_bytes[0] = '\0';
6528 // check "magic bytes" for correct file format
6529 if (!strPrefix(magic_bytes, "DC2"))
6531 level->no_valid_file = TRUE;
6533 Warn("unknown DC level file '%s' -- using empty level", filename);
6538 if (strPrefix(magic_bytes, "DC2Win95") ||
6539 strPrefix(magic_bytes, "DC2Win98"))
6541 int position_first_level = 0x00fa;
6542 int extra_bytes = 4;
6545 // advance file stream to first level inside the level package
6546 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6548 // each block of level data is followed by block of non-level data
6549 num_levels_to_skip *= 2;
6551 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6552 while (num_levels_to_skip >= 0)
6554 // advance file stream to next level inside the level package
6555 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6557 level->no_valid_file = TRUE;
6559 Warn("cannot fseek in file '%s' -- using empty level", filename);
6564 // skip apparently unused extra bytes following each level
6565 ReadUnusedBytesFromFile(file, extra_bytes);
6567 // read size of next level in level package
6568 skip_bytes = getFile32BitLE(file);
6570 num_levels_to_skip--;
6575 level->no_valid_file = TRUE;
6577 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6583 LoadLevelFromFileStream_DC(file, level);
6589 // ----------------------------------------------------------------------------
6590 // functions for loading SB level
6591 // ----------------------------------------------------------------------------
6593 int getMappedElement_SB(int element_ascii, boolean use_ces)
6601 sb_element_mapping[] =
6603 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6604 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6605 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6606 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6607 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6608 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6609 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6610 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6617 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6618 if (element_ascii == sb_element_mapping[i].ascii)
6619 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6621 return EL_UNDEFINED;
6624 static void SetLevelSettings_SB(struct LevelInfo *level)
6628 level->use_step_counter = TRUE;
6631 level->score[SC_TIME_BONUS] = 0;
6632 level->time_score_base = 1;
6633 level->rate_time_over_score = TRUE;
6636 level->auto_exit_sokoban = TRUE;
6639 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6640 struct LevelFileInfo *level_file_info,
6641 boolean level_info_only)
6643 char *filename = level_file_info->filename;
6644 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6645 char last_comment[MAX_LINE_LEN];
6646 char level_name[MAX_LINE_LEN];
6649 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6650 boolean read_continued_line = FALSE;
6651 boolean reading_playfield = FALSE;
6652 boolean got_valid_playfield_line = FALSE;
6653 boolean invalid_playfield_char = FALSE;
6654 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6655 int file_level_nr = 0;
6656 int x = 0, y = 0; // initialized to make compilers happy
6658 last_comment[0] = '\0';
6659 level_name[0] = '\0';
6661 if (!(file = openFile(filename, MODE_READ)))
6663 level->no_valid_file = TRUE;
6665 if (!level_info_only)
6666 Warn("cannot read level '%s' -- using empty level", filename);
6671 while (!checkEndOfFile(file))
6673 // level successfully read, but next level may follow here
6674 if (!got_valid_playfield_line && reading_playfield)
6676 // read playfield from single level file -- skip remaining file
6677 if (!level_file_info->packed)
6680 if (file_level_nr >= num_levels_to_skip)
6685 last_comment[0] = '\0';
6686 level_name[0] = '\0';
6688 reading_playfield = FALSE;
6691 got_valid_playfield_line = FALSE;
6693 // read next line of input file
6694 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6697 // cut trailing line break (this can be newline and/or carriage return)
6698 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6699 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6702 // copy raw input line for later use (mainly debugging output)
6703 strcpy(line_raw, line);
6705 if (read_continued_line)
6707 // append new line to existing line, if there is enough space
6708 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6709 strcat(previous_line, line_ptr);
6711 strcpy(line, previous_line); // copy storage buffer to line
6713 read_continued_line = FALSE;
6716 // if the last character is '\', continue at next line
6717 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6719 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6720 strcpy(previous_line, line); // copy line to storage buffer
6722 read_continued_line = TRUE;
6728 if (line[0] == '\0')
6731 // extract comment text from comment line
6734 for (line_ptr = line; *line_ptr; line_ptr++)
6735 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6738 strcpy(last_comment, line_ptr);
6743 // extract level title text from line containing level title
6744 if (line[0] == '\'')
6746 strcpy(level_name, &line[1]);
6748 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6749 level_name[strlen(level_name) - 1] = '\0';
6754 // skip lines containing only spaces (or empty lines)
6755 for (line_ptr = line; *line_ptr; line_ptr++)
6756 if (*line_ptr != ' ')
6758 if (*line_ptr == '\0')
6761 // at this point, we have found a line containing part of a playfield
6763 got_valid_playfield_line = TRUE;
6765 if (!reading_playfield)
6767 reading_playfield = TRUE;
6768 invalid_playfield_char = FALSE;
6770 for (x = 0; x < MAX_LEV_FIELDX; x++)
6771 for (y = 0; y < MAX_LEV_FIELDY; y++)
6772 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6777 // start with topmost tile row
6781 // skip playfield line if larger row than allowed
6782 if (y >= MAX_LEV_FIELDY)
6785 // start with leftmost tile column
6788 // read playfield elements from line
6789 for (line_ptr = line; *line_ptr; line_ptr++)
6791 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6793 // stop parsing playfield line if larger column than allowed
6794 if (x >= MAX_LEV_FIELDX)
6797 if (mapped_sb_element == EL_UNDEFINED)
6799 invalid_playfield_char = TRUE;
6804 level->field[x][y] = mapped_sb_element;
6806 // continue with next tile column
6809 level->fieldx = MAX(x, level->fieldx);
6812 if (invalid_playfield_char)
6814 // if first playfield line, treat invalid lines as comment lines
6816 reading_playfield = FALSE;
6821 // continue with next tile row
6829 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6830 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6832 if (!reading_playfield)
6834 level->no_valid_file = TRUE;
6836 Warn("cannot read level '%s' -- using empty level", filename);
6841 if (*level_name != '\0')
6843 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6844 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6846 else if (*last_comment != '\0')
6848 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6849 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6853 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6856 // set all empty fields beyond the border walls to invisible steel wall
6857 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6859 if ((x == 0 || x == level->fieldx - 1 ||
6860 y == 0 || y == level->fieldy - 1) &&
6861 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6862 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6863 level->field, level->fieldx, level->fieldy);
6866 // set special level settings for Sokoban levels
6867 SetLevelSettings_SB(level);
6869 if (load_xsb_to_ces)
6871 // special global settings can now be set in level template
6872 level->use_custom_template = TRUE;
6877 // -------------------------------------------------------------------------
6878 // functions for handling native levels
6879 // -------------------------------------------------------------------------
6881 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6882 struct LevelFileInfo *level_file_info,
6883 boolean level_info_only)
6887 // determine position of requested level inside level package
6888 if (level_file_info->packed)
6889 pos = level_file_info->nr - leveldir_current->first_level;
6891 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6892 level->no_valid_file = TRUE;
6895 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6896 struct LevelFileInfo *level_file_info,
6897 boolean level_info_only)
6899 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6900 level->no_valid_file = TRUE;
6903 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6904 struct LevelFileInfo *level_file_info,
6905 boolean level_info_only)
6909 // determine position of requested level inside level package
6910 if (level_file_info->packed)
6911 pos = level_file_info->nr - leveldir_current->first_level;
6913 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6914 level->no_valid_file = TRUE;
6917 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6918 struct LevelFileInfo *level_file_info,
6919 boolean level_info_only)
6921 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6922 level->no_valid_file = TRUE;
6925 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6927 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6928 CopyNativeLevel_RND_to_BD(level);
6929 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6930 CopyNativeLevel_RND_to_EM(level);
6931 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6932 CopyNativeLevel_RND_to_SP(level);
6933 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6934 CopyNativeLevel_RND_to_MM(level);
6937 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6939 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6940 CopyNativeLevel_BD_to_RND(level);
6941 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6942 CopyNativeLevel_EM_to_RND(level);
6943 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6944 CopyNativeLevel_SP_to_RND(level);
6945 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6946 CopyNativeLevel_MM_to_RND(level);
6949 void SaveNativeLevel(struct LevelInfo *level)
6951 // saving native level files only supported for some game engines
6952 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6953 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6956 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6957 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6958 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6959 char *filename = getLevelFilenameFromBasename(basename);
6961 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6964 boolean success = FALSE;
6966 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6968 CopyNativeLevel_RND_to_BD(level);
6969 // CopyNativeTape_RND_to_BD(level);
6971 success = SaveNativeLevel_BD(filename);
6973 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6975 CopyNativeLevel_RND_to_SP(level);
6976 CopyNativeTape_RND_to_SP(level);
6978 success = SaveNativeLevel_SP(filename);
6982 Request("Native level file saved!", REQ_CONFIRM);
6984 Request("Failed to save native level file!", REQ_CONFIRM);
6988 // ----------------------------------------------------------------------------
6989 // functions for loading generic level
6990 // ----------------------------------------------------------------------------
6992 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6993 struct LevelFileInfo *level_file_info,
6994 boolean level_info_only)
6996 // always start with reliable default values
6997 setLevelInfoToDefaults(level, level_info_only, TRUE);
6999 switch (level_file_info->type)
7001 case LEVEL_FILE_TYPE_RND:
7002 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7005 case LEVEL_FILE_TYPE_BD:
7006 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7007 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7010 case LEVEL_FILE_TYPE_EM:
7011 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7012 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7015 case LEVEL_FILE_TYPE_SP:
7016 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7017 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7020 case LEVEL_FILE_TYPE_MM:
7021 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7022 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7025 case LEVEL_FILE_TYPE_DC:
7026 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7029 case LEVEL_FILE_TYPE_SB:
7030 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7034 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7038 // if level file is invalid, restore level structure to default values
7039 if (level->no_valid_file)
7040 setLevelInfoToDefaults(level, level_info_only, FALSE);
7042 if (check_special_flags("use_native_bd_game_engine"))
7043 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7045 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7046 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7048 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7049 CopyNativeLevel_Native_to_RND(level);
7052 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7054 static struct LevelFileInfo level_file_info;
7056 // always start with reliable default values
7057 setFileInfoToDefaults(&level_file_info);
7059 level_file_info.nr = 0; // unknown level number
7060 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7062 setString(&level_file_info.filename, filename);
7064 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7067 static void LoadLevel_InitVersion(struct LevelInfo *level)
7071 if (leveldir_current == NULL) // only when dumping level
7074 // all engine modifications also valid for levels which use latest engine
7075 if (level->game_version < VERSION_IDENT(3,2,0,5))
7077 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7078 level->time_score_base = 10;
7081 if (leveldir_current->latest_engine)
7083 // ---------- use latest game engine --------------------------------------
7085 /* For all levels which are forced to use the latest game engine version
7086 (normally all but user contributed, private and undefined levels), set
7087 the game engine version to the actual version; this allows for actual
7088 corrections in the game engine to take effect for existing, converted
7089 levels (from "classic" or other existing games) to make the emulation
7090 of the corresponding game more accurate, while (hopefully) not breaking
7091 existing levels created from other players. */
7093 level->game_version = GAME_VERSION_ACTUAL;
7095 /* Set special EM style gems behaviour: EM style gems slip down from
7096 normal, steel and growing wall. As this is a more fundamental change,
7097 it seems better to set the default behaviour to "off" (as it is more
7098 natural) and make it configurable in the level editor (as a property
7099 of gem style elements). Already existing converted levels (neither
7100 private nor contributed levels) are changed to the new behaviour. */
7102 if (level->file_version < FILE_VERSION_2_0)
7103 level->em_slippery_gems = TRUE;
7108 // ---------- use game engine the level was created with --------------------
7110 /* For all levels which are not forced to use the latest game engine
7111 version (normally user contributed, private and undefined levels),
7112 use the version of the game engine the levels were created for.
7114 Since 2.0.1, the game engine version is now directly stored
7115 in the level file (chunk "VERS"), so there is no need anymore
7116 to set the game version from the file version (except for old,
7117 pre-2.0 levels, where the game version is still taken from the
7118 file format version used to store the level -- see above). */
7120 // player was faster than enemies in 1.0.0 and before
7121 if (level->file_version == FILE_VERSION_1_0)
7122 for (i = 0; i < MAX_PLAYERS; i++)
7123 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7125 // default behaviour for EM style gems was "slippery" only in 2.0.1
7126 if (level->game_version == VERSION_IDENT(2,0,1,0))
7127 level->em_slippery_gems = TRUE;
7129 // springs could be pushed over pits before (pre-release version) 2.2.0
7130 if (level->game_version < VERSION_IDENT(2,2,0,0))
7131 level->use_spring_bug = TRUE;
7133 if (level->game_version < VERSION_IDENT(3,2,0,5))
7135 // time orb caused limited time in endless time levels before 3.2.0-5
7136 level->use_time_orb_bug = TRUE;
7138 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7139 level->block_snap_field = FALSE;
7141 // extra time score was same value as time left score before 3.2.0-5
7142 level->extra_time_score = level->score[SC_TIME_BONUS];
7145 if (level->game_version < VERSION_IDENT(3,2,0,7))
7147 // default behaviour for snapping was "not continuous" before 3.2.0-7
7148 level->continuous_snapping = FALSE;
7151 // only few elements were able to actively move into acid before 3.1.0
7152 // trigger settings did not exist before 3.1.0; set to default "any"
7153 if (level->game_version < VERSION_IDENT(3,1,0,0))
7155 // correct "can move into acid" settings (all zero in old levels)
7157 level->can_move_into_acid_bits = 0; // nothing can move into acid
7158 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7160 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7161 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7162 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7163 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7165 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7166 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7168 // correct trigger settings (stored as zero == "none" in old levels)
7170 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7172 int element = EL_CUSTOM_START + i;
7173 struct ElementInfo *ei = &element_info[element];
7175 for (j = 0; j < ei->num_change_pages; j++)
7177 struct ElementChangeInfo *change = &ei->change_page[j];
7179 change->trigger_player = CH_PLAYER_ANY;
7180 change->trigger_page = CH_PAGE_ANY;
7185 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7187 int element = EL_CUSTOM_256;
7188 struct ElementInfo *ei = &element_info[element];
7189 struct ElementChangeInfo *change = &ei->change_page[0];
7191 /* This is needed to fix a problem that was caused by a bugfix in function
7192 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7193 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7194 not replace walkable elements, but instead just placed the player on it,
7195 without placing the Sokoban field under the player). Unfortunately, this
7196 breaks "Snake Bite" style levels when the snake is halfway through a door
7197 that just closes (the snake head is still alive and can be moved in this
7198 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7199 player (without Sokoban element) which then gets killed as designed). */
7201 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7202 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7203 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7204 change->target_element = EL_PLAYER_1;
7207 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7208 if (level->game_version < VERSION_IDENT(3,2,5,0))
7210 /* This is needed to fix a problem that was caused by a bugfix in function
7211 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7212 corrects the behaviour when a custom element changes to another custom
7213 element with a higher element number that has change actions defined.
7214 Normally, only one change per frame is allowed for custom elements.
7215 Therefore, it is checked if a custom element already changed in the
7216 current frame; if it did, subsequent changes are suppressed.
7217 Unfortunately, this is only checked for element changes, but not for
7218 change actions, which are still executed. As the function above loops
7219 through all custom elements from lower to higher, an element change
7220 resulting in a lower CE number won't be checked again, while a target
7221 element with a higher number will also be checked, and potential change
7222 actions will get executed for this CE, too (which is wrong), while
7223 further changes are ignored (which is correct). As this bugfix breaks
7224 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7225 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7226 behaviour for existing levels and tapes that make use of this bug */
7228 level->use_action_after_change_bug = TRUE;
7231 // not centering level after relocating player was default only in 3.2.3
7232 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7233 level->shifted_relocation = TRUE;
7235 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7236 if (level->game_version < VERSION_IDENT(3,2,6,0))
7237 level->em_explodes_by_fire = TRUE;
7239 // levels were solved by the first player entering an exit up to 4.1.0.0
7240 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7241 level->solved_by_one_player = TRUE;
7243 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7244 if (level->game_version < VERSION_IDENT(4,1,1,1))
7245 level->use_life_bugs = TRUE;
7247 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7248 if (level->game_version < VERSION_IDENT(4,1,1,1))
7249 level->sb_objects_needed = FALSE;
7251 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7252 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7253 level->finish_dig_collect = FALSE;
7255 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7256 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7257 level->keep_walkable_ce = TRUE;
7260 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7262 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7265 // check if this level is (not) a Sokoban level
7266 for (y = 0; y < level->fieldy; y++)
7267 for (x = 0; x < level->fieldx; x++)
7268 if (!IS_SB_ELEMENT(Tile[x][y]))
7269 is_sokoban_level = FALSE;
7271 if (is_sokoban_level)
7273 // set special level settings for Sokoban levels
7274 SetLevelSettings_SB(level);
7278 static void LoadLevel_InitSettings(struct LevelInfo *level)
7280 // adjust level settings for (non-native) Sokoban-style levels
7281 LoadLevel_InitSettings_SB(level);
7283 // rename levels with title "nameless level" or if renaming is forced
7284 if (leveldir_current->empty_level_name != NULL &&
7285 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7286 leveldir_current->force_level_name))
7287 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7288 leveldir_current->empty_level_name, level_nr);
7291 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7295 // map elements that have changed in newer versions
7296 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7297 level->game_version);
7298 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7299 for (x = 0; x < 3; x++)
7300 for (y = 0; y < 3; y++)
7301 level->yamyam_content[i].e[x][y] =
7302 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7303 level->game_version);
7307 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7311 // map custom element change events that have changed in newer versions
7312 // (these following values were accidentally changed in version 3.0.1)
7313 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7314 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7316 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7318 int element = EL_CUSTOM_START + i;
7320 // order of checking and copying events to be mapped is important
7321 // (do not change the start and end value -- they are constant)
7322 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7324 if (HAS_CHANGE_EVENT(element, j - 2))
7326 SET_CHANGE_EVENT(element, j - 2, FALSE);
7327 SET_CHANGE_EVENT(element, j, TRUE);
7331 // order of checking and copying events to be mapped is important
7332 // (do not change the start and end value -- they are constant)
7333 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7335 if (HAS_CHANGE_EVENT(element, j - 1))
7337 SET_CHANGE_EVENT(element, j - 1, FALSE);
7338 SET_CHANGE_EVENT(element, j, TRUE);
7344 // initialize "can_change" field for old levels with only one change page
7345 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7347 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7349 int element = EL_CUSTOM_START + i;
7351 if (CAN_CHANGE(element))
7352 element_info[element].change->can_change = TRUE;
7356 // correct custom element values (for old levels without these options)
7357 if (level->game_version < VERSION_IDENT(3,1,1,0))
7359 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7361 int element = EL_CUSTOM_START + i;
7362 struct ElementInfo *ei = &element_info[element];
7364 if (ei->access_direction == MV_NO_DIRECTION)
7365 ei->access_direction = MV_ALL_DIRECTIONS;
7369 // correct custom element values (fix invalid values for all versions)
7372 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7374 int element = EL_CUSTOM_START + i;
7375 struct ElementInfo *ei = &element_info[element];
7377 for (j = 0; j < ei->num_change_pages; j++)
7379 struct ElementChangeInfo *change = &ei->change_page[j];
7381 if (change->trigger_player == CH_PLAYER_NONE)
7382 change->trigger_player = CH_PLAYER_ANY;
7384 if (change->trigger_side == CH_SIDE_NONE)
7385 change->trigger_side = CH_SIDE_ANY;
7390 // initialize "can_explode" field for old levels which did not store this
7391 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7392 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7394 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7396 int element = EL_CUSTOM_START + i;
7398 if (EXPLODES_1X1_OLD(element))
7399 element_info[element].explosion_type = EXPLODES_1X1;
7401 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7402 EXPLODES_SMASHED(element) ||
7403 EXPLODES_IMPACT(element)));
7407 // correct previously hard-coded move delay values for maze runner style
7408 if (level->game_version < VERSION_IDENT(3,1,1,0))
7410 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7412 int element = EL_CUSTOM_START + i;
7414 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7416 // previously hard-coded and therefore ignored
7417 element_info[element].move_delay_fixed = 9;
7418 element_info[element].move_delay_random = 0;
7423 // set some other uninitialized values of custom elements in older levels
7424 if (level->game_version < VERSION_IDENT(3,1,0,0))
7426 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7428 int element = EL_CUSTOM_START + i;
7430 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7432 element_info[element].explosion_delay = 17;
7433 element_info[element].ignition_delay = 8;
7437 // set mouse click change events to work for left/middle/right mouse button
7438 if (level->game_version < VERSION_IDENT(4,2,3,0))
7440 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7442 int element = EL_CUSTOM_START + i;
7443 struct ElementInfo *ei = &element_info[element];
7445 for (j = 0; j < ei->num_change_pages; j++)
7447 struct ElementChangeInfo *change = &ei->change_page[j];
7449 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7450 change->has_event[CE_PRESSED_BY_MOUSE] ||
7451 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7452 change->has_event[CE_MOUSE_PRESSED_ON_X])
7453 change->trigger_side = CH_SIDE_ANY;
7459 static void LoadLevel_InitElements(struct LevelInfo *level)
7461 LoadLevel_InitStandardElements(level);
7463 if (level->file_has_custom_elements)
7464 LoadLevel_InitCustomElements(level);
7466 // initialize element properties for level editor etc.
7467 InitElementPropertiesEngine(level->game_version);
7468 InitElementPropertiesGfxElement();
7471 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7475 // map elements that have changed in newer versions
7476 for (y = 0; y < level->fieldy; y++)
7477 for (x = 0; x < level->fieldx; x++)
7478 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7479 level->game_version);
7481 // clear unused playfield data (nicer if level gets resized in editor)
7482 for (x = 0; x < MAX_LEV_FIELDX; x++)
7483 for (y = 0; y < MAX_LEV_FIELDY; y++)
7484 if (x >= level->fieldx || y >= level->fieldy)
7485 level->field[x][y] = EL_EMPTY;
7487 // copy elements to runtime playfield array
7488 for (x = 0; x < MAX_LEV_FIELDX; x++)
7489 for (y = 0; y < MAX_LEV_FIELDY; y++)
7490 Tile[x][y] = level->field[x][y];
7492 // initialize level size variables for faster access
7493 lev_fieldx = level->fieldx;
7494 lev_fieldy = level->fieldy;
7496 // determine border element for this level
7497 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7498 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7503 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7505 struct LevelFileInfo *level_file_info = &level->file_info;
7507 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7508 CopyNativeLevel_RND_to_Native(level);
7511 static void LoadLevelTemplate_LoadAndInit(void)
7513 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7515 LoadLevel_InitVersion(&level_template);
7516 LoadLevel_InitElements(&level_template);
7517 LoadLevel_InitSettings(&level_template);
7519 ActivateLevelTemplate();
7522 void LoadLevelTemplate(int nr)
7524 if (!fileExists(getGlobalLevelTemplateFilename()))
7526 Warn("no level template found for this level");
7531 setLevelFileInfo(&level_template.file_info, nr);
7533 LoadLevelTemplate_LoadAndInit();
7536 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7538 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7540 LoadLevelTemplate_LoadAndInit();
7543 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7545 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7547 if (level.use_custom_template)
7549 if (network_level != NULL)
7550 LoadNetworkLevelTemplate(network_level);
7552 LoadLevelTemplate(-1);
7555 LoadLevel_InitVersion(&level);
7556 LoadLevel_InitElements(&level);
7557 LoadLevel_InitPlayfield(&level);
7558 LoadLevel_InitSettings(&level);
7560 LoadLevel_InitNativeEngines(&level);
7563 void LoadLevel(int nr)
7565 SetLevelSetInfo(leveldir_current->identifier, nr);
7567 setLevelFileInfo(&level.file_info, nr);
7569 LoadLevel_LoadAndInit(NULL);
7572 void LoadLevelInfoOnly(int nr)
7574 setLevelFileInfo(&level.file_info, nr);
7576 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7579 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7581 SetLevelSetInfo(network_level->leveldir_identifier,
7582 network_level->file_info.nr);
7584 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7586 LoadLevel_LoadAndInit(network_level);
7589 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7593 chunk_size += putFileVersion(file, level->file_version);
7594 chunk_size += putFileVersion(file, level->game_version);
7599 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7603 chunk_size += putFile16BitBE(file, level->creation_date.year);
7604 chunk_size += putFile8Bit(file, level->creation_date.month);
7605 chunk_size += putFile8Bit(file, level->creation_date.day);
7610 #if ENABLE_HISTORIC_CHUNKS
7611 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7615 putFile8Bit(file, level->fieldx);
7616 putFile8Bit(file, level->fieldy);
7618 putFile16BitBE(file, level->time);
7619 putFile16BitBE(file, level->gems_needed);
7621 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7622 putFile8Bit(file, level->name[i]);
7624 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7625 putFile8Bit(file, level->score[i]);
7627 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7628 for (y = 0; y < 3; y++)
7629 for (x = 0; x < 3; x++)
7630 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7631 level->yamyam_content[i].e[x][y]));
7632 putFile8Bit(file, level->amoeba_speed);
7633 putFile8Bit(file, level->time_magic_wall);
7634 putFile8Bit(file, level->time_wheel);
7635 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7636 level->amoeba_content));
7637 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7638 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7639 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7640 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7642 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7644 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7645 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7646 putFile32BitBE(file, level->can_move_into_acid_bits);
7647 putFile8Bit(file, level->dont_collide_with_bits);
7649 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7650 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7652 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7653 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7654 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7656 putFile8Bit(file, level->game_engine_type);
7658 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7662 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7667 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7668 chunk_size += putFile8Bit(file, level->name[i]);
7673 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7678 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7679 chunk_size += putFile8Bit(file, level->author[i]);
7684 #if ENABLE_HISTORIC_CHUNKS
7685 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7690 for (y = 0; y < level->fieldy; y++)
7691 for (x = 0; x < level->fieldx; x++)
7692 if (level->encoding_16bit_field)
7693 chunk_size += putFile16BitBE(file, level->field[x][y]);
7695 chunk_size += putFile8Bit(file, level->field[x][y]);
7701 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7706 for (y = 0; y < level->fieldy; y++)
7707 for (x = 0; x < level->fieldx; x++)
7708 chunk_size += putFile16BitBE(file, level->field[x][y]);
7713 #if ENABLE_HISTORIC_CHUNKS
7714 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7718 putFile8Bit(file, EL_YAMYAM);
7719 putFile8Bit(file, level->num_yamyam_contents);
7720 putFile8Bit(file, 0);
7721 putFile8Bit(file, 0);
7723 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7724 for (y = 0; y < 3; y++)
7725 for (x = 0; x < 3; x++)
7726 if (level->encoding_16bit_field)
7727 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7729 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7733 #if ENABLE_HISTORIC_CHUNKS
7734 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7737 int num_contents, content_xsize, content_ysize;
7738 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7740 if (element == EL_YAMYAM)
7742 num_contents = level->num_yamyam_contents;
7746 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7747 for (y = 0; y < 3; y++)
7748 for (x = 0; x < 3; x++)
7749 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7751 else if (element == EL_BD_AMOEBA)
7757 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7758 for (y = 0; y < 3; y++)
7759 for (x = 0; x < 3; x++)
7760 content_array[i][x][y] = EL_EMPTY;
7761 content_array[0][0][0] = level->amoeba_content;
7765 // chunk header already written -- write empty chunk data
7766 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7768 Warn("cannot save content for element '%d'", element);
7773 putFile16BitBE(file, element);
7774 putFile8Bit(file, num_contents);
7775 putFile8Bit(file, content_xsize);
7776 putFile8Bit(file, content_ysize);
7778 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7780 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7781 for (y = 0; y < 3; y++)
7782 for (x = 0; x < 3; x++)
7783 putFile16BitBE(file, content_array[i][x][y]);
7787 #if ENABLE_HISTORIC_CHUNKS
7788 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7790 int envelope_nr = element - EL_ENVELOPE_1;
7791 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7795 chunk_size += putFile16BitBE(file, element);
7796 chunk_size += putFile16BitBE(file, envelope_len);
7797 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7798 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7800 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7801 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7803 for (i = 0; i < envelope_len; i++)
7804 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7810 #if ENABLE_HISTORIC_CHUNKS
7811 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7812 int num_changed_custom_elements)
7816 putFile16BitBE(file, num_changed_custom_elements);
7818 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7820 int element = EL_CUSTOM_START + i;
7822 struct ElementInfo *ei = &element_info[element];
7824 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7826 if (check < num_changed_custom_elements)
7828 putFile16BitBE(file, element);
7829 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7836 if (check != num_changed_custom_elements) // should not happen
7837 Warn("inconsistent number of custom element properties");
7841 #if ENABLE_HISTORIC_CHUNKS
7842 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7843 int num_changed_custom_elements)
7847 putFile16BitBE(file, num_changed_custom_elements);
7849 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7851 int element = EL_CUSTOM_START + i;
7853 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7855 if (check < num_changed_custom_elements)
7857 putFile16BitBE(file, element);
7858 putFile16BitBE(file, element_info[element].change->target_element);
7865 if (check != num_changed_custom_elements) // should not happen
7866 Warn("inconsistent number of custom target elements");
7870 #if ENABLE_HISTORIC_CHUNKS
7871 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7872 int num_changed_custom_elements)
7874 int i, j, x, y, check = 0;
7876 putFile16BitBE(file, num_changed_custom_elements);
7878 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7880 int element = EL_CUSTOM_START + i;
7881 struct ElementInfo *ei = &element_info[element];
7883 if (ei->modified_settings)
7885 if (check < num_changed_custom_elements)
7887 putFile16BitBE(file, element);
7889 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7890 putFile8Bit(file, ei->description[j]);
7892 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7894 // some free bytes for future properties and padding
7895 WriteUnusedBytesToFile(file, 7);
7897 putFile8Bit(file, ei->use_gfx_element);
7898 putFile16BitBE(file, ei->gfx_element_initial);
7900 putFile8Bit(file, ei->collect_score_initial);
7901 putFile8Bit(file, ei->collect_count_initial);
7903 putFile16BitBE(file, ei->push_delay_fixed);
7904 putFile16BitBE(file, ei->push_delay_random);
7905 putFile16BitBE(file, ei->move_delay_fixed);
7906 putFile16BitBE(file, ei->move_delay_random);
7908 putFile16BitBE(file, ei->move_pattern);
7909 putFile8Bit(file, ei->move_direction_initial);
7910 putFile8Bit(file, ei->move_stepsize);
7912 for (y = 0; y < 3; y++)
7913 for (x = 0; x < 3; x++)
7914 putFile16BitBE(file, ei->content.e[x][y]);
7916 putFile32BitBE(file, ei->change->events);
7918 putFile16BitBE(file, ei->change->target_element);
7920 putFile16BitBE(file, ei->change->delay_fixed);
7921 putFile16BitBE(file, ei->change->delay_random);
7922 putFile16BitBE(file, ei->change->delay_frames);
7924 putFile16BitBE(file, ei->change->initial_trigger_element);
7926 putFile8Bit(file, ei->change->explode);
7927 putFile8Bit(file, ei->change->use_target_content);
7928 putFile8Bit(file, ei->change->only_if_complete);
7929 putFile8Bit(file, ei->change->use_random_replace);
7931 putFile8Bit(file, ei->change->random_percentage);
7932 putFile8Bit(file, ei->change->replace_when);
7934 for (y = 0; y < 3; y++)
7935 for (x = 0; x < 3; x++)
7936 putFile16BitBE(file, ei->change->content.e[x][y]);
7938 putFile8Bit(file, ei->slippery_type);
7940 // some free bytes for future properties and padding
7941 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7948 if (check != num_changed_custom_elements) // should not happen
7949 Warn("inconsistent number of custom element properties");
7953 #if ENABLE_HISTORIC_CHUNKS
7954 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7956 struct ElementInfo *ei = &element_info[element];
7959 // ---------- custom element base property values (96 bytes) ----------------
7961 putFile16BitBE(file, element);
7963 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7964 putFile8Bit(file, ei->description[i]);
7966 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7968 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7970 putFile8Bit(file, ei->num_change_pages);
7972 putFile16BitBE(file, ei->ce_value_fixed_initial);
7973 putFile16BitBE(file, ei->ce_value_random_initial);
7974 putFile8Bit(file, ei->use_last_ce_value);
7976 putFile8Bit(file, ei->use_gfx_element);
7977 putFile16BitBE(file, ei->gfx_element_initial);
7979 putFile8Bit(file, ei->collect_score_initial);
7980 putFile8Bit(file, ei->collect_count_initial);
7982 putFile8Bit(file, ei->drop_delay_fixed);
7983 putFile8Bit(file, ei->push_delay_fixed);
7984 putFile8Bit(file, ei->drop_delay_random);
7985 putFile8Bit(file, ei->push_delay_random);
7986 putFile16BitBE(file, ei->move_delay_fixed);
7987 putFile16BitBE(file, ei->move_delay_random);
7989 // bits 0 - 15 of "move_pattern" ...
7990 putFile16BitBE(file, ei->move_pattern & 0xffff);
7991 putFile8Bit(file, ei->move_direction_initial);
7992 putFile8Bit(file, ei->move_stepsize);
7994 putFile8Bit(file, ei->slippery_type);
7996 for (y = 0; y < 3; y++)
7997 for (x = 0; x < 3; x++)
7998 putFile16BitBE(file, ei->content.e[x][y]);
8000 putFile16BitBE(file, ei->move_enter_element);
8001 putFile16BitBE(file, ei->move_leave_element);
8002 putFile8Bit(file, ei->move_leave_type);
8004 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8005 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8007 putFile8Bit(file, ei->access_direction);
8009 putFile8Bit(file, ei->explosion_delay);
8010 putFile8Bit(file, ei->ignition_delay);
8011 putFile8Bit(file, ei->explosion_type);
8013 // some free bytes for future custom property values and padding
8014 WriteUnusedBytesToFile(file, 1);
8016 // ---------- change page property values (48 bytes) ------------------------
8018 for (i = 0; i < ei->num_change_pages; i++)
8020 struct ElementChangeInfo *change = &ei->change_page[i];
8021 unsigned int event_bits;
8023 // bits 0 - 31 of "has_event[]" ...
8025 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8026 if (change->has_event[j])
8027 event_bits |= (1u << j);
8028 putFile32BitBE(file, event_bits);
8030 putFile16BitBE(file, change->target_element);
8032 putFile16BitBE(file, change->delay_fixed);
8033 putFile16BitBE(file, change->delay_random);
8034 putFile16BitBE(file, change->delay_frames);
8036 putFile16BitBE(file, change->initial_trigger_element);
8038 putFile8Bit(file, change->explode);
8039 putFile8Bit(file, change->use_target_content);
8040 putFile8Bit(file, change->only_if_complete);
8041 putFile8Bit(file, change->use_random_replace);
8043 putFile8Bit(file, change->random_percentage);
8044 putFile8Bit(file, change->replace_when);
8046 for (y = 0; y < 3; y++)
8047 for (x = 0; x < 3; x++)
8048 putFile16BitBE(file, change->target_content.e[x][y]);
8050 putFile8Bit(file, change->can_change);
8052 putFile8Bit(file, change->trigger_side);
8054 putFile8Bit(file, change->trigger_player);
8055 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8056 log_2(change->trigger_page)));
8058 putFile8Bit(file, change->has_action);
8059 putFile8Bit(file, change->action_type);
8060 putFile8Bit(file, change->action_mode);
8061 putFile16BitBE(file, change->action_arg);
8063 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8065 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8066 if (change->has_event[j])
8067 event_bits |= (1u << (j - 32));
8068 putFile8Bit(file, event_bits);
8073 #if ENABLE_HISTORIC_CHUNKS
8074 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8076 struct ElementInfo *ei = &element_info[element];
8077 struct ElementGroupInfo *group = ei->group;
8080 putFile16BitBE(file, element);
8082 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8083 putFile8Bit(file, ei->description[i]);
8085 putFile8Bit(file, group->num_elements);
8087 putFile8Bit(file, ei->use_gfx_element);
8088 putFile16BitBE(file, ei->gfx_element_initial);
8090 putFile8Bit(file, group->choice_mode);
8092 // some free bytes for future values and padding
8093 WriteUnusedBytesToFile(file, 3);
8095 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8096 putFile16BitBE(file, group->element[i]);
8100 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8101 boolean write_element)
8103 int save_type = entry->save_type;
8104 int data_type = entry->data_type;
8105 int conf_type = entry->conf_type;
8106 int byte_mask = conf_type & CONF_MASK_BYTES;
8107 int element = entry->element;
8108 int default_value = entry->default_value;
8110 boolean modified = FALSE;
8112 if (byte_mask != CONF_MASK_MULTI_BYTES)
8114 void *value_ptr = entry->value;
8115 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8118 // check if any settings have been modified before saving them
8119 if (value != default_value)
8122 // do not save if explicitly told or if unmodified default settings
8123 if ((save_type == SAVE_CONF_NEVER) ||
8124 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8128 num_bytes += putFile16BitBE(file, element);
8130 num_bytes += putFile8Bit(file, conf_type);
8131 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8132 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8133 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8136 else if (data_type == TYPE_STRING)
8138 char *default_string = entry->default_string;
8139 char *string = (char *)(entry->value);
8140 int string_length = strlen(string);
8143 // check if any settings have been modified before saving them
8144 if (!strEqual(string, default_string))
8147 // do not save if explicitly told or if unmodified default settings
8148 if ((save_type == SAVE_CONF_NEVER) ||
8149 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8153 num_bytes += putFile16BitBE(file, element);
8155 num_bytes += putFile8Bit(file, conf_type);
8156 num_bytes += putFile16BitBE(file, string_length);
8158 for (i = 0; i < string_length; i++)
8159 num_bytes += putFile8Bit(file, string[i]);
8161 else if (data_type == TYPE_ELEMENT_LIST)
8163 int *element_array = (int *)(entry->value);
8164 int num_elements = *(int *)(entry->num_entities);
8167 // check if any settings have been modified before saving them
8168 for (i = 0; i < num_elements; i++)
8169 if (element_array[i] != default_value)
8172 // do not save if explicitly told or if unmodified default settings
8173 if ((save_type == SAVE_CONF_NEVER) ||
8174 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8178 num_bytes += putFile16BitBE(file, element);
8180 num_bytes += putFile8Bit(file, conf_type);
8181 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8183 for (i = 0; i < num_elements; i++)
8184 num_bytes += putFile16BitBE(file, element_array[i]);
8186 else if (data_type == TYPE_CONTENT_LIST)
8188 struct Content *content = (struct Content *)(entry->value);
8189 int num_contents = *(int *)(entry->num_entities);
8192 // check if any settings have been modified before saving them
8193 for (i = 0; i < num_contents; i++)
8194 for (y = 0; y < 3; y++)
8195 for (x = 0; x < 3; x++)
8196 if (content[i].e[x][y] != default_value)
8199 // do not save if explicitly told or if unmodified default settings
8200 if ((save_type == SAVE_CONF_NEVER) ||
8201 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8205 num_bytes += putFile16BitBE(file, element);
8207 num_bytes += putFile8Bit(file, conf_type);
8208 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8210 for (i = 0; i < num_contents; i++)
8211 for (y = 0; y < 3; y++)
8212 for (x = 0; x < 3; x++)
8213 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8219 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8224 li = *level; // copy level data into temporary buffer
8226 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8227 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8232 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8237 li = *level; // copy level data into temporary buffer
8239 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8240 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8245 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8247 int envelope_nr = element - EL_ENVELOPE_1;
8251 chunk_size += putFile16BitBE(file, element);
8253 // copy envelope data into temporary buffer
8254 xx_envelope = level->envelope[envelope_nr];
8256 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8257 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8262 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8264 struct ElementInfo *ei = &element_info[element];
8268 chunk_size += putFile16BitBE(file, element);
8270 xx_ei = *ei; // copy element data into temporary buffer
8272 // set default description string for this specific element
8273 strcpy(xx_default_description, getDefaultElementDescription(ei));
8275 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8276 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8278 for (i = 0; i < ei->num_change_pages; i++)
8280 struct ElementChangeInfo *change = &ei->change_page[i];
8282 xx_current_change_page = i;
8284 xx_change = *change; // copy change data into temporary buffer
8287 setEventBitsFromEventFlags(change);
8289 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8290 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8297 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8299 struct ElementInfo *ei = &element_info[element];
8300 struct ElementGroupInfo *group = ei->group;
8304 chunk_size += putFile16BitBE(file, element);
8306 xx_ei = *ei; // copy element data into temporary buffer
8307 xx_group = *group; // copy group data into temporary buffer
8309 // set default description string for this specific element
8310 strcpy(xx_default_description, getDefaultElementDescription(ei));
8312 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8313 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8318 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8320 struct ElementInfo *ei = &element_info[element];
8324 chunk_size += putFile16BitBE(file, element);
8326 xx_ei = *ei; // copy element data into temporary buffer
8328 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8329 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8334 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8335 boolean save_as_template)
8341 if (!(file = fopen(filename, MODE_WRITE)))
8343 Warn("cannot save level file '%s'", filename);
8348 level->file_version = FILE_VERSION_ACTUAL;
8349 level->game_version = GAME_VERSION_ACTUAL;
8351 level->creation_date = getCurrentDate();
8353 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8354 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8356 chunk_size = SaveLevel_VERS(NULL, level);
8357 putFileChunkBE(file, "VERS", chunk_size);
8358 SaveLevel_VERS(file, level);
8360 chunk_size = SaveLevel_DATE(NULL, level);
8361 putFileChunkBE(file, "DATE", chunk_size);
8362 SaveLevel_DATE(file, level);
8364 chunk_size = SaveLevel_NAME(NULL, level);
8365 putFileChunkBE(file, "NAME", chunk_size);
8366 SaveLevel_NAME(file, level);
8368 chunk_size = SaveLevel_AUTH(NULL, level);
8369 putFileChunkBE(file, "AUTH", chunk_size);
8370 SaveLevel_AUTH(file, level);
8372 chunk_size = SaveLevel_INFO(NULL, level);
8373 putFileChunkBE(file, "INFO", chunk_size);
8374 SaveLevel_INFO(file, level);
8376 chunk_size = SaveLevel_BODY(NULL, level);
8377 putFileChunkBE(file, "BODY", chunk_size);
8378 SaveLevel_BODY(file, level);
8380 chunk_size = SaveLevel_ELEM(NULL, level);
8381 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8383 putFileChunkBE(file, "ELEM", chunk_size);
8384 SaveLevel_ELEM(file, level);
8387 for (i = 0; i < NUM_ENVELOPES; i++)
8389 int element = EL_ENVELOPE_1 + i;
8391 chunk_size = SaveLevel_NOTE(NULL, level, element);
8392 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8394 putFileChunkBE(file, "NOTE", chunk_size);
8395 SaveLevel_NOTE(file, level, element);
8399 // if not using template level, check for non-default custom/group elements
8400 if (!level->use_custom_template || save_as_template)
8402 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8404 int element = EL_CUSTOM_START + i;
8406 chunk_size = SaveLevel_CUSX(NULL, level, element);
8407 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8409 putFileChunkBE(file, "CUSX", chunk_size);
8410 SaveLevel_CUSX(file, level, element);
8414 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8416 int element = EL_GROUP_START + i;
8418 chunk_size = SaveLevel_GRPX(NULL, level, element);
8419 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8421 putFileChunkBE(file, "GRPX", chunk_size);
8422 SaveLevel_GRPX(file, level, element);
8426 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8428 int element = GET_EMPTY_ELEMENT(i);
8430 chunk_size = SaveLevel_EMPX(NULL, level, element);
8431 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8433 putFileChunkBE(file, "EMPX", chunk_size);
8434 SaveLevel_EMPX(file, level, element);
8441 SetFilePermissions(filename, PERMS_PRIVATE);
8444 void SaveLevel(int nr)
8446 char *filename = getDefaultLevelFilename(nr);
8448 SaveLevelFromFilename(&level, filename, FALSE);
8451 void SaveLevelTemplate(void)
8453 char *filename = getLocalLevelTemplateFilename();
8455 SaveLevelFromFilename(&level, filename, TRUE);
8458 boolean SaveLevelChecked(int nr)
8460 char *filename = getDefaultLevelFilename(nr);
8461 boolean new_level = !fileExists(filename);
8462 boolean level_saved = FALSE;
8464 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8469 Request("Level saved!", REQ_CONFIRM);
8477 void DumpLevel(struct LevelInfo *level)
8479 if (level->no_level_file || level->no_valid_file)
8481 Warn("cannot dump -- no valid level file found");
8487 Print("Level xxx (file version %08d, game version %08d)\n",
8488 level->file_version, level->game_version);
8491 Print("Level author: '%s'\n", level->author);
8492 Print("Level title: '%s'\n", level->name);
8494 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8496 Print("Level time: %d seconds\n", level->time);
8497 Print("Gems needed: %d\n", level->gems_needed);
8499 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8500 Print("Time for wheel: %d seconds\n", level->time_wheel);
8501 Print("Time for light: %d seconds\n", level->time_light);
8502 Print("Time for timegate: %d seconds\n", level->time_timegate);
8504 Print("Amoeba speed: %d\n", level->amoeba_speed);
8507 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8508 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8509 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8510 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8511 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8512 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8518 for (i = 0; i < NUM_ENVELOPES; i++)
8520 char *text = level->envelope[i].text;
8521 int text_len = strlen(text);
8522 boolean has_text = FALSE;
8524 for (j = 0; j < text_len; j++)
8525 if (text[j] != ' ' && text[j] != '\n')
8531 Print("Envelope %d:\n'%s'\n", i + 1, text);
8539 void DumpLevels(void)
8541 static LevelDirTree *dumplevel_leveldir = NULL;
8543 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8544 global.dumplevel_leveldir);
8546 if (dumplevel_leveldir == NULL)
8547 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8549 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8550 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8551 Fail("no such level number: %d", global.dumplevel_level_nr);
8553 leveldir_current = dumplevel_leveldir;
8555 LoadLevel(global.dumplevel_level_nr);
8562 // ============================================================================
8563 // tape file functions
8564 // ============================================================================
8566 static void setTapeInfoToDefaults(void)
8570 // always start with reliable default values (empty tape)
8573 // default values (also for pre-1.2 tapes) with only the first player
8574 tape.player_participates[0] = TRUE;
8575 for (i = 1; i < MAX_PLAYERS; i++)
8576 tape.player_participates[i] = FALSE;
8578 // at least one (default: the first) player participates in every tape
8579 tape.num_participating_players = 1;
8581 tape.property_bits = TAPE_PROPERTY_NONE;
8583 tape.level_nr = level_nr;
8585 tape.changed = FALSE;
8586 tape.solved = FALSE;
8588 tape.recording = FALSE;
8589 tape.playing = FALSE;
8590 tape.pausing = FALSE;
8592 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8593 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8595 tape.no_info_chunk = TRUE;
8596 tape.no_valid_file = FALSE;
8599 static int getTapePosSize(struct TapeInfo *tape)
8601 int tape_pos_size = 0;
8603 if (tape->use_key_actions)
8604 tape_pos_size += tape->num_participating_players;
8606 if (tape->use_mouse_actions)
8607 tape_pos_size += 3; // x and y position and mouse button mask
8609 tape_pos_size += 1; // tape action delay value
8611 return tape_pos_size;
8614 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8616 tape->use_key_actions = FALSE;
8617 tape->use_mouse_actions = FALSE;
8619 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8620 tape->use_key_actions = TRUE;
8622 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8623 tape->use_mouse_actions = TRUE;
8626 static int getTapeActionValue(struct TapeInfo *tape)
8628 return (tape->use_key_actions &&
8629 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8630 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8631 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8632 TAPE_ACTIONS_DEFAULT);
8635 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8637 tape->file_version = getFileVersion(file);
8638 tape->game_version = getFileVersion(file);
8643 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8647 tape->random_seed = getFile32BitBE(file);
8648 tape->date = getFile32BitBE(file);
8649 tape->length = getFile32BitBE(file);
8651 // read header fields that are new since version 1.2
8652 if (tape->file_version >= FILE_VERSION_1_2)
8654 byte store_participating_players = getFile8Bit(file);
8657 // since version 1.2, tapes store which players participate in the tape
8658 tape->num_participating_players = 0;
8659 for (i = 0; i < MAX_PLAYERS; i++)
8661 tape->player_participates[i] = FALSE;
8663 if (store_participating_players & (1 << i))
8665 tape->player_participates[i] = TRUE;
8666 tape->num_participating_players++;
8670 setTapeActionFlags(tape, getFile8Bit(file));
8672 tape->property_bits = getFile8Bit(file);
8673 tape->solved = getFile8Bit(file);
8675 engine_version = getFileVersion(file);
8676 if (engine_version > 0)
8677 tape->engine_version = engine_version;
8679 tape->engine_version = tape->game_version;
8685 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8687 tape->scr_fieldx = getFile8Bit(file);
8688 tape->scr_fieldy = getFile8Bit(file);
8693 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8695 char *level_identifier = NULL;
8696 int level_identifier_size;
8699 tape->no_info_chunk = FALSE;
8701 level_identifier_size = getFile16BitBE(file);
8703 level_identifier = checked_malloc(level_identifier_size);
8705 for (i = 0; i < level_identifier_size; i++)
8706 level_identifier[i] = getFile8Bit(file);
8708 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8709 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8711 checked_free(level_identifier);
8713 tape->level_nr = getFile16BitBE(file);
8715 chunk_size = 2 + level_identifier_size + 2;
8720 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8723 int tape_pos_size = getTapePosSize(tape);
8724 int chunk_size_expected = tape_pos_size * tape->length;
8726 if (chunk_size_expected != chunk_size)
8728 ReadUnusedBytesFromFile(file, chunk_size);
8729 return chunk_size_expected;
8732 for (i = 0; i < tape->length; i++)
8734 if (i >= MAX_TAPE_LEN)
8736 Warn("tape truncated -- size exceeds maximum tape size %d",
8739 // tape too large; read and ignore remaining tape data from this chunk
8740 for (;i < tape->length; i++)
8741 ReadUnusedBytesFromFile(file, tape_pos_size);
8746 if (tape->use_key_actions)
8748 for (j = 0; j < MAX_PLAYERS; j++)
8750 tape->pos[i].action[j] = MV_NONE;
8752 if (tape->player_participates[j])
8753 tape->pos[i].action[j] = getFile8Bit(file);
8757 if (tape->use_mouse_actions)
8759 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8760 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8761 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8764 tape->pos[i].delay = getFile8Bit(file);
8766 if (tape->file_version == FILE_VERSION_1_0)
8768 // eliminate possible diagonal moves in old tapes
8769 // this is only for backward compatibility
8771 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8772 byte action = tape->pos[i].action[0];
8773 int k, num_moves = 0;
8775 for (k = 0; k < 4; k++)
8777 if (action & joy_dir[k])
8779 tape->pos[i + num_moves].action[0] = joy_dir[k];
8781 tape->pos[i + num_moves].delay = 0;
8790 tape->length += num_moves;
8793 else if (tape->file_version < FILE_VERSION_2_0)
8795 // convert pre-2.0 tapes to new tape format
8797 if (tape->pos[i].delay > 1)
8800 tape->pos[i + 1] = tape->pos[i];
8801 tape->pos[i + 1].delay = 1;
8804 for (j = 0; j < MAX_PLAYERS; j++)
8805 tape->pos[i].action[j] = MV_NONE;
8806 tape->pos[i].delay--;
8813 if (checkEndOfFile(file))
8817 if (i != tape->length)
8818 chunk_size = tape_pos_size * i;
8823 static void LoadTape_SokobanSolution(char *filename)
8826 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8828 if (!(file = openFile(filename, MODE_READ)))
8830 tape.no_valid_file = TRUE;
8835 while (!checkEndOfFile(file))
8837 unsigned char c = getByteFromFile(file);
8839 if (checkEndOfFile(file))
8846 tape.pos[tape.length].action[0] = MV_UP;
8847 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8853 tape.pos[tape.length].action[0] = MV_DOWN;
8854 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8860 tape.pos[tape.length].action[0] = MV_LEFT;
8861 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8867 tape.pos[tape.length].action[0] = MV_RIGHT;
8868 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8876 // ignore white-space characters
8880 tape.no_valid_file = TRUE;
8882 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8890 if (tape.no_valid_file)
8893 tape.length_frames = GetTapeLengthFrames();
8894 tape.length_seconds = GetTapeLengthSeconds();
8897 void LoadTapeFromFilename(char *filename)
8899 char cookie[MAX_LINE_LEN];
8900 char chunk_name[CHUNK_ID_LEN + 1];
8904 // always start with reliable default values
8905 setTapeInfoToDefaults();
8907 if (strSuffix(filename, ".sln"))
8909 LoadTape_SokobanSolution(filename);
8914 if (!(file = openFile(filename, MODE_READ)))
8916 tape.no_valid_file = TRUE;
8921 getFileChunkBE(file, chunk_name, NULL);
8922 if (strEqual(chunk_name, "RND1"))
8924 getFile32BitBE(file); // not used
8926 getFileChunkBE(file, chunk_name, NULL);
8927 if (!strEqual(chunk_name, "TAPE"))
8929 tape.no_valid_file = TRUE;
8931 Warn("unknown format of tape file '%s'", filename);
8938 else // check for pre-2.0 file format with cookie string
8940 strcpy(cookie, chunk_name);
8941 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8943 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8944 cookie[strlen(cookie) - 1] = '\0';
8946 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8948 tape.no_valid_file = TRUE;
8950 Warn("unknown format of tape file '%s'", filename);
8957 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8959 tape.no_valid_file = TRUE;
8961 Warn("unsupported version of tape file '%s'", filename);
8968 // pre-2.0 tape files have no game version, so use file version here
8969 tape.game_version = tape.file_version;
8972 if (tape.file_version < FILE_VERSION_1_2)
8974 // tape files from versions before 1.2.0 without chunk structure
8975 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8976 LoadTape_BODY(file, 2 * tape.length, &tape);
8984 int (*loader)(File *, int, struct TapeInfo *);
8988 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8989 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8990 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8991 { "INFO", -1, LoadTape_INFO },
8992 { "BODY", -1, LoadTape_BODY },
8996 while (getFileChunkBE(file, chunk_name, &chunk_size))
9000 while (chunk_info[i].name != NULL &&
9001 !strEqual(chunk_name, chunk_info[i].name))
9004 if (chunk_info[i].name == NULL)
9006 Warn("unknown chunk '%s' in tape file '%s'",
9007 chunk_name, filename);
9009 ReadUnusedBytesFromFile(file, chunk_size);
9011 else if (chunk_info[i].size != -1 &&
9012 chunk_info[i].size != chunk_size)
9014 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9015 chunk_size, chunk_name, filename);
9017 ReadUnusedBytesFromFile(file, chunk_size);
9021 // call function to load this tape chunk
9022 int chunk_size_expected =
9023 (chunk_info[i].loader)(file, chunk_size, &tape);
9025 // the size of some chunks cannot be checked before reading other
9026 // chunks first (like "HEAD" and "BODY") that contain some header
9027 // information, so check them here
9028 if (chunk_size_expected != chunk_size)
9030 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9031 chunk_size, chunk_name, filename);
9039 tape.length_frames = GetTapeLengthFrames();
9040 tape.length_seconds = GetTapeLengthSeconds();
9043 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9045 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9047 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9048 tape.engine_version);
9052 void LoadTape(int nr)
9054 char *filename = getTapeFilename(nr);
9056 LoadTapeFromFilename(filename);
9059 void LoadSolutionTape(int nr)
9061 char *filename = getSolutionTapeFilename(nr);
9063 LoadTapeFromFilename(filename);
9065 if (TAPE_IS_EMPTY(tape))
9067 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9068 level.native_bd_level->replay != NULL)
9069 CopyNativeTape_BD_to_RND(&level);
9070 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9071 level.native_sp_level->demo.is_available)
9072 CopyNativeTape_SP_to_RND(&level);
9076 void LoadScoreTape(char *score_tape_basename, int nr)
9078 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9080 LoadTapeFromFilename(filename);
9083 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9085 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9087 LoadTapeFromFilename(filename);
9090 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9092 // chunk required for team mode tapes with non-default screen size
9093 return (tape->num_participating_players > 1 &&
9094 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9095 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9098 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9100 putFileVersion(file, tape->file_version);
9101 putFileVersion(file, tape->game_version);
9104 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9107 byte store_participating_players = 0;
9109 // set bits for participating players for compact storage
9110 for (i = 0; i < MAX_PLAYERS; i++)
9111 if (tape->player_participates[i])
9112 store_participating_players |= (1 << i);
9114 putFile32BitBE(file, tape->random_seed);
9115 putFile32BitBE(file, tape->date);
9116 putFile32BitBE(file, tape->length);
9118 putFile8Bit(file, store_participating_players);
9120 putFile8Bit(file, getTapeActionValue(tape));
9122 putFile8Bit(file, tape->property_bits);
9123 putFile8Bit(file, tape->solved);
9125 putFileVersion(file, tape->engine_version);
9128 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9130 putFile8Bit(file, tape->scr_fieldx);
9131 putFile8Bit(file, tape->scr_fieldy);
9134 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9136 int level_identifier_size = strlen(tape->level_identifier) + 1;
9139 putFile16BitBE(file, level_identifier_size);
9141 for (i = 0; i < level_identifier_size; i++)
9142 putFile8Bit(file, tape->level_identifier[i]);
9144 putFile16BitBE(file, tape->level_nr);
9147 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9151 for (i = 0; i < tape->length; i++)
9153 if (tape->use_key_actions)
9155 for (j = 0; j < MAX_PLAYERS; j++)
9156 if (tape->player_participates[j])
9157 putFile8Bit(file, tape->pos[i].action[j]);
9160 if (tape->use_mouse_actions)
9162 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9163 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9164 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9167 putFile8Bit(file, tape->pos[i].delay);
9171 void SaveTapeToFilename(char *filename)
9175 int info_chunk_size;
9176 int body_chunk_size;
9178 if (!(file = fopen(filename, MODE_WRITE)))
9180 Warn("cannot save level recording file '%s'", filename);
9185 tape_pos_size = getTapePosSize(&tape);
9187 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9188 body_chunk_size = tape_pos_size * tape.length;
9190 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9191 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9193 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9194 SaveTape_VERS(file, &tape);
9196 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9197 SaveTape_HEAD(file, &tape);
9199 if (checkSaveTape_SCRN(&tape))
9201 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9202 SaveTape_SCRN(file, &tape);
9205 putFileChunkBE(file, "INFO", info_chunk_size);
9206 SaveTape_INFO(file, &tape);
9208 putFileChunkBE(file, "BODY", body_chunk_size);
9209 SaveTape_BODY(file, &tape);
9213 SetFilePermissions(filename, PERMS_PRIVATE);
9216 static void SaveTapeExt(char *filename)
9220 tape.file_version = FILE_VERSION_ACTUAL;
9221 tape.game_version = GAME_VERSION_ACTUAL;
9223 tape.num_participating_players = 0;
9225 // count number of participating players
9226 for (i = 0; i < MAX_PLAYERS; i++)
9227 if (tape.player_participates[i])
9228 tape.num_participating_players++;
9230 SaveTapeToFilename(filename);
9232 tape.changed = FALSE;
9235 void SaveTape(int nr)
9237 char *filename = getTapeFilename(nr);
9239 InitTapeDirectory(leveldir_current->subdir);
9241 SaveTapeExt(filename);
9244 void SaveScoreTape(int nr)
9246 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9248 // used instead of "leveldir_current->subdir" (for network games)
9249 InitScoreTapeDirectory(levelset.identifier, nr);
9251 SaveTapeExt(filename);
9254 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9255 unsigned int req_state_added)
9257 char *filename = getTapeFilename(nr);
9258 boolean new_tape = !fileExists(filename);
9259 boolean tape_saved = FALSE;
9261 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9266 Request(msg_saved, REQ_CONFIRM | req_state_added);
9274 boolean SaveTapeChecked(int nr)
9276 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9279 boolean SaveTapeChecked_LevelSolved(int nr)
9281 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9282 "Level solved! Tape saved!", REQ_STAY_OPEN);
9285 void DumpTape(struct TapeInfo *tape)
9287 int tape_frame_counter;
9290 if (tape->no_valid_file)
9292 Warn("cannot dump -- no valid tape file found");
9299 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9300 tape->level_nr, tape->file_version, tape->game_version);
9301 Print(" (effective engine version %08d)\n",
9302 tape->engine_version);
9303 Print("Level series identifier: '%s'\n", tape->level_identifier);
9305 Print("Solution tape: %s\n",
9306 tape->solved ? "yes" :
9307 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9309 Print("Special tape properties: ");
9310 if (tape->property_bits == TAPE_PROPERTY_NONE)
9312 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9313 Print("[em_random_bug]");
9314 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9315 Print("[game_speed]");
9316 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9318 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9319 Print("[single_step]");
9320 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9321 Print("[snapshot]");
9322 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9323 Print("[replayed]");
9324 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9325 Print("[tas_keys]");
9326 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9327 Print("[small_graphics]");
9330 int year2 = tape->date / 10000;
9331 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9332 int month_index_raw = (tape->date / 100) % 100;
9333 int month_index = month_index_raw % 12; // prevent invalid index
9334 int month = month_index + 1;
9335 int day = tape->date % 100;
9337 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9341 tape_frame_counter = 0;
9343 for (i = 0; i < tape->length; i++)
9345 if (i >= MAX_TAPE_LEN)
9350 for (j = 0; j < MAX_PLAYERS; j++)
9352 if (tape->player_participates[j])
9354 int action = tape->pos[i].action[j];
9356 Print("%d:%02x ", j, action);
9357 Print("[%c%c%c%c|%c%c] - ",
9358 (action & JOY_LEFT ? '<' : ' '),
9359 (action & JOY_RIGHT ? '>' : ' '),
9360 (action & JOY_UP ? '^' : ' '),
9361 (action & JOY_DOWN ? 'v' : ' '),
9362 (action & JOY_BUTTON_1 ? '1' : ' '),
9363 (action & JOY_BUTTON_2 ? '2' : ' '));
9367 Print("(%03d) ", tape->pos[i].delay);
9368 Print("[%05d]\n", tape_frame_counter);
9370 tape_frame_counter += tape->pos[i].delay;
9376 void DumpTapes(void)
9378 static LevelDirTree *dumptape_leveldir = NULL;
9380 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9381 global.dumptape_leveldir);
9383 if (dumptape_leveldir == NULL)
9384 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9386 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9387 global.dumptape_level_nr > dumptape_leveldir->last_level)
9388 Fail("no such level number: %d", global.dumptape_level_nr);
9390 leveldir_current = dumptape_leveldir;
9392 if (options.mytapes)
9393 LoadTape(global.dumptape_level_nr);
9395 LoadSolutionTape(global.dumptape_level_nr);
9403 // ============================================================================
9404 // score file functions
9405 // ============================================================================
9407 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9411 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9413 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9414 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9415 scores->entry[i].score = 0;
9416 scores->entry[i].time = 0;
9418 scores->entry[i].id = -1;
9419 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9420 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9421 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9422 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9423 strcpy(scores->entry[i].country_code, "??");
9426 scores->num_entries = 0;
9427 scores->last_added = -1;
9428 scores->last_added_local = -1;
9430 scores->updated = FALSE;
9431 scores->uploaded = FALSE;
9432 scores->tape_downloaded = FALSE;
9433 scores->force_last_added = FALSE;
9435 // The following values are intentionally not reset here:
9439 // - continue_playing
9440 // - continue_on_return
9443 static void setScoreInfoToDefaults(void)
9445 setScoreInfoToDefaultsExt(&scores);
9448 static void setServerScoreInfoToDefaults(void)
9450 setScoreInfoToDefaultsExt(&server_scores);
9453 static void LoadScore_OLD(int nr)
9456 char *filename = getScoreFilename(nr);
9457 char cookie[MAX_LINE_LEN];
9458 char line[MAX_LINE_LEN];
9462 if (!(file = fopen(filename, MODE_READ)))
9465 // check file identifier
9466 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9468 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9469 cookie[strlen(cookie) - 1] = '\0';
9471 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9473 Warn("unknown format of score file '%s'", filename);
9480 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9482 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9483 Warn("fscanf() failed; %s", strerror(errno));
9485 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9488 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9489 line[strlen(line) - 1] = '\0';
9491 for (line_ptr = line; *line_ptr; line_ptr++)
9493 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9495 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9496 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9505 static void ConvertScore_OLD(void)
9507 // only convert score to time for levels that rate playing time over score
9508 if (!level.rate_time_over_score)
9511 // convert old score to playing time for score-less levels (like Supaplex)
9512 int time_final_max = 999;
9515 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9517 int score = scores.entry[i].score;
9519 if (score > 0 && score < time_final_max)
9520 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9524 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9526 scores->file_version = getFileVersion(file);
9527 scores->game_version = getFileVersion(file);
9532 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9534 char *level_identifier = NULL;
9535 int level_identifier_size;
9538 level_identifier_size = getFile16BitBE(file);
9540 level_identifier = checked_malloc(level_identifier_size);
9542 for (i = 0; i < level_identifier_size; i++)
9543 level_identifier[i] = getFile8Bit(file);
9545 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9546 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9548 checked_free(level_identifier);
9550 scores->level_nr = getFile16BitBE(file);
9551 scores->num_entries = getFile16BitBE(file);
9553 chunk_size = 2 + level_identifier_size + 2 + 2;
9558 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9562 for (i = 0; i < scores->num_entries; i++)
9564 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9565 scores->entry[i].name[j] = getFile8Bit(file);
9567 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9570 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9575 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9579 for (i = 0; i < scores->num_entries; i++)
9580 scores->entry[i].score = getFile16BitBE(file);
9582 chunk_size = scores->num_entries * 2;
9587 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9591 for (i = 0; i < scores->num_entries; i++)
9592 scores->entry[i].score = getFile32BitBE(file);
9594 chunk_size = scores->num_entries * 4;
9599 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9603 for (i = 0; i < scores->num_entries; i++)
9604 scores->entry[i].time = getFile32BitBE(file);
9606 chunk_size = scores->num_entries * 4;
9611 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9615 for (i = 0; i < scores->num_entries; i++)
9617 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9618 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9620 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9623 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9628 void LoadScore(int nr)
9630 char *filename = getScoreFilename(nr);
9631 char cookie[MAX_LINE_LEN];
9632 char chunk_name[CHUNK_ID_LEN + 1];
9634 boolean old_score_file_format = FALSE;
9637 // always start with reliable default values
9638 setScoreInfoToDefaults();
9640 if (!(file = openFile(filename, MODE_READ)))
9643 getFileChunkBE(file, chunk_name, NULL);
9644 if (strEqual(chunk_name, "RND1"))
9646 getFile32BitBE(file); // not used
9648 getFileChunkBE(file, chunk_name, NULL);
9649 if (!strEqual(chunk_name, "SCOR"))
9651 Warn("unknown format of score file '%s'", filename);
9658 else // check for old file format with cookie string
9660 strcpy(cookie, chunk_name);
9661 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9663 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9664 cookie[strlen(cookie) - 1] = '\0';
9666 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9668 Warn("unknown format of score file '%s'", filename);
9675 old_score_file_format = TRUE;
9678 if (old_score_file_format)
9680 // score files from versions before 4.2.4.0 without chunk structure
9683 // convert score to time, if possible (mainly for Supaplex levels)
9692 int (*loader)(File *, int, struct ScoreInfo *);
9696 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9697 { "INFO", -1, LoadScore_INFO },
9698 { "NAME", -1, LoadScore_NAME },
9699 { "SCOR", -1, LoadScore_SCOR },
9700 { "SC4R", -1, LoadScore_SC4R },
9701 { "TIME", -1, LoadScore_TIME },
9702 { "TAPE", -1, LoadScore_TAPE },
9707 while (getFileChunkBE(file, chunk_name, &chunk_size))
9711 while (chunk_info[i].name != NULL &&
9712 !strEqual(chunk_name, chunk_info[i].name))
9715 if (chunk_info[i].name == NULL)
9717 Warn("unknown chunk '%s' in score file '%s'",
9718 chunk_name, filename);
9720 ReadUnusedBytesFromFile(file, chunk_size);
9722 else if (chunk_info[i].size != -1 &&
9723 chunk_info[i].size != chunk_size)
9725 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9726 chunk_size, chunk_name, filename);
9728 ReadUnusedBytesFromFile(file, chunk_size);
9732 // call function to load this score chunk
9733 int chunk_size_expected =
9734 (chunk_info[i].loader)(file, chunk_size, &scores);
9736 // the size of some chunks cannot be checked before reading other
9737 // chunks first (like "HEAD" and "BODY") that contain some header
9738 // information, so check them here
9739 if (chunk_size_expected != chunk_size)
9741 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9742 chunk_size, chunk_name, filename);
9751 #if ENABLE_HISTORIC_CHUNKS
9752 void SaveScore_OLD(int nr)
9755 char *filename = getScoreFilename(nr);
9758 // used instead of "leveldir_current->subdir" (for network games)
9759 InitScoreDirectory(levelset.identifier);
9761 if (!(file = fopen(filename, MODE_WRITE)))
9763 Warn("cannot save score for level %d", nr);
9768 fprintf(file, "%s\n\n", SCORE_COOKIE);
9770 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9771 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9775 SetFilePermissions(filename, PERMS_PRIVATE);
9779 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9781 putFileVersion(file, scores->file_version);
9782 putFileVersion(file, scores->game_version);
9785 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9787 int level_identifier_size = strlen(scores->level_identifier) + 1;
9790 putFile16BitBE(file, level_identifier_size);
9792 for (i = 0; i < level_identifier_size; i++)
9793 putFile8Bit(file, scores->level_identifier[i]);
9795 putFile16BitBE(file, scores->level_nr);
9796 putFile16BitBE(file, scores->num_entries);
9799 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9803 for (i = 0; i < scores->num_entries; i++)
9805 int name_size = strlen(scores->entry[i].name);
9807 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9808 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9812 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9816 for (i = 0; i < scores->num_entries; i++)
9817 putFile16BitBE(file, scores->entry[i].score);
9820 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9824 for (i = 0; i < scores->num_entries; i++)
9825 putFile32BitBE(file, scores->entry[i].score);
9828 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9832 for (i = 0; i < scores->num_entries; i++)
9833 putFile32BitBE(file, scores->entry[i].time);
9836 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9840 for (i = 0; i < scores->num_entries; i++)
9842 int size = strlen(scores->entry[i].tape_basename);
9844 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9845 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9849 static void SaveScoreToFilename(char *filename)
9852 int info_chunk_size;
9853 int name_chunk_size;
9854 int scor_chunk_size;
9855 int sc4r_chunk_size;
9856 int time_chunk_size;
9857 int tape_chunk_size;
9858 boolean has_large_score_values;
9861 if (!(file = fopen(filename, MODE_WRITE)))
9863 Warn("cannot save score file '%s'", filename);
9868 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9869 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9870 scor_chunk_size = scores.num_entries * 2;
9871 sc4r_chunk_size = scores.num_entries * 4;
9872 time_chunk_size = scores.num_entries * 4;
9873 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9875 has_large_score_values = FALSE;
9876 for (i = 0; i < scores.num_entries; i++)
9877 if (scores.entry[i].score > 0xffff)
9878 has_large_score_values = TRUE;
9880 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9881 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9883 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9884 SaveScore_VERS(file, &scores);
9886 putFileChunkBE(file, "INFO", info_chunk_size);
9887 SaveScore_INFO(file, &scores);
9889 putFileChunkBE(file, "NAME", name_chunk_size);
9890 SaveScore_NAME(file, &scores);
9892 if (has_large_score_values)
9894 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9895 SaveScore_SC4R(file, &scores);
9899 putFileChunkBE(file, "SCOR", scor_chunk_size);
9900 SaveScore_SCOR(file, &scores);
9903 putFileChunkBE(file, "TIME", time_chunk_size);
9904 SaveScore_TIME(file, &scores);
9906 putFileChunkBE(file, "TAPE", tape_chunk_size);
9907 SaveScore_TAPE(file, &scores);
9911 SetFilePermissions(filename, PERMS_PRIVATE);
9914 void SaveScore(int nr)
9916 char *filename = getScoreFilename(nr);
9919 // used instead of "leveldir_current->subdir" (for network games)
9920 InitScoreDirectory(levelset.identifier);
9922 scores.file_version = FILE_VERSION_ACTUAL;
9923 scores.game_version = GAME_VERSION_ACTUAL;
9925 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9926 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9927 scores.level_nr = level_nr;
9929 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9930 if (scores.entry[i].score == 0 &&
9931 scores.entry[i].time == 0 &&
9932 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9935 scores.num_entries = i;
9937 if (scores.num_entries == 0)
9940 SaveScoreToFilename(filename);
9943 static void LoadServerScoreFromCache(int nr)
9945 struct ScoreEntry score_entry;
9954 { &score_entry.score, FALSE, 0 },
9955 { &score_entry.time, FALSE, 0 },
9956 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9957 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9958 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9959 { &score_entry.id, FALSE, 0 },
9960 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9961 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9962 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9963 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9967 char *filename = getScoreCacheFilename(nr);
9968 SetupFileHash *score_hash = loadSetupFileHash(filename);
9971 server_scores.num_entries = 0;
9973 if (score_hash == NULL)
9976 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9978 score_entry = server_scores.entry[i];
9980 for (j = 0; score_mapping[j].value != NULL; j++)
9984 sprintf(token, "%02d.%d", i, j);
9986 char *value = getHashEntry(score_hash, token);
9991 if (score_mapping[j].is_string)
9993 char *score_value = (char *)score_mapping[j].value;
9994 int value_size = score_mapping[j].string_size;
9996 strncpy(score_value, value, value_size);
9997 score_value[value_size] = '\0';
10001 int *score_value = (int *)score_mapping[j].value;
10003 *score_value = atoi(value);
10006 server_scores.num_entries = i + 1;
10009 server_scores.entry[i] = score_entry;
10012 freeSetupFileHash(score_hash);
10015 void LoadServerScore(int nr, boolean download_score)
10017 if (!setup.use_api_server)
10020 // always start with reliable default values
10021 setServerScoreInfoToDefaults();
10023 // 1st step: load server scores from cache file (which may not exist)
10024 // (this should prevent reading it while the thread is writing to it)
10025 LoadServerScoreFromCache(nr);
10027 if (download_score && runtime.use_api_server)
10029 // 2nd step: download server scores from score server to cache file
10030 // (as thread, as it might time out if the server is not reachable)
10031 ApiGetScoreAsThread(nr);
10035 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10037 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10039 // if score tape not uploaded, ask for uploading missing tapes later
10040 if (!setup.has_remaining_tapes)
10041 setup.ask_for_remaining_tapes = TRUE;
10043 setup.provide_uploading_tapes = TRUE;
10044 setup.has_remaining_tapes = TRUE;
10046 SaveSetup_ServerSetup();
10049 void SaveServerScore(int nr, boolean tape_saved)
10051 if (!runtime.use_api_server)
10053 PrepareScoreTapesForUpload(leveldir_current->subdir);
10058 ApiAddScoreAsThread(nr, tape_saved, NULL);
10061 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10062 char *score_tape_filename)
10064 if (!runtime.use_api_server)
10067 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10070 void LoadLocalAndServerScore(int nr, boolean download_score)
10072 int last_added_local = scores.last_added_local;
10073 boolean force_last_added = scores.force_last_added;
10075 // needed if only showing server scores
10076 setScoreInfoToDefaults();
10078 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10081 // restore last added local score entry (before merging server scores)
10082 scores.last_added = scores.last_added_local = last_added_local;
10084 if (setup.use_api_server &&
10085 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10087 // load server scores from cache file and trigger update from server
10088 LoadServerScore(nr, download_score);
10090 // merge local scores with scores from server
10091 MergeServerScore();
10094 if (force_last_added)
10095 scores.force_last_added = force_last_added;
10099 // ============================================================================
10100 // setup file functions
10101 // ============================================================================
10103 #define TOKEN_STR_PLAYER_PREFIX "player_"
10106 static struct TokenInfo global_setup_tokens[] =
10110 &setup.player_name, "player_name"
10114 &setup.multiple_users, "multiple_users"
10118 &setup.sound, "sound"
10122 &setup.sound_loops, "repeating_sound_loops"
10126 &setup.sound_music, "background_music"
10130 &setup.sound_simple, "simple_sound_effects"
10134 &setup.toons, "toons"
10138 &setup.global_animations, "global_animations"
10142 &setup.scroll_delay, "scroll_delay"
10146 &setup.forced_scroll_delay, "forced_scroll_delay"
10150 &setup.scroll_delay_value, "scroll_delay_value"
10154 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10158 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10162 &setup.fade_screens, "fade_screens"
10166 &setup.autorecord, "automatic_tape_recording"
10170 &setup.autorecord_after_replay, "autorecord_after_replay"
10174 &setup.auto_pause_on_start, "auto_pause_on_start"
10178 &setup.show_titlescreen, "show_titlescreen"
10182 &setup.quick_doors, "quick_doors"
10186 &setup.team_mode, "team_mode"
10190 &setup.handicap, "handicap"
10194 &setup.skip_levels, "skip_levels"
10198 &setup.increment_levels, "increment_levels"
10202 &setup.auto_play_next_level, "auto_play_next_level"
10206 &setup.count_score_after_game, "count_score_after_game"
10210 &setup.show_scores_after_game, "show_scores_after_game"
10214 &setup.time_limit, "time_limit"
10218 &setup.fullscreen, "fullscreen"
10222 &setup.window_scaling_percent, "window_scaling_percent"
10226 &setup.window_scaling_quality, "window_scaling_quality"
10230 &setup.screen_rendering_mode, "screen_rendering_mode"
10234 &setup.vsync_mode, "vsync_mode"
10238 &setup.ask_on_escape, "ask_on_escape"
10242 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10246 &setup.ask_on_game_over, "ask_on_game_over"
10250 &setup.ask_on_quit_game, "ask_on_quit_game"
10254 &setup.ask_on_quit_program, "ask_on_quit_program"
10258 &setup.quick_switch, "quick_player_switch"
10262 &setup.input_on_focus, "input_on_focus"
10266 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10270 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10274 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10278 &setup.game_speed_extended, "game_speed_extended"
10282 &setup.game_frame_delay, "game_frame_delay"
10286 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10290 &setup.bd_skip_hatching, "bd_skip_hatching"
10294 &setup.bd_scroll_delay, "bd_scroll_delay"
10298 &setup.bd_smooth_movements, "bd_smooth_movements"
10302 &setup.sp_show_border_elements, "sp_show_border_elements"
10306 &setup.small_game_graphics, "small_game_graphics"
10310 &setup.show_load_save_buttons, "show_load_save_buttons"
10314 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10318 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10322 &setup.graphics_set, "graphics_set"
10326 &setup.sounds_set, "sounds_set"
10330 &setup.music_set, "music_set"
10334 &setup.override_level_graphics, "override_level_graphics"
10338 &setup.override_level_sounds, "override_level_sounds"
10342 &setup.override_level_music, "override_level_music"
10346 &setup.volume_simple, "volume_simple"
10350 &setup.volume_loops, "volume_loops"
10354 &setup.volume_music, "volume_music"
10358 &setup.network_mode, "network_mode"
10362 &setup.network_player_nr, "network_player"
10366 &setup.network_server_hostname, "network_server_hostname"
10370 &setup.touch.control_type, "touch.control_type"
10374 &setup.touch.move_distance, "touch.move_distance"
10378 &setup.touch.drop_distance, "touch.drop_distance"
10382 &setup.touch.transparency, "touch.transparency"
10386 &setup.touch.draw_outlined, "touch.draw_outlined"
10390 &setup.touch.draw_pressed, "touch.draw_pressed"
10394 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10398 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10402 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10406 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10410 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10414 static struct TokenInfo auto_setup_tokens[] =
10418 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10422 static struct TokenInfo server_setup_tokens[] =
10426 &setup.player_uuid, "player_uuid"
10430 &setup.player_version, "player_version"
10434 &setup.use_api_server, TEST_PREFIX "use_api_server"
10438 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10442 &setup.api_server_password, TEST_PREFIX "api_server_password"
10446 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10450 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10454 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10458 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10462 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10466 static struct TokenInfo editor_setup_tokens[] =
10470 &setup.editor.el_classic, "editor.el_classic"
10474 &setup.editor.el_custom, "editor.el_custom"
10478 &setup.editor.el_user_defined, "editor.el_user_defined"
10482 &setup.editor.el_dynamic, "editor.el_dynamic"
10486 &setup.editor.el_headlines, "editor.el_headlines"
10490 &setup.editor.show_element_token, "editor.show_element_token"
10494 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10498 static struct TokenInfo editor_cascade_setup_tokens[] =
10502 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10506 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10510 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10514 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10518 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10522 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10526 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10530 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10534 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10538 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10542 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10546 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10550 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10554 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10558 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10562 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10566 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10570 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10574 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10578 static struct TokenInfo shortcut_setup_tokens[] =
10582 &setup.shortcut.save_game, "shortcut.save_game"
10586 &setup.shortcut.load_game, "shortcut.load_game"
10590 &setup.shortcut.restart_game, "shortcut.restart_game"
10594 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10598 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10602 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10606 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10610 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10614 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10618 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10622 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10626 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10630 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10634 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10638 &setup.shortcut.tape_record, "shortcut.tape_record"
10642 &setup.shortcut.tape_play, "shortcut.tape_play"
10646 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10650 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10654 &setup.shortcut.sound_music, "shortcut.sound_music"
10658 &setup.shortcut.snap_left, "shortcut.snap_left"
10662 &setup.shortcut.snap_right, "shortcut.snap_right"
10666 &setup.shortcut.snap_up, "shortcut.snap_up"
10670 &setup.shortcut.snap_down, "shortcut.snap_down"
10674 static struct SetupInputInfo setup_input;
10675 static struct TokenInfo player_setup_tokens[] =
10679 &setup_input.use_joystick, ".use_joystick"
10683 &setup_input.joy.device_name, ".joy.device_name"
10687 &setup_input.joy.xleft, ".joy.xleft"
10691 &setup_input.joy.xmiddle, ".joy.xmiddle"
10695 &setup_input.joy.xright, ".joy.xright"
10699 &setup_input.joy.yupper, ".joy.yupper"
10703 &setup_input.joy.ymiddle, ".joy.ymiddle"
10707 &setup_input.joy.ylower, ".joy.ylower"
10711 &setup_input.joy.snap, ".joy.snap_field"
10715 &setup_input.joy.drop, ".joy.place_bomb"
10719 &setup_input.key.left, ".key.move_left"
10723 &setup_input.key.right, ".key.move_right"
10727 &setup_input.key.up, ".key.move_up"
10731 &setup_input.key.down, ".key.move_down"
10735 &setup_input.key.snap, ".key.snap_field"
10739 &setup_input.key.drop, ".key.place_bomb"
10743 static struct TokenInfo system_setup_tokens[] =
10747 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10751 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10755 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10759 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10763 static struct TokenInfo internal_setup_tokens[] =
10767 &setup.internal.program_title, "program_title"
10771 &setup.internal.program_version, "program_version"
10775 &setup.internal.program_author, "program_author"
10779 &setup.internal.program_email, "program_email"
10783 &setup.internal.program_website, "program_website"
10787 &setup.internal.program_copyright, "program_copyright"
10791 &setup.internal.program_company, "program_company"
10795 &setup.internal.program_icon_file, "program_icon_file"
10799 &setup.internal.default_graphics_set, "default_graphics_set"
10803 &setup.internal.default_sounds_set, "default_sounds_set"
10807 &setup.internal.default_music_set, "default_music_set"
10811 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10815 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10819 &setup.internal.fallback_music_file, "fallback_music_file"
10823 &setup.internal.default_level_series, "default_level_series"
10827 &setup.internal.default_window_width, "default_window_width"
10831 &setup.internal.default_window_height, "default_window_height"
10835 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10839 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10843 &setup.internal.create_user_levelset, "create_user_levelset"
10847 &setup.internal.info_screens_from_main, "info_screens_from_main"
10851 &setup.internal.menu_game, "menu_game"
10855 &setup.internal.menu_engines, "menu_engines"
10859 &setup.internal.menu_editor, "menu_editor"
10863 &setup.internal.menu_graphics, "menu_graphics"
10867 &setup.internal.menu_sound, "menu_sound"
10871 &setup.internal.menu_artwork, "menu_artwork"
10875 &setup.internal.menu_input, "menu_input"
10879 &setup.internal.menu_touch, "menu_touch"
10883 &setup.internal.menu_shortcuts, "menu_shortcuts"
10887 &setup.internal.menu_exit, "menu_exit"
10891 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10895 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10899 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10903 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10907 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10911 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10915 &setup.internal.info_title, "info_title"
10919 &setup.internal.info_elements, "info_elements"
10923 &setup.internal.info_music, "info_music"
10927 &setup.internal.info_credits, "info_credits"
10931 &setup.internal.info_program, "info_program"
10935 &setup.internal.info_version, "info_version"
10939 &setup.internal.info_levelset, "info_levelset"
10943 &setup.internal.info_exit, "info_exit"
10947 static struct TokenInfo debug_setup_tokens[] =
10951 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10955 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10959 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10963 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10967 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10971 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10975 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10979 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10983 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10987 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10991 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10995 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10999 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11003 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11007 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11011 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11015 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11019 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11023 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11027 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11031 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11034 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11038 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11042 &setup.debug.xsn_mode, "debug.xsn_mode"
11046 &setup.debug.xsn_percent, "debug.xsn_percent"
11050 static struct TokenInfo options_setup_tokens[] =
11054 &setup.options.verbose, "options.verbose"
11058 &setup.options.debug, "options.debug"
11062 &setup.options.debug_mode, "options.debug_mode"
11066 static void setSetupInfoToDefaults(struct SetupInfo *si)
11070 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11072 si->multiple_users = TRUE;
11075 si->sound_loops = TRUE;
11076 si->sound_music = TRUE;
11077 si->sound_simple = TRUE;
11079 si->global_animations = TRUE;
11080 si->scroll_delay = TRUE;
11081 si->forced_scroll_delay = FALSE;
11082 si->scroll_delay_value = STD_SCROLL_DELAY;
11083 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11084 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11085 si->fade_screens = TRUE;
11086 si->autorecord = TRUE;
11087 si->autorecord_after_replay = TRUE;
11088 si->auto_pause_on_start = FALSE;
11089 si->show_titlescreen = TRUE;
11090 si->quick_doors = FALSE;
11091 si->team_mode = FALSE;
11092 si->handicap = TRUE;
11093 si->skip_levels = TRUE;
11094 si->increment_levels = TRUE;
11095 si->auto_play_next_level = TRUE;
11096 si->count_score_after_game = TRUE;
11097 si->show_scores_after_game = TRUE;
11098 si->time_limit = TRUE;
11099 si->fullscreen = FALSE;
11100 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11101 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11102 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11103 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11104 si->ask_on_escape = TRUE;
11105 si->ask_on_escape_editor = TRUE;
11106 si->ask_on_game_over = TRUE;
11107 si->ask_on_quit_game = TRUE;
11108 si->ask_on_quit_program = TRUE;
11109 si->quick_switch = FALSE;
11110 si->input_on_focus = FALSE;
11111 si->prefer_aga_graphics = TRUE;
11112 si->prefer_lowpass_sounds = FALSE;
11113 si->prefer_extra_panel_items = TRUE;
11114 si->game_speed_extended = FALSE;
11115 si->game_frame_delay = GAME_FRAME_DELAY;
11116 si->bd_skip_uncovering = FALSE;
11117 si->bd_skip_hatching = FALSE;
11118 si->bd_scroll_delay = TRUE;
11119 si->bd_smooth_movements = AUTO;
11120 si->sp_show_border_elements = FALSE;
11121 si->small_game_graphics = FALSE;
11122 si->show_load_save_buttons = FALSE;
11123 si->show_undo_redo_buttons = FALSE;
11124 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11126 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11127 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11128 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11130 si->override_level_graphics = FALSE;
11131 si->override_level_sounds = FALSE;
11132 si->override_level_music = FALSE;
11134 si->volume_simple = 100; // percent
11135 si->volume_loops = 100; // percent
11136 si->volume_music = 100; // percent
11138 si->network_mode = FALSE;
11139 si->network_player_nr = 0; // first player
11140 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11142 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11143 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11144 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11145 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11146 si->touch.draw_outlined = TRUE;
11147 si->touch.draw_pressed = TRUE;
11149 for (i = 0; i < 2; i++)
11151 char *default_grid_button[6][2] =
11157 { "111222", " vv " },
11158 { "111222", " vv " }
11160 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11161 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11162 int min_xsize = MIN(6, grid_xsize);
11163 int min_ysize = MIN(6, grid_ysize);
11164 int startx = grid_xsize - min_xsize;
11165 int starty = grid_ysize - min_ysize;
11168 // virtual buttons grid can only be set to defaults if video is initialized
11169 // (this will be repeated if virtual buttons are not loaded from setup file)
11170 if (video.initialized)
11172 si->touch.grid_xsize[i] = grid_xsize;
11173 si->touch.grid_ysize[i] = grid_ysize;
11177 si->touch.grid_xsize[i] = -1;
11178 si->touch.grid_ysize[i] = -1;
11181 for (x = 0; x < MAX_GRID_XSIZE; x++)
11182 for (y = 0; y < MAX_GRID_YSIZE; y++)
11183 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11185 for (x = 0; x < min_xsize; x++)
11186 for (y = 0; y < min_ysize; y++)
11187 si->touch.grid_button[i][x][starty + y] =
11188 default_grid_button[y][0][x];
11190 for (x = 0; x < min_xsize; x++)
11191 for (y = 0; y < min_ysize; y++)
11192 si->touch.grid_button[i][startx + x][starty + y] =
11193 default_grid_button[y][1][x];
11196 si->touch.grid_initialized = video.initialized;
11198 si->touch.overlay_buttons = FALSE;
11200 si->editor.el_boulderdash = TRUE;
11201 si->editor.el_boulderdash_native = TRUE;
11202 si->editor.el_emerald_mine = TRUE;
11203 si->editor.el_emerald_mine_club = TRUE;
11204 si->editor.el_more = TRUE;
11205 si->editor.el_sokoban = TRUE;
11206 si->editor.el_supaplex = TRUE;
11207 si->editor.el_diamond_caves = TRUE;
11208 si->editor.el_dx_boulderdash = TRUE;
11210 si->editor.el_mirror_magic = TRUE;
11211 si->editor.el_deflektor = TRUE;
11213 si->editor.el_chars = TRUE;
11214 si->editor.el_steel_chars = TRUE;
11216 si->editor.el_classic = TRUE;
11217 si->editor.el_custom = TRUE;
11219 si->editor.el_user_defined = FALSE;
11220 si->editor.el_dynamic = TRUE;
11222 si->editor.el_headlines = TRUE;
11224 si->editor.show_element_token = FALSE;
11226 si->editor.show_read_only_warning = TRUE;
11228 si->editor.use_template_for_new_levels = TRUE;
11230 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11231 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11232 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11233 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11234 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11236 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11237 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11238 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11239 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11240 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11242 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11243 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11244 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11245 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11246 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11247 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11249 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11250 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11251 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11253 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11254 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11255 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11256 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11258 for (i = 0; i < MAX_PLAYERS; i++)
11260 si->input[i].use_joystick = FALSE;
11261 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11262 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11263 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11264 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11265 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11266 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11267 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11268 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11269 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11270 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11271 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11272 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11273 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11274 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11275 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11278 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11279 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11280 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11281 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11283 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11284 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11285 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11286 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11287 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11288 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11289 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11291 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11293 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11294 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11295 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11297 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11298 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11299 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11301 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11302 si->internal.choose_from_top_leveldir = FALSE;
11303 si->internal.show_scaling_in_title = TRUE;
11304 si->internal.create_user_levelset = TRUE;
11305 si->internal.info_screens_from_main = FALSE;
11307 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11308 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11310 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11311 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11312 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11313 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11314 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11315 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11316 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11317 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11318 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11319 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11321 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11322 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11323 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11324 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11325 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11326 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11327 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11328 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11329 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11330 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11332 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11333 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11335 si->debug.show_frames_per_second = FALSE;
11337 si->debug.xsn_mode = AUTO;
11338 si->debug.xsn_percent = 0;
11340 si->options.verbose = FALSE;
11341 si->options.debug = FALSE;
11342 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11344 #if defined(PLATFORM_ANDROID)
11345 si->fullscreen = TRUE;
11346 si->touch.overlay_buttons = TRUE;
11349 setHideSetupEntry(&setup.debug.xsn_mode);
11352 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11354 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11357 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11359 si->player_uuid = NULL; // (will be set later)
11360 si->player_version = 1; // (will be set later)
11362 si->use_api_server = TRUE;
11363 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11364 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11365 si->ask_for_uploading_tapes = TRUE;
11366 si->ask_for_remaining_tapes = FALSE;
11367 si->provide_uploading_tapes = TRUE;
11368 si->ask_for_using_api_server = TRUE;
11369 si->has_remaining_tapes = FALSE;
11372 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11374 si->editor_cascade.el_bd = TRUE;
11375 si->editor_cascade.el_bd_native = TRUE;
11376 si->editor_cascade.el_em = TRUE;
11377 si->editor_cascade.el_emc = TRUE;
11378 si->editor_cascade.el_rnd = TRUE;
11379 si->editor_cascade.el_sb = TRUE;
11380 si->editor_cascade.el_sp = TRUE;
11381 si->editor_cascade.el_dc = TRUE;
11382 si->editor_cascade.el_dx = TRUE;
11384 si->editor_cascade.el_mm = TRUE;
11385 si->editor_cascade.el_df = TRUE;
11387 si->editor_cascade.el_chars = FALSE;
11388 si->editor_cascade.el_steel_chars = FALSE;
11389 si->editor_cascade.el_ce = FALSE;
11390 si->editor_cascade.el_ge = FALSE;
11391 si->editor_cascade.el_es = FALSE;
11392 si->editor_cascade.el_ref = FALSE;
11393 si->editor_cascade.el_user = FALSE;
11394 si->editor_cascade.el_dynamic = FALSE;
11397 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11399 static char *getHideSetupToken(void *setup_value)
11401 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11403 if (setup_value != NULL)
11404 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11406 return hide_setup_token;
11409 void setHideSetupEntry(void *setup_value)
11411 char *hide_setup_token = getHideSetupToken(setup_value);
11413 if (hide_setup_hash == NULL)
11414 hide_setup_hash = newSetupFileHash();
11416 if (setup_value != NULL)
11417 setHashEntry(hide_setup_hash, hide_setup_token, "");
11420 void removeHideSetupEntry(void *setup_value)
11422 char *hide_setup_token = getHideSetupToken(setup_value);
11424 if (setup_value != NULL)
11425 removeHashEntry(hide_setup_hash, hide_setup_token);
11428 boolean hideSetupEntry(void *setup_value)
11430 char *hide_setup_token = getHideSetupToken(setup_value);
11432 return (setup_value != NULL &&
11433 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11436 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11437 struct TokenInfo *token_info,
11438 int token_nr, char *token_text)
11440 char *token_hide_text = getStringCat2(token_text, ".hide");
11441 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11443 // set the value of this setup option in the setup option structure
11444 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11446 // check if this setup option should be hidden in the setup menu
11447 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11448 setHideSetupEntry(token_info[token_nr].value);
11450 free(token_hide_text);
11453 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11454 struct TokenInfo *token_info,
11457 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11458 token_info[token_nr].text);
11461 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11465 if (!setup_file_hash)
11468 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11469 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11471 setup.touch.grid_initialized = TRUE;
11472 for (i = 0; i < 2; i++)
11474 int grid_xsize = setup.touch.grid_xsize[i];
11475 int grid_ysize = setup.touch.grid_ysize[i];
11478 // if virtual buttons are not loaded from setup file, repeat initializing
11479 // virtual buttons grid with default values later when video is initialized
11480 if (grid_xsize == -1 ||
11483 setup.touch.grid_initialized = FALSE;
11488 for (y = 0; y < grid_ysize; y++)
11490 char token_string[MAX_LINE_LEN];
11492 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11494 char *value_string = getHashEntry(setup_file_hash, token_string);
11496 if (value_string == NULL)
11499 for (x = 0; x < grid_xsize; x++)
11501 char c = value_string[x];
11503 setup.touch.grid_button[i][x][y] =
11504 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11509 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11510 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11512 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11513 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11515 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11519 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11521 setup_input = setup.input[pnr];
11522 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11524 char full_token[100];
11526 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11527 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11530 setup.input[pnr] = setup_input;
11533 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11534 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11536 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11537 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11539 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11540 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11542 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11543 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11545 setHideRelatedSetupEntries();
11548 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11552 if (!setup_file_hash)
11555 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11556 setSetupInfo(auto_setup_tokens, i,
11557 getHashEntry(setup_file_hash,
11558 auto_setup_tokens[i].text));
11561 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11565 if (!setup_file_hash)
11568 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11569 setSetupInfo(server_setup_tokens, i,
11570 getHashEntry(setup_file_hash,
11571 server_setup_tokens[i].text));
11574 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11578 if (!setup_file_hash)
11581 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11582 setSetupInfo(editor_cascade_setup_tokens, i,
11583 getHashEntry(setup_file_hash,
11584 editor_cascade_setup_tokens[i].text));
11587 void LoadUserNames(void)
11589 int last_user_nr = user.nr;
11592 if (global.user_names != NULL)
11594 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11595 checked_free(global.user_names[i]);
11597 checked_free(global.user_names);
11600 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11602 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11606 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11608 if (setup_file_hash)
11610 char *player_name = getHashEntry(setup_file_hash, "player_name");
11612 global.user_names[i] = getFixedUserName(player_name);
11614 freeSetupFileHash(setup_file_hash);
11617 if (global.user_names[i] == NULL)
11618 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11621 user.nr = last_user_nr;
11624 void LoadSetupFromFilename(char *filename)
11626 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11628 if (setup_file_hash)
11630 decodeSetupFileHash_Default(setup_file_hash);
11632 freeSetupFileHash(setup_file_hash);
11636 Debug("setup", "using default setup values");
11640 static void LoadSetup_SpecialPostProcessing(void)
11642 char *player_name_new;
11644 // needed to work around problems with fixed length strings
11645 player_name_new = getFixedUserName(setup.player_name);
11646 free(setup.player_name);
11647 setup.player_name = player_name_new;
11649 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11650 if (setup.scroll_delay == FALSE)
11652 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11653 setup.scroll_delay = TRUE; // now always "on"
11656 // make sure that scroll delay value stays inside valid range
11657 setup.scroll_delay_value =
11658 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11661 void LoadSetup_Default(void)
11665 // always start with reliable default values
11666 setSetupInfoToDefaults(&setup);
11668 // try to load setup values from default setup file
11669 filename = getDefaultSetupFilename();
11671 if (fileExists(filename))
11672 LoadSetupFromFilename(filename);
11674 // try to load setup values from platform setup file
11675 filename = getPlatformSetupFilename();
11677 if (fileExists(filename))
11678 LoadSetupFromFilename(filename);
11680 // try to load setup values from user setup file
11681 filename = getSetupFilename();
11683 LoadSetupFromFilename(filename);
11685 LoadSetup_SpecialPostProcessing();
11688 void LoadSetup_AutoSetup(void)
11690 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11691 SetupFileHash *setup_file_hash = NULL;
11693 // always start with reliable default values
11694 setSetupInfoToDefaults_AutoSetup(&setup);
11696 setup_file_hash = loadSetupFileHash(filename);
11698 if (setup_file_hash)
11700 decodeSetupFileHash_AutoSetup(setup_file_hash);
11702 freeSetupFileHash(setup_file_hash);
11708 void LoadSetup_ServerSetup(void)
11710 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11711 SetupFileHash *setup_file_hash = NULL;
11713 // always start with reliable default values
11714 setSetupInfoToDefaults_ServerSetup(&setup);
11716 setup_file_hash = loadSetupFileHash(filename);
11718 if (setup_file_hash)
11720 decodeSetupFileHash_ServerSetup(setup_file_hash);
11722 freeSetupFileHash(setup_file_hash);
11727 if (setup.player_uuid == NULL)
11729 // player UUID does not yet exist in setup file
11730 setup.player_uuid = getStringCopy(getUUID());
11731 setup.player_version = 2;
11733 SaveSetup_ServerSetup();
11737 void LoadSetup_EditorCascade(void)
11739 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11740 SetupFileHash *setup_file_hash = NULL;
11742 // always start with reliable default values
11743 setSetupInfoToDefaults_EditorCascade(&setup);
11745 setup_file_hash = loadSetupFileHash(filename);
11747 if (setup_file_hash)
11749 decodeSetupFileHash_EditorCascade(setup_file_hash);
11751 freeSetupFileHash(setup_file_hash);
11757 void LoadSetup(void)
11759 LoadSetup_Default();
11760 LoadSetup_AutoSetup();
11761 LoadSetup_ServerSetup();
11762 LoadSetup_EditorCascade();
11765 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11766 char *mapping_line)
11768 char mapping_guid[MAX_LINE_LEN];
11769 char *mapping_start, *mapping_end;
11771 // get GUID from game controller mapping line: copy complete line
11772 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11773 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11775 // get GUID from game controller mapping line: cut after GUID part
11776 mapping_start = strchr(mapping_guid, ',');
11777 if (mapping_start != NULL)
11778 *mapping_start = '\0';
11780 // cut newline from game controller mapping line
11781 mapping_end = strchr(mapping_line, '\n');
11782 if (mapping_end != NULL)
11783 *mapping_end = '\0';
11785 // add mapping entry to game controller mappings hash
11786 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11789 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11794 if (!(file = fopen(filename, MODE_READ)))
11796 Warn("cannot read game controller mappings file '%s'", filename);
11801 while (!feof(file))
11803 char line[MAX_LINE_LEN];
11805 if (!fgets(line, MAX_LINE_LEN, file))
11808 addGameControllerMappingToHash(mappings_hash, line);
11814 void SaveSetup_Default(void)
11816 char *filename = getSetupFilename();
11820 InitUserDataDirectory();
11822 if (!(file = fopen(filename, MODE_WRITE)))
11824 Warn("cannot write setup file '%s'", filename);
11829 fprintFileHeader(file, SETUP_FILENAME);
11831 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11833 // just to make things nicer :)
11834 if (global_setup_tokens[i].value == &setup.multiple_users ||
11835 global_setup_tokens[i].value == &setup.sound ||
11836 global_setup_tokens[i].value == &setup.graphics_set ||
11837 global_setup_tokens[i].value == &setup.volume_simple ||
11838 global_setup_tokens[i].value == &setup.network_mode ||
11839 global_setup_tokens[i].value == &setup.touch.control_type ||
11840 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11841 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11842 fprintf(file, "\n");
11844 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11847 for (i = 0; i < 2; i++)
11849 int grid_xsize = setup.touch.grid_xsize[i];
11850 int grid_ysize = setup.touch.grid_ysize[i];
11853 fprintf(file, "\n");
11855 for (y = 0; y < grid_ysize; y++)
11857 char token_string[MAX_LINE_LEN];
11858 char value_string[MAX_LINE_LEN];
11860 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11862 for (x = 0; x < grid_xsize; x++)
11864 char c = setup.touch.grid_button[i][x][y];
11866 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11869 value_string[grid_xsize] = '\0';
11871 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11875 fprintf(file, "\n");
11876 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11877 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11879 fprintf(file, "\n");
11880 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11881 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11883 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11887 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11888 fprintf(file, "\n");
11890 setup_input = setup.input[pnr];
11891 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11892 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11895 fprintf(file, "\n");
11896 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11897 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11899 // (internal setup values not saved to user setup file)
11901 fprintf(file, "\n");
11902 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11903 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11904 setup.debug.xsn_mode != AUTO)
11905 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11907 fprintf(file, "\n");
11908 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11909 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11913 SetFilePermissions(filename, PERMS_PRIVATE);
11916 void SaveSetup_AutoSetup(void)
11918 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11922 InitUserDataDirectory();
11924 if (!(file = fopen(filename, MODE_WRITE)))
11926 Warn("cannot write auto setup file '%s'", filename);
11933 fprintFileHeader(file, AUTOSETUP_FILENAME);
11935 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11936 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11940 SetFilePermissions(filename, PERMS_PRIVATE);
11945 void SaveSetup_ServerSetup(void)
11947 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11951 InitUserDataDirectory();
11953 if (!(file = fopen(filename, MODE_WRITE)))
11955 Warn("cannot write server setup file '%s'", filename);
11962 fprintFileHeader(file, SERVERSETUP_FILENAME);
11964 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11966 // just to make things nicer :)
11967 if (server_setup_tokens[i].value == &setup.use_api_server)
11968 fprintf(file, "\n");
11970 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11975 SetFilePermissions(filename, PERMS_PRIVATE);
11980 void SaveSetup_EditorCascade(void)
11982 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11986 InitUserDataDirectory();
11988 if (!(file = fopen(filename, MODE_WRITE)))
11990 Warn("cannot write editor cascade state file '%s'", filename);
11997 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11999 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12000 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12004 SetFilePermissions(filename, PERMS_PRIVATE);
12009 void SaveSetup(void)
12011 SaveSetup_Default();
12012 SaveSetup_AutoSetup();
12013 SaveSetup_ServerSetup();
12014 SaveSetup_EditorCascade();
12017 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12022 if (!(file = fopen(filename, MODE_WRITE)))
12024 Warn("cannot write game controller mappings file '%s'", filename);
12029 BEGIN_HASH_ITERATION(mappings_hash, itr)
12031 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12033 END_HASH_ITERATION(mappings_hash, itr)
12038 void SaveSetup_AddGameControllerMapping(char *mapping)
12040 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12041 SetupFileHash *mappings_hash = newSetupFileHash();
12043 InitUserDataDirectory();
12045 // load existing personal game controller mappings
12046 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12048 // add new mapping to personal game controller mappings
12049 addGameControllerMappingToHash(mappings_hash, mapping);
12051 // save updated personal game controller mappings
12052 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12054 freeSetupFileHash(mappings_hash);
12058 void LoadCustomElementDescriptions(void)
12060 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12061 SetupFileHash *setup_file_hash;
12064 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12066 if (element_info[i].custom_description != NULL)
12068 free(element_info[i].custom_description);
12069 element_info[i].custom_description = NULL;
12073 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12076 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12078 char *token = getStringCat2(element_info[i].token_name, ".name");
12079 char *value = getHashEntry(setup_file_hash, token);
12082 element_info[i].custom_description = getStringCopy(value);
12087 freeSetupFileHash(setup_file_hash);
12090 static int getElementFromToken(char *token)
12092 char *value = getHashEntry(element_token_hash, token);
12095 return atoi(value);
12097 Warn("unknown element token '%s'", token);
12099 return EL_UNDEFINED;
12102 void FreeGlobalAnimEventInfo(void)
12104 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12106 if (gaei->event_list == NULL)
12111 for (i = 0; i < gaei->num_event_lists; i++)
12113 checked_free(gaei->event_list[i]->event_value);
12114 checked_free(gaei->event_list[i]);
12117 checked_free(gaei->event_list);
12119 gaei->event_list = NULL;
12120 gaei->num_event_lists = 0;
12123 static int AddGlobalAnimEventList(void)
12125 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12126 int list_pos = gaei->num_event_lists++;
12128 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12129 sizeof(struct GlobalAnimEventListInfo *));
12131 gaei->event_list[list_pos] =
12132 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12134 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12136 gaeli->event_value = NULL;
12137 gaeli->num_event_values = 0;
12142 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12144 // do not add empty global animation events
12145 if (event_value == ANIM_EVENT_NONE)
12148 // if list position is undefined, create new list
12149 if (list_pos == ANIM_EVENT_UNDEFINED)
12150 list_pos = AddGlobalAnimEventList();
12152 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12153 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12154 int value_pos = gaeli->num_event_values++;
12156 gaeli->event_value = checked_realloc(gaeli->event_value,
12157 gaeli->num_event_values * sizeof(int *));
12159 gaeli->event_value[value_pos] = event_value;
12164 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12166 if (list_pos == ANIM_EVENT_UNDEFINED)
12167 return ANIM_EVENT_NONE;
12169 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12170 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12172 return gaeli->event_value[value_pos];
12175 int GetGlobalAnimEventValueCount(int list_pos)
12177 if (list_pos == ANIM_EVENT_UNDEFINED)
12180 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12181 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12183 return gaeli->num_event_values;
12186 // This function checks if a string <s> of the format "string1, string2, ..."
12187 // exactly contains a string <s_contained>.
12189 static boolean string_has_parameter(char *s, char *s_contained)
12193 if (s == NULL || s_contained == NULL)
12196 if (strlen(s_contained) > strlen(s))
12199 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12201 char next_char = s[strlen(s_contained)];
12203 // check if next character is delimiter or whitespace
12204 if (next_char == ',' || next_char == '\0' ||
12205 next_char == ' ' || next_char == '\t')
12209 // check if string contains another parameter string after a comma
12210 substring = strchr(s, ',');
12211 if (substring == NULL) // string does not contain a comma
12214 // advance string pointer to next character after the comma
12217 // skip potential whitespaces after the comma
12218 while (*substring == ' ' || *substring == '\t')
12221 return string_has_parameter(substring, s_contained);
12224 static int get_anim_parameter_value_ce(char *s)
12227 char *pattern_1 = "ce_change:custom_";
12228 char *pattern_2 = ".page_";
12229 int pattern_1_len = strlen(pattern_1);
12230 char *matching_char = strstr(s_ptr, pattern_1);
12231 int result = ANIM_EVENT_NONE;
12233 if (matching_char == NULL)
12234 return ANIM_EVENT_NONE;
12236 result = ANIM_EVENT_CE_CHANGE;
12238 s_ptr = matching_char + pattern_1_len;
12240 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12241 if (*s_ptr >= '0' && *s_ptr <= '9')
12243 int gic_ce_nr = (*s_ptr++ - '0');
12245 if (*s_ptr >= '0' && *s_ptr <= '9')
12247 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12249 if (*s_ptr >= '0' && *s_ptr <= '9')
12250 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12253 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12254 return ANIM_EVENT_NONE;
12256 // custom element stored as 0 to 255
12259 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12263 // invalid custom element number specified
12265 return ANIM_EVENT_NONE;
12268 // check for change page number ("page_X" or "page_XX") (optional)
12269 if (strPrefix(s_ptr, pattern_2))
12271 s_ptr += strlen(pattern_2);
12273 if (*s_ptr >= '0' && *s_ptr <= '9')
12275 int gic_page_nr = (*s_ptr++ - '0');
12277 if (*s_ptr >= '0' && *s_ptr <= '9')
12278 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12280 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12281 return ANIM_EVENT_NONE;
12283 // change page stored as 1 to 32 (0 means "all change pages")
12285 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12289 // invalid animation part number specified
12291 return ANIM_EVENT_NONE;
12295 // discard result if next character is neither delimiter nor whitespace
12296 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12297 *s_ptr == ' ' || *s_ptr == '\t'))
12298 return ANIM_EVENT_NONE;
12303 static int get_anim_parameter_value(char *s)
12305 int event_value[] =
12313 char *pattern_1[] =
12321 char *pattern_2 = ".part_";
12322 char *matching_char = NULL;
12324 int pattern_1_len = 0;
12325 int result = ANIM_EVENT_NONE;
12328 result = get_anim_parameter_value_ce(s);
12330 if (result != ANIM_EVENT_NONE)
12333 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12335 matching_char = strstr(s_ptr, pattern_1[i]);
12336 pattern_1_len = strlen(pattern_1[i]);
12337 result = event_value[i];
12339 if (matching_char != NULL)
12343 if (matching_char == NULL)
12344 return ANIM_EVENT_NONE;
12346 s_ptr = matching_char + pattern_1_len;
12348 // check for main animation number ("anim_X" or "anim_XX")
12349 if (*s_ptr >= '0' && *s_ptr <= '9')
12351 int gic_anim_nr = (*s_ptr++ - '0');
12353 if (*s_ptr >= '0' && *s_ptr <= '9')
12354 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12356 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12357 return ANIM_EVENT_NONE;
12359 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12363 // invalid main animation number specified
12365 return ANIM_EVENT_NONE;
12368 // check for animation part number ("part_X" or "part_XX") (optional)
12369 if (strPrefix(s_ptr, pattern_2))
12371 s_ptr += strlen(pattern_2);
12373 if (*s_ptr >= '0' && *s_ptr <= '9')
12375 int gic_part_nr = (*s_ptr++ - '0');
12377 if (*s_ptr >= '0' && *s_ptr <= '9')
12378 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12380 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12381 return ANIM_EVENT_NONE;
12383 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12387 // invalid animation part number specified
12389 return ANIM_EVENT_NONE;
12393 // discard result if next character is neither delimiter nor whitespace
12394 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12395 *s_ptr == ' ' || *s_ptr == '\t'))
12396 return ANIM_EVENT_NONE;
12401 static int get_anim_parameter_values(char *s)
12403 int list_pos = ANIM_EVENT_UNDEFINED;
12404 int event_value = ANIM_EVENT_DEFAULT;
12406 if (string_has_parameter(s, "any"))
12407 event_value |= ANIM_EVENT_ANY;
12409 if (string_has_parameter(s, "click:self") ||
12410 string_has_parameter(s, "click") ||
12411 string_has_parameter(s, "self"))
12412 event_value |= ANIM_EVENT_SELF;
12414 if (string_has_parameter(s, "unclick:any"))
12415 event_value |= ANIM_EVENT_UNCLICK_ANY;
12417 // if animation event found, add it to global animation event list
12418 if (event_value != ANIM_EVENT_NONE)
12419 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12423 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12424 event_value = get_anim_parameter_value(s);
12426 // if animation event found, add it to global animation event list
12427 if (event_value != ANIM_EVENT_NONE)
12428 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12430 // continue with next part of the string, starting with next comma
12431 s = strchr(s + 1, ',');
12437 static int get_anim_action_parameter_value(char *token)
12439 // check most common default case first to massively speed things up
12440 if (strEqual(token, ARG_UNDEFINED))
12441 return ANIM_EVENT_ACTION_NONE;
12443 int result = getImageIDFromToken(token);
12447 char *gfx_token = getStringCat2("gfx.", token);
12449 result = getImageIDFromToken(gfx_token);
12451 checked_free(gfx_token);
12456 Key key = getKeyFromX11KeyName(token);
12458 if (key != KSYM_UNDEFINED)
12459 result = -(int)key;
12466 result = get_hash_from_string(token); // unsigned int => int
12467 result = ABS(result); // may be negative now
12468 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12470 setHashEntry(anim_url_hash, int2str(result, 0), token);
12475 result = ANIM_EVENT_ACTION_NONE;
12480 int get_parameter_value(char *value_raw, char *suffix, int type)
12482 char *value = getStringToLower(value_raw);
12483 int result = 0; // probably a save default value
12485 if (strEqual(suffix, ".direction"))
12487 result = (strEqual(value, "left") ? MV_LEFT :
12488 strEqual(value, "right") ? MV_RIGHT :
12489 strEqual(value, "up") ? MV_UP :
12490 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12492 else if (strEqual(suffix, ".position"))
12494 result = (strEqual(value, "left") ? POS_LEFT :
12495 strEqual(value, "right") ? POS_RIGHT :
12496 strEqual(value, "top") ? POS_TOP :
12497 strEqual(value, "upper") ? POS_UPPER :
12498 strEqual(value, "middle") ? POS_MIDDLE :
12499 strEqual(value, "lower") ? POS_LOWER :
12500 strEqual(value, "bottom") ? POS_BOTTOM :
12501 strEqual(value, "any") ? POS_ANY :
12502 strEqual(value, "ce") ? POS_CE :
12503 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12504 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12506 else if (strEqual(suffix, ".align"))
12508 result = (strEqual(value, "left") ? ALIGN_LEFT :
12509 strEqual(value, "right") ? ALIGN_RIGHT :
12510 strEqual(value, "center") ? ALIGN_CENTER :
12511 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12513 else if (strEqual(suffix, ".valign"))
12515 result = (strEqual(value, "top") ? VALIGN_TOP :
12516 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12517 strEqual(value, "middle") ? VALIGN_MIDDLE :
12518 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12520 else if (strEqual(suffix, ".anim_mode"))
12522 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12523 string_has_parameter(value, "loop") ? ANIM_LOOP :
12524 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12525 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12526 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12527 string_has_parameter(value, "random") ? ANIM_RANDOM :
12528 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12529 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12530 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12531 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12532 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12533 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12534 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12535 string_has_parameter(value, "all") ? ANIM_ALL :
12536 string_has_parameter(value, "tiled") ? ANIM_TILED :
12537 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12540 if (string_has_parameter(value, "once"))
12541 result |= ANIM_ONCE;
12543 if (string_has_parameter(value, "reverse"))
12544 result |= ANIM_REVERSE;
12546 if (string_has_parameter(value, "opaque_player"))
12547 result |= ANIM_OPAQUE_PLAYER;
12549 if (string_has_parameter(value, "static_panel"))
12550 result |= ANIM_STATIC_PANEL;
12552 else if (strEqual(suffix, ".init_event") ||
12553 strEqual(suffix, ".anim_event"))
12555 result = get_anim_parameter_values(value);
12557 else if (strEqual(suffix, ".init_delay_action") ||
12558 strEqual(suffix, ".anim_delay_action") ||
12559 strEqual(suffix, ".post_delay_action") ||
12560 strEqual(suffix, ".init_event_action") ||
12561 strEqual(suffix, ".anim_event_action"))
12563 result = get_anim_action_parameter_value(value_raw);
12565 else if (strEqual(suffix, ".class"))
12567 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12568 get_hash_from_string(value));
12570 else if (strEqual(suffix, ".style"))
12572 result = STYLE_DEFAULT;
12574 if (string_has_parameter(value, "accurate_borders"))
12575 result |= STYLE_ACCURATE_BORDERS;
12577 if (string_has_parameter(value, "inner_corners"))
12578 result |= STYLE_INNER_CORNERS;
12580 if (string_has_parameter(value, "reverse"))
12581 result |= STYLE_REVERSE;
12583 if (string_has_parameter(value, "leftmost_position"))
12584 result |= STYLE_LEFTMOST_POSITION;
12586 if (string_has_parameter(value, "block_clicks"))
12587 result |= STYLE_BLOCK;
12589 if (string_has_parameter(value, "passthrough_clicks"))
12590 result |= STYLE_PASSTHROUGH;
12592 if (string_has_parameter(value, "multiple_actions"))
12593 result |= STYLE_MULTIPLE_ACTIONS;
12595 if (string_has_parameter(value, "consume_ce_event"))
12596 result |= STYLE_CONSUME_CE_EVENT;
12598 else if (strEqual(suffix, ".fade_mode"))
12600 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12601 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12602 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12603 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12604 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12605 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12606 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12607 FADE_MODE_DEFAULT);
12609 else if (strEqual(suffix, ".auto_delay_unit"))
12611 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12612 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12613 AUTO_DELAY_UNIT_DEFAULT);
12615 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12617 result = gfx.get_font_from_token_function(value);
12619 else // generic parameter of type integer or boolean
12621 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12622 type == TYPE_INTEGER ? get_integer_from_string(value) :
12623 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12624 ARG_UNDEFINED_VALUE);
12632 static int get_token_parameter_value(char *token, char *value_raw)
12636 if (token == NULL || value_raw == NULL)
12637 return ARG_UNDEFINED_VALUE;
12639 suffix = strrchr(token, '.');
12640 if (suffix == NULL)
12643 if (strEqual(suffix, ".element"))
12644 return getElementFromToken(value_raw);
12646 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12647 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12650 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12651 boolean ignore_defaults)
12655 for (i = 0; image_config_vars[i].token != NULL; i++)
12657 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12659 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12660 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12664 *image_config_vars[i].value =
12665 get_token_parameter_value(image_config_vars[i].token, value);
12669 void InitMenuDesignSettings_Static(void)
12671 // always start with reliable default values from static default config
12672 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12675 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12679 // the following initializes hierarchical values from static configuration
12681 // special case: initialize "ARG_DEFAULT" values in static default config
12682 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12683 titlescreen_initial_first_default.fade_mode =
12684 title_initial_first_default.fade_mode;
12685 titlescreen_initial_first_default.fade_delay =
12686 title_initial_first_default.fade_delay;
12687 titlescreen_initial_first_default.post_delay =
12688 title_initial_first_default.post_delay;
12689 titlescreen_initial_first_default.auto_delay =
12690 title_initial_first_default.auto_delay;
12691 titlescreen_initial_first_default.auto_delay_unit =
12692 title_initial_first_default.auto_delay_unit;
12693 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12694 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12695 titlescreen_first_default.post_delay = title_first_default.post_delay;
12696 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12697 titlescreen_first_default.auto_delay_unit =
12698 title_first_default.auto_delay_unit;
12699 titlemessage_initial_first_default.fade_mode =
12700 title_initial_first_default.fade_mode;
12701 titlemessage_initial_first_default.fade_delay =
12702 title_initial_first_default.fade_delay;
12703 titlemessage_initial_first_default.post_delay =
12704 title_initial_first_default.post_delay;
12705 titlemessage_initial_first_default.auto_delay =
12706 title_initial_first_default.auto_delay;
12707 titlemessage_initial_first_default.auto_delay_unit =
12708 title_initial_first_default.auto_delay_unit;
12709 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12710 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12711 titlemessage_first_default.post_delay = title_first_default.post_delay;
12712 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12713 titlemessage_first_default.auto_delay_unit =
12714 title_first_default.auto_delay_unit;
12716 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12717 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12718 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12719 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12720 titlescreen_initial_default.auto_delay_unit =
12721 title_initial_default.auto_delay_unit;
12722 titlescreen_default.fade_mode = title_default.fade_mode;
12723 titlescreen_default.fade_delay = title_default.fade_delay;
12724 titlescreen_default.post_delay = title_default.post_delay;
12725 titlescreen_default.auto_delay = title_default.auto_delay;
12726 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12727 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12728 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12729 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12730 titlemessage_initial_default.auto_delay_unit =
12731 title_initial_default.auto_delay_unit;
12732 titlemessage_default.fade_mode = title_default.fade_mode;
12733 titlemessage_default.fade_delay = title_default.fade_delay;
12734 titlemessage_default.post_delay = title_default.post_delay;
12735 titlemessage_default.auto_delay = title_default.auto_delay;
12736 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12738 // special case: initialize "ARG_DEFAULT" values in static default config
12739 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12740 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12742 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12743 titlescreen_first[i] = titlescreen_first_default;
12744 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12745 titlemessage_first[i] = titlemessage_first_default;
12747 titlescreen_initial[i] = titlescreen_initial_default;
12748 titlescreen[i] = titlescreen_default;
12749 titlemessage_initial[i] = titlemessage_initial_default;
12750 titlemessage[i] = titlemessage_default;
12753 // special case: initialize "ARG_DEFAULT" values in static default config
12754 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12755 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12757 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12760 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12761 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12762 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12765 // special case: initialize "ARG_DEFAULT" values in static default config
12766 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12767 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12769 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12770 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12771 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12773 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12776 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12780 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12784 struct XY *dst, *src;
12786 game_buttons_xy[] =
12788 { &game.button.save, &game.button.stop },
12789 { &game.button.pause2, &game.button.pause },
12790 { &game.button.load, &game.button.play },
12791 { &game.button.undo, &game.button.stop },
12792 { &game.button.redo, &game.button.play },
12798 // special case: initialize later added SETUP list size from LEVELS value
12799 if (menu.list_size[GAME_MODE_SETUP] == -1)
12800 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12802 // set default position for snapshot buttons to stop/pause/play buttons
12803 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12804 if ((*game_buttons_xy[i].dst).x == -1 &&
12805 (*game_buttons_xy[i].dst).y == -1)
12806 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12808 // --------------------------------------------------------------------------
12809 // dynamic viewports (including playfield margins, borders and alignments)
12810 // --------------------------------------------------------------------------
12812 // dynamic viewports currently only supported for landscape mode
12813 int display_width = MAX(video.display_width, video.display_height);
12814 int display_height = MIN(video.display_width, video.display_height);
12816 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12818 struct RectWithBorder *vp_window = &viewport.window[i];
12819 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12820 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12821 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12822 boolean dynamic_window_width = (vp_window->min_width != -1);
12823 boolean dynamic_window_height = (vp_window->min_height != -1);
12824 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12825 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12827 // adjust window size if min/max width/height is specified
12829 if (vp_window->min_width != -1)
12831 int window_width = display_width;
12833 // when using static window height, use aspect ratio of display
12834 if (vp_window->min_height == -1)
12835 window_width = vp_window->height * display_width / display_height;
12837 vp_window->width = MAX(vp_window->min_width, window_width);
12840 if (vp_window->min_height != -1)
12842 int window_height = display_height;
12844 // when using static window width, use aspect ratio of display
12845 if (vp_window->min_width == -1)
12846 window_height = vp_window->width * display_height / display_width;
12848 vp_window->height = MAX(vp_window->min_height, window_height);
12851 if (vp_window->max_width != -1)
12852 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12854 if (vp_window->max_height != -1)
12855 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12857 int playfield_width = vp_window->width;
12858 int playfield_height = vp_window->height;
12860 // adjust playfield size and position according to specified margins
12862 playfield_width -= vp_playfield->margin_left;
12863 playfield_width -= vp_playfield->margin_right;
12865 playfield_height -= vp_playfield->margin_top;
12866 playfield_height -= vp_playfield->margin_bottom;
12868 // adjust playfield size if min/max width/height is specified
12870 if (vp_playfield->min_width != -1)
12871 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12873 if (vp_playfield->min_height != -1)
12874 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12876 if (vp_playfield->max_width != -1)
12877 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12879 if (vp_playfield->max_height != -1)
12880 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12882 // adjust playfield position according to specified alignment
12884 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12885 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12886 else if (vp_playfield->align == ALIGN_CENTER)
12887 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12888 else if (vp_playfield->align == ALIGN_RIGHT)
12889 vp_playfield->x += playfield_width - vp_playfield->width;
12891 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12892 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12893 else if (vp_playfield->valign == VALIGN_MIDDLE)
12894 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12895 else if (vp_playfield->valign == VALIGN_BOTTOM)
12896 vp_playfield->y += playfield_height - vp_playfield->height;
12898 vp_playfield->x += vp_playfield->margin_left;
12899 vp_playfield->y += vp_playfield->margin_top;
12901 // adjust individual playfield borders if only default border is specified
12903 if (vp_playfield->border_left == -1)
12904 vp_playfield->border_left = vp_playfield->border_size;
12905 if (vp_playfield->border_right == -1)
12906 vp_playfield->border_right = vp_playfield->border_size;
12907 if (vp_playfield->border_top == -1)
12908 vp_playfield->border_top = vp_playfield->border_size;
12909 if (vp_playfield->border_bottom == -1)
12910 vp_playfield->border_bottom = vp_playfield->border_size;
12912 // set dynamic playfield borders if borders are specified as undefined
12913 // (but only if window size was dynamic and playfield size was static)
12915 if (dynamic_window_width && !dynamic_playfield_width)
12917 if (vp_playfield->border_left == -1)
12919 vp_playfield->border_left = (vp_playfield->x -
12920 vp_playfield->margin_left);
12921 vp_playfield->x -= vp_playfield->border_left;
12922 vp_playfield->width += vp_playfield->border_left;
12925 if (vp_playfield->border_right == -1)
12927 vp_playfield->border_right = (vp_window->width -
12929 vp_playfield->width -
12930 vp_playfield->margin_right);
12931 vp_playfield->width += vp_playfield->border_right;
12935 if (dynamic_window_height && !dynamic_playfield_height)
12937 if (vp_playfield->border_top == -1)
12939 vp_playfield->border_top = (vp_playfield->y -
12940 vp_playfield->margin_top);
12941 vp_playfield->y -= vp_playfield->border_top;
12942 vp_playfield->height += vp_playfield->border_top;
12945 if (vp_playfield->border_bottom == -1)
12947 vp_playfield->border_bottom = (vp_window->height -
12949 vp_playfield->height -
12950 vp_playfield->margin_bottom);
12951 vp_playfield->height += vp_playfield->border_bottom;
12955 // adjust playfield size to be a multiple of a defined alignment tile size
12957 int align_size = vp_playfield->align_size;
12958 int playfield_xtiles = vp_playfield->width / align_size;
12959 int playfield_ytiles = vp_playfield->height / align_size;
12960 int playfield_width_corrected = playfield_xtiles * align_size;
12961 int playfield_height_corrected = playfield_ytiles * align_size;
12962 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12963 i == GFX_SPECIAL_ARG_EDITOR);
12965 if (is_playfield_mode &&
12966 dynamic_playfield_width &&
12967 vp_playfield->width != playfield_width_corrected)
12969 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12971 vp_playfield->width = playfield_width_corrected;
12973 if (vp_playfield->align == ALIGN_LEFT)
12975 vp_playfield->border_left += playfield_xdiff;
12977 else if (vp_playfield->align == ALIGN_RIGHT)
12979 vp_playfield->border_right += playfield_xdiff;
12981 else if (vp_playfield->align == ALIGN_CENTER)
12983 int border_left_diff = playfield_xdiff / 2;
12984 int border_right_diff = playfield_xdiff - border_left_diff;
12986 vp_playfield->border_left += border_left_diff;
12987 vp_playfield->border_right += border_right_diff;
12991 if (is_playfield_mode &&
12992 dynamic_playfield_height &&
12993 vp_playfield->height != playfield_height_corrected)
12995 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12997 vp_playfield->height = playfield_height_corrected;
12999 if (vp_playfield->valign == VALIGN_TOP)
13001 vp_playfield->border_top += playfield_ydiff;
13003 else if (vp_playfield->align == VALIGN_BOTTOM)
13005 vp_playfield->border_right += playfield_ydiff;
13007 else if (vp_playfield->align == VALIGN_MIDDLE)
13009 int border_top_diff = playfield_ydiff / 2;
13010 int border_bottom_diff = playfield_ydiff - border_top_diff;
13012 vp_playfield->border_top += border_top_diff;
13013 vp_playfield->border_bottom += border_bottom_diff;
13017 // adjust door positions according to specified alignment
13019 for (j = 0; j < 2; j++)
13021 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13023 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13024 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13025 else if (vp_door->align == ALIGN_CENTER)
13026 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13027 else if (vp_door->align == ALIGN_RIGHT)
13028 vp_door->x += vp_window->width - vp_door->width;
13030 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13031 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13032 else if (vp_door->valign == VALIGN_MIDDLE)
13033 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13034 else if (vp_door->valign == VALIGN_BOTTOM)
13035 vp_door->y += vp_window->height - vp_door->height;
13040 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13044 struct XYTileSize *dst, *src;
13047 editor_buttons_xy[] =
13050 &editor.button.element_left, &editor.palette.element_left,
13051 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13054 &editor.button.element_middle, &editor.palette.element_middle,
13055 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13058 &editor.button.element_right, &editor.palette.element_right,
13059 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13066 // set default position for element buttons to element graphics
13067 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13069 if ((*editor_buttons_xy[i].dst).x == -1 &&
13070 (*editor_buttons_xy[i].dst).y == -1)
13072 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13074 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13076 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13080 // adjust editor palette rows and columns if specified to be dynamic
13082 if (editor.palette.cols == -1)
13084 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13085 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13086 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13088 editor.palette.cols = (vp_width - sc_width) / bt_width;
13090 if (editor.palette.x == -1)
13092 int palette_width = editor.palette.cols * bt_width + sc_width;
13094 editor.palette.x = (vp_width - palette_width) / 2;
13098 if (editor.palette.rows == -1)
13100 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13101 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13102 int tx_height = getFontHeight(FONT_TEXT_2);
13104 editor.palette.rows = (vp_height - tx_height) / bt_height;
13106 if (editor.palette.y == -1)
13108 int palette_height = editor.palette.rows * bt_height + tx_height;
13110 editor.palette.y = (vp_height - palette_height) / 2;
13115 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13116 boolean initialize)
13118 // special case: check if network and preview player positions are redefined,
13119 // to compare this later against the main menu level preview being redefined
13120 struct TokenIntPtrInfo menu_config_players[] =
13122 { "main.network_players.x", &menu.main.network_players.redefined },
13123 { "main.network_players.y", &menu.main.network_players.redefined },
13124 { "main.preview_players.x", &menu.main.preview_players.redefined },
13125 { "main.preview_players.y", &menu.main.preview_players.redefined },
13126 { "preview.x", &preview.redefined },
13127 { "preview.y", &preview.redefined }
13133 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13134 *menu_config_players[i].value = FALSE;
13138 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13139 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13140 *menu_config_players[i].value = TRUE;
13144 static void InitMenuDesignSettings_PreviewPlayers(void)
13146 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13149 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13151 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13154 static void LoadMenuDesignSettingsFromFilename(char *filename)
13156 static struct TitleFadingInfo tfi;
13157 static struct TitleMessageInfo tmi;
13158 static struct TokenInfo title_tokens[] =
13160 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13161 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13162 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13163 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13164 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13168 static struct TokenInfo titlemessage_tokens[] =
13170 { TYPE_INTEGER, &tmi.x, ".x" },
13171 { TYPE_INTEGER, &tmi.y, ".y" },
13172 { TYPE_INTEGER, &tmi.width, ".width" },
13173 { TYPE_INTEGER, &tmi.height, ".height" },
13174 { TYPE_INTEGER, &tmi.chars, ".chars" },
13175 { TYPE_INTEGER, &tmi.lines, ".lines" },
13176 { TYPE_INTEGER, &tmi.align, ".align" },
13177 { TYPE_INTEGER, &tmi.valign, ".valign" },
13178 { TYPE_INTEGER, &tmi.font, ".font" },
13179 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13180 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13181 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13182 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13183 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13184 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13185 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13186 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13187 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13193 struct TitleFadingInfo *info;
13198 // initialize first titles from "enter screen" definitions, if defined
13199 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13200 { &title_first_default, "menu.enter_screen.TITLE" },
13202 // initialize title screens from "next screen" definitions, if defined
13203 { &title_initial_default, "menu.next_screen.TITLE" },
13204 { &title_default, "menu.next_screen.TITLE" },
13210 struct TitleMessageInfo *array;
13213 titlemessage_arrays[] =
13215 // initialize first titles from "enter screen" definitions, if defined
13216 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13217 { titlescreen_first, "menu.enter_screen.TITLE" },
13218 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13219 { titlemessage_first, "menu.enter_screen.TITLE" },
13221 // initialize titles from "next screen" definitions, if defined
13222 { titlescreen_initial, "menu.next_screen.TITLE" },
13223 { titlescreen, "menu.next_screen.TITLE" },
13224 { titlemessage_initial, "menu.next_screen.TITLE" },
13225 { titlemessage, "menu.next_screen.TITLE" },
13227 // overwrite titles with title definitions, if defined
13228 { titlescreen_initial_first, "[title_initial]" },
13229 { titlescreen_first, "[title]" },
13230 { titlemessage_initial_first, "[title_initial]" },
13231 { titlemessage_first, "[title]" },
13233 { titlescreen_initial, "[title_initial]" },
13234 { titlescreen, "[title]" },
13235 { titlemessage_initial, "[title_initial]" },
13236 { titlemessage, "[title]" },
13238 // overwrite titles with title screen/message definitions, if defined
13239 { titlescreen_initial_first, "[titlescreen_initial]" },
13240 { titlescreen_first, "[titlescreen]" },
13241 { titlemessage_initial_first, "[titlemessage_initial]" },
13242 { titlemessage_first, "[titlemessage]" },
13244 { titlescreen_initial, "[titlescreen_initial]" },
13245 { titlescreen, "[titlescreen]" },
13246 { titlemessage_initial, "[titlemessage_initial]" },
13247 { titlemessage, "[titlemessage]" },
13251 SetupFileHash *setup_file_hash;
13254 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13257 // the following initializes hierarchical values from dynamic configuration
13259 // special case: initialize with default values that may be overwritten
13260 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13261 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13263 struct TokenIntPtrInfo menu_config[] =
13265 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13266 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13267 { "menu.list_size", &menu.list_size[i] }
13270 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13272 char *token = menu_config[j].token;
13273 char *value = getHashEntry(setup_file_hash, token);
13276 *menu_config[j].value = get_integer_from_string(value);
13280 // special case: initialize with default values that may be overwritten
13281 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13282 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13284 struct TokenIntPtrInfo menu_config[] =
13286 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13287 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13288 { "menu.list_size.INFO", &menu.list_size_info[i] },
13289 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13290 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13293 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13295 char *token = menu_config[j].token;
13296 char *value = getHashEntry(setup_file_hash, token);
13299 *menu_config[j].value = get_integer_from_string(value);
13303 // special case: initialize with default values that may be overwritten
13304 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13305 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13307 struct TokenIntPtrInfo menu_config[] =
13309 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13310 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13313 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13315 char *token = menu_config[j].token;
13316 char *value = getHashEntry(setup_file_hash, token);
13319 *menu_config[j].value = get_integer_from_string(value);
13323 // special case: initialize with default values that may be overwritten
13324 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13325 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13327 struct TokenIntPtrInfo menu_config[] =
13329 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13330 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13331 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13332 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13333 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13334 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13335 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13336 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13337 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13338 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13341 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13343 char *token = menu_config[j].token;
13344 char *value = getHashEntry(setup_file_hash, token);
13347 *menu_config[j].value = get_integer_from_string(value);
13351 // special case: initialize with default values that may be overwritten
13352 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13353 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13355 struct TokenIntPtrInfo menu_config[] =
13357 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13358 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13359 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13360 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13361 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13362 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13363 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13364 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13365 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13368 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13370 char *token = menu_config[j].token;
13371 char *value = getHashEntry(setup_file_hash, token);
13374 *menu_config[j].value = get_token_parameter_value(token, value);
13378 // special case: initialize with default values that may be overwritten
13379 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13380 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13384 char *token_prefix;
13385 struct RectWithBorder *struct_ptr;
13389 { "viewport.window", &viewport.window[i] },
13390 { "viewport.playfield", &viewport.playfield[i] },
13391 { "viewport.door_1", &viewport.door_1[i] },
13392 { "viewport.door_2", &viewport.door_2[i] }
13395 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13397 struct TokenIntPtrInfo vp_config[] =
13399 { ".x", &vp_struct[j].struct_ptr->x },
13400 { ".y", &vp_struct[j].struct_ptr->y },
13401 { ".width", &vp_struct[j].struct_ptr->width },
13402 { ".height", &vp_struct[j].struct_ptr->height },
13403 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13404 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13405 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13406 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13407 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13408 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13409 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13410 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13411 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13412 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13413 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13414 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13415 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13416 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13417 { ".align", &vp_struct[j].struct_ptr->align },
13418 { ".valign", &vp_struct[j].struct_ptr->valign }
13421 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13423 char *token = getStringCat2(vp_struct[j].token_prefix,
13424 vp_config[k].token);
13425 char *value = getHashEntry(setup_file_hash, token);
13428 *vp_config[k].value = get_token_parameter_value(token, value);
13435 // special case: initialize with default values that may be overwritten
13436 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13437 for (i = 0; title_info[i].info != NULL; i++)
13439 struct TitleFadingInfo *info = title_info[i].info;
13440 char *base_token = title_info[i].text;
13442 for (j = 0; title_tokens[j].type != -1; j++)
13444 char *token = getStringCat2(base_token, title_tokens[j].text);
13445 char *value = getHashEntry(setup_file_hash, token);
13449 int parameter_value = get_token_parameter_value(token, value);
13453 *(int *)title_tokens[j].value = (int)parameter_value;
13462 // special case: initialize with default values that may be overwritten
13463 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13464 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13466 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13467 char *base_token = titlemessage_arrays[i].text;
13469 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13471 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13472 char *value = getHashEntry(setup_file_hash, token);
13476 int parameter_value = get_token_parameter_value(token, value);
13478 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13482 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13483 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13485 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13495 // read (and overwrite with) values that may be specified in config file
13496 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13498 // special case: check if network and preview player positions are redefined
13499 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13501 freeSetupFileHash(setup_file_hash);
13504 void LoadMenuDesignSettings(void)
13506 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13508 InitMenuDesignSettings_Static();
13509 InitMenuDesignSettings_SpecialPreProcessing();
13510 InitMenuDesignSettings_PreviewPlayers();
13512 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13514 // first look for special settings configured in level series config
13515 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13517 if (fileExists(filename_base))
13518 LoadMenuDesignSettingsFromFilename(filename_base);
13521 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13523 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13524 LoadMenuDesignSettingsFromFilename(filename_local);
13526 InitMenuDesignSettings_SpecialPostProcessing();
13529 void LoadMenuDesignSettings_AfterGraphics(void)
13531 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13534 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13535 boolean ignore_defaults)
13539 for (i = 0; sound_config_vars[i].token != NULL; i++)
13541 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13543 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13544 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13548 *sound_config_vars[i].value =
13549 get_token_parameter_value(sound_config_vars[i].token, value);
13553 void InitSoundSettings_Static(void)
13555 // always start with reliable default values from static default config
13556 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13559 static void LoadSoundSettingsFromFilename(char *filename)
13561 SetupFileHash *setup_file_hash;
13563 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13566 // read (and overwrite with) values that may be specified in config file
13567 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13569 freeSetupFileHash(setup_file_hash);
13572 void LoadSoundSettings(void)
13574 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13576 InitSoundSettings_Static();
13578 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13580 // first look for special settings configured in level series config
13581 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13583 if (fileExists(filename_base))
13584 LoadSoundSettingsFromFilename(filename_base);
13587 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13589 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13590 LoadSoundSettingsFromFilename(filename_local);
13593 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13595 char *filename = getEditorSetupFilename();
13596 SetupFileList *setup_file_list, *list;
13597 SetupFileHash *element_hash;
13598 int num_unknown_tokens = 0;
13601 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13604 element_hash = newSetupFileHash();
13606 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13607 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13609 // determined size may be larger than needed (due to unknown elements)
13611 for (list = setup_file_list; list != NULL; list = list->next)
13614 // add space for up to 3 more elements for padding that may be needed
13615 *num_elements += 3;
13617 // free memory for old list of elements, if needed
13618 checked_free(*elements);
13620 // allocate memory for new list of elements
13621 *elements = checked_malloc(*num_elements * sizeof(int));
13624 for (list = setup_file_list; list != NULL; list = list->next)
13626 char *value = getHashEntry(element_hash, list->token);
13628 if (value == NULL) // try to find obsolete token mapping
13630 char *mapped_token = get_mapped_token(list->token);
13632 if (mapped_token != NULL)
13634 value = getHashEntry(element_hash, mapped_token);
13636 free(mapped_token);
13642 (*elements)[(*num_elements)++] = atoi(value);
13646 if (num_unknown_tokens == 0)
13649 Warn("unknown token(s) found in config file:");
13650 Warn("- config file: '%s'", filename);
13652 num_unknown_tokens++;
13655 Warn("- token: '%s'", list->token);
13659 if (num_unknown_tokens > 0)
13662 while (*num_elements % 4) // pad with empty elements, if needed
13663 (*elements)[(*num_elements)++] = EL_EMPTY;
13665 freeSetupFileList(setup_file_list);
13666 freeSetupFileHash(element_hash);
13669 for (i = 0; i < *num_elements; i++)
13670 Debug("editor", "element '%s' [%d]\n",
13671 element_info[(*elements)[i]].token_name, (*elements)[i]);
13675 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13678 SetupFileHash *setup_file_hash = NULL;
13679 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13680 char *filename_music, *filename_prefix, *filename_info;
13686 token_to_value_ptr[] =
13688 { "title_header", &tmp_music_file_info.title_header },
13689 { "artist_header", &tmp_music_file_info.artist_header },
13690 { "album_header", &tmp_music_file_info.album_header },
13691 { "year_header", &tmp_music_file_info.year_header },
13692 { "played_header", &tmp_music_file_info.played_header },
13694 { "title", &tmp_music_file_info.title },
13695 { "artist", &tmp_music_file_info.artist },
13696 { "album", &tmp_music_file_info.album },
13697 { "year", &tmp_music_file_info.year },
13698 { "played", &tmp_music_file_info.played },
13704 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13705 getCustomMusicFilename(basename));
13707 if (filename_music == NULL)
13710 // ---------- try to replace file extension ----------
13712 filename_prefix = getStringCopy(filename_music);
13713 if (strrchr(filename_prefix, '.') != NULL)
13714 *strrchr(filename_prefix, '.') = '\0';
13715 filename_info = getStringCat2(filename_prefix, ".txt");
13717 if (fileExists(filename_info))
13718 setup_file_hash = loadSetupFileHash(filename_info);
13720 free(filename_prefix);
13721 free(filename_info);
13723 if (setup_file_hash == NULL)
13725 // ---------- try to add file extension ----------
13727 filename_prefix = getStringCopy(filename_music);
13728 filename_info = getStringCat2(filename_prefix, ".txt");
13730 if (fileExists(filename_info))
13731 setup_file_hash = loadSetupFileHash(filename_info);
13733 free(filename_prefix);
13734 free(filename_info);
13737 if (setup_file_hash == NULL)
13740 // ---------- music file info found ----------
13742 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13744 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13746 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13748 *token_to_value_ptr[i].value_ptr =
13749 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13752 tmp_music_file_info.basename = getStringCopy(basename);
13753 tmp_music_file_info.music = music;
13754 tmp_music_file_info.is_sound = is_sound;
13756 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13757 *new_music_file_info = tmp_music_file_info;
13759 return new_music_file_info;
13762 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13764 return get_music_file_info_ext(basename, music, FALSE);
13767 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13769 return get_music_file_info_ext(basename, sound, TRUE);
13772 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13773 char *basename, boolean is_sound)
13775 for (; list != NULL; list = list->next)
13776 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13782 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13784 return music_info_listed_ext(list, basename, FALSE);
13787 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13789 return music_info_listed_ext(list, basename, TRUE);
13792 void LoadMusicInfo(void)
13794 int num_music_noconf = getMusicListSize_NoConf();
13795 int num_music = getMusicListSize();
13796 int num_sounds = getSoundListSize();
13797 struct FileInfo *music, *sound;
13798 struct MusicFileInfo *next, **new;
13802 while (music_file_info != NULL)
13804 next = music_file_info->next;
13806 checked_free(music_file_info->basename);
13808 checked_free(music_file_info->title_header);
13809 checked_free(music_file_info->artist_header);
13810 checked_free(music_file_info->album_header);
13811 checked_free(music_file_info->year_header);
13812 checked_free(music_file_info->played_header);
13814 checked_free(music_file_info->title);
13815 checked_free(music_file_info->artist);
13816 checked_free(music_file_info->album);
13817 checked_free(music_file_info->year);
13818 checked_free(music_file_info->played);
13820 free(music_file_info);
13822 music_file_info = next;
13825 new = &music_file_info;
13827 // get (configured or unconfigured) music file info for all levels
13828 for (i = leveldir_current->first_level;
13829 i <= leveldir_current->last_level; i++)
13833 if (levelset.music[i] != MUS_UNDEFINED)
13835 // get music file info for configured level music
13836 music_nr = levelset.music[i];
13838 else if (num_music_noconf > 0)
13840 // get music file info for unconfigured level music
13841 int level_pos = i - leveldir_current->first_level;
13843 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13850 char *basename = getMusicInfoEntryFilename(music_nr);
13852 if (basename == NULL)
13855 if (!music_info_listed(music_file_info, basename))
13857 *new = get_music_file_info(basename, music_nr);
13860 new = &(*new)->next;
13864 // get music file info for all remaining configured music files
13865 for (i = 0; i < num_music; i++)
13867 music = getMusicListEntry(i);
13869 if (music->filename == NULL)
13872 if (strEqual(music->filename, UNDEFINED_FILENAME))
13875 // a configured file may be not recognized as music
13876 if (!FileIsMusic(music->filename))
13879 if (!music_info_listed(music_file_info, music->filename))
13881 *new = get_music_file_info(music->filename, i);
13884 new = &(*new)->next;
13888 // get sound file info for all configured sound files
13889 for (i = 0; i < num_sounds; i++)
13891 sound = getSoundListEntry(i);
13893 if (sound->filename == NULL)
13896 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13899 // a configured file may be not recognized as sound
13900 if (!FileIsSound(sound->filename))
13903 if (!sound_info_listed(music_file_info, sound->filename))
13905 *new = get_sound_file_info(sound->filename, i);
13907 new = &(*new)->next;
13911 // add pointers to previous list nodes
13913 struct MusicFileInfo *node = music_file_info;
13915 while (node != NULL)
13918 node->next->prev = node;
13924 static void add_helpanim_entry(int element, int action, int direction,
13925 int delay, int *num_list_entries)
13927 struct HelpAnimInfo *new_list_entry;
13928 (*num_list_entries)++;
13931 checked_realloc(helpanim_info,
13932 *num_list_entries * sizeof(struct HelpAnimInfo));
13933 new_list_entry = &helpanim_info[*num_list_entries - 1];
13935 new_list_entry->element = element;
13936 new_list_entry->action = action;
13937 new_list_entry->direction = direction;
13938 new_list_entry->delay = delay;
13941 static void print_unknown_token(char *filename, char *token, int token_nr)
13946 Warn("unknown token(s) found in config file:");
13947 Warn("- config file: '%s'", filename);
13950 Warn("- token: '%s'", token);
13953 static void print_unknown_token_end(int token_nr)
13959 void LoadHelpAnimInfo(void)
13961 char *filename = getHelpAnimFilename();
13962 SetupFileList *setup_file_list = NULL, *list;
13963 SetupFileHash *element_hash, *action_hash, *direction_hash;
13964 int num_list_entries = 0;
13965 int num_unknown_tokens = 0;
13968 if (fileExists(filename))
13969 setup_file_list = loadSetupFileList(filename);
13971 if (setup_file_list == NULL)
13973 // use reliable default values from static configuration
13974 SetupFileList *insert_ptr;
13976 insert_ptr = setup_file_list =
13977 newSetupFileList(helpanim_config[0].token,
13978 helpanim_config[0].value);
13980 for (i = 1; helpanim_config[i].token; i++)
13981 insert_ptr = addListEntry(insert_ptr,
13982 helpanim_config[i].token,
13983 helpanim_config[i].value);
13986 element_hash = newSetupFileHash();
13987 action_hash = newSetupFileHash();
13988 direction_hash = newSetupFileHash();
13990 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13991 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13993 for (i = 0; i < NUM_ACTIONS; i++)
13994 setHashEntry(action_hash, element_action_info[i].suffix,
13995 i_to_a(element_action_info[i].value));
13997 // do not store direction index (bit) here, but direction value!
13998 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13999 setHashEntry(direction_hash, element_direction_info[i].suffix,
14000 i_to_a(1 << element_direction_info[i].value));
14002 for (list = setup_file_list; list != NULL; list = list->next)
14004 char *element_token, *action_token, *direction_token;
14005 char *element_value, *action_value, *direction_value;
14006 int delay = atoi(list->value);
14008 if (strEqual(list->token, "end"))
14010 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14015 /* first try to break element into element/action/direction parts;
14016 if this does not work, also accept combined "element[.act][.dir]"
14017 elements (like "dynamite.active"), which are unique elements */
14019 if (strchr(list->token, '.') == NULL) // token contains no '.'
14021 element_value = getHashEntry(element_hash, list->token);
14022 if (element_value != NULL) // element found
14023 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14024 &num_list_entries);
14027 // no further suffixes found -- this is not an element
14028 print_unknown_token(filename, list->token, num_unknown_tokens++);
14034 // token has format "<prefix>.<something>"
14036 action_token = strchr(list->token, '.'); // suffix may be action ...
14037 direction_token = action_token; // ... or direction
14039 element_token = getStringCopy(list->token);
14040 *strchr(element_token, '.') = '\0';
14042 element_value = getHashEntry(element_hash, element_token);
14044 if (element_value == NULL) // this is no element
14046 element_value = getHashEntry(element_hash, list->token);
14047 if (element_value != NULL) // combined element found
14048 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14049 &num_list_entries);
14051 print_unknown_token(filename, list->token, num_unknown_tokens++);
14053 free(element_token);
14058 action_value = getHashEntry(action_hash, action_token);
14060 if (action_value != NULL) // action found
14062 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14063 &num_list_entries);
14065 free(element_token);
14070 direction_value = getHashEntry(direction_hash, direction_token);
14072 if (direction_value != NULL) // direction found
14074 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14075 &num_list_entries);
14077 free(element_token);
14082 if (strchr(action_token + 1, '.') == NULL)
14084 // no further suffixes found -- this is not an action nor direction
14086 element_value = getHashEntry(element_hash, list->token);
14087 if (element_value != NULL) // combined element found
14088 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14089 &num_list_entries);
14091 print_unknown_token(filename, list->token, num_unknown_tokens++);
14093 free(element_token);
14098 // token has format "<prefix>.<suffix>.<something>"
14100 direction_token = strchr(action_token + 1, '.');
14102 action_token = getStringCopy(action_token);
14103 *strchr(action_token + 1, '.') = '\0';
14105 action_value = getHashEntry(action_hash, action_token);
14107 if (action_value == NULL) // this is no action
14109 element_value = getHashEntry(element_hash, list->token);
14110 if (element_value != NULL) // combined element found
14111 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14112 &num_list_entries);
14114 print_unknown_token(filename, list->token, num_unknown_tokens++);
14116 free(element_token);
14117 free(action_token);
14122 direction_value = getHashEntry(direction_hash, direction_token);
14124 if (direction_value != NULL) // direction found
14126 add_helpanim_entry(atoi(element_value), atoi(action_value),
14127 atoi(direction_value), delay, &num_list_entries);
14129 free(element_token);
14130 free(action_token);
14135 // this is no direction
14137 element_value = getHashEntry(element_hash, list->token);
14138 if (element_value != NULL) // combined element found
14139 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14140 &num_list_entries);
14142 print_unknown_token(filename, list->token, num_unknown_tokens++);
14144 free(element_token);
14145 free(action_token);
14148 print_unknown_token_end(num_unknown_tokens);
14150 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14151 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14153 freeSetupFileList(setup_file_list);
14154 freeSetupFileHash(element_hash);
14155 freeSetupFileHash(action_hash);
14156 freeSetupFileHash(direction_hash);
14159 for (i = 0; i < num_list_entries; i++)
14160 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14161 EL_NAME(helpanim_info[i].element),
14162 helpanim_info[i].element,
14163 helpanim_info[i].action,
14164 helpanim_info[i].direction,
14165 helpanim_info[i].delay);
14169 void LoadHelpTextInfo(void)
14171 char *filename = getHelpTextFilename();
14174 if (helptext_info != NULL)
14176 freeSetupFileHash(helptext_info);
14177 helptext_info = NULL;
14180 if (fileExists(filename))
14181 helptext_info = loadSetupFileHash(filename);
14183 if (helptext_info == NULL)
14185 // use reliable default values from static configuration
14186 helptext_info = newSetupFileHash();
14188 for (i = 0; helptext_config[i].token; i++)
14189 setHashEntry(helptext_info,
14190 helptext_config[i].token,
14191 helptext_config[i].value);
14195 BEGIN_HASH_ITERATION(helptext_info, itr)
14197 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14198 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14200 END_HASH_ITERATION(hash, itr)
14205 // ----------------------------------------------------------------------------
14207 // ----------------------------------------------------------------------------
14209 #define MAX_NUM_CONVERT_LEVELS 1000
14211 void ConvertLevels(void)
14213 static LevelDirTree *convert_leveldir = NULL;
14214 static int convert_level_nr = -1;
14215 static int num_levels_handled = 0;
14216 static int num_levels_converted = 0;
14217 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14220 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14221 global.convert_leveldir);
14223 if (convert_leveldir == NULL)
14224 Fail("no such level identifier: '%s'", global.convert_leveldir);
14226 leveldir_current = convert_leveldir;
14228 if (global.convert_level_nr != -1)
14230 convert_leveldir->first_level = global.convert_level_nr;
14231 convert_leveldir->last_level = global.convert_level_nr;
14234 convert_level_nr = convert_leveldir->first_level;
14236 PrintLine("=", 79);
14237 Print("Converting levels\n");
14238 PrintLine("-", 79);
14239 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14240 Print("Level series name: '%s'\n", convert_leveldir->name);
14241 Print("Level series author: '%s'\n", convert_leveldir->author);
14242 Print("Number of levels: %d\n", convert_leveldir->levels);
14243 PrintLine("=", 79);
14246 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14247 levels_failed[i] = FALSE;
14249 while (convert_level_nr <= convert_leveldir->last_level)
14251 char *level_filename;
14254 level_nr = convert_level_nr++;
14256 Print("Level %03d: ", level_nr);
14258 LoadLevel(level_nr);
14259 if (level.no_level_file || level.no_valid_file)
14261 Print("(no level)\n");
14265 Print("converting level ... ");
14268 // special case: conversion of some EMC levels as requested by ACME
14269 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14272 level_filename = getDefaultLevelFilename(level_nr);
14273 new_level = !fileExists(level_filename);
14277 SaveLevel(level_nr);
14279 num_levels_converted++;
14281 Print("converted.\n");
14285 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14286 levels_failed[level_nr] = TRUE;
14288 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14291 num_levels_handled++;
14295 PrintLine("=", 79);
14296 Print("Number of levels handled: %d\n", num_levels_handled);
14297 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14298 (num_levels_handled ?
14299 num_levels_converted * 100 / num_levels_handled : 0));
14300 PrintLine("-", 79);
14301 Print("Summary (for automatic parsing by scripts):\n");
14302 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14303 convert_leveldir->identifier, num_levels_converted,
14304 num_levels_handled,
14305 (num_levels_handled ?
14306 num_levels_converted * 100 / num_levels_handled : 0));
14308 if (num_levels_handled != num_levels_converted)
14310 Print(", FAILED:");
14311 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14312 if (levels_failed[i])
14317 PrintLine("=", 79);
14319 CloseAllAndExit(0);
14323 // ----------------------------------------------------------------------------
14324 // create and save images for use in level sketches (raw BMP format)
14325 // ----------------------------------------------------------------------------
14327 void CreateLevelSketchImages(void)
14333 InitElementPropertiesGfxElement();
14335 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14336 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14338 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14340 int element = getMappedElement(i);
14341 char basename1[16];
14342 char basename2[16];
14346 sprintf(basename1, "%04d.bmp", i);
14347 sprintf(basename2, "%04ds.bmp", i);
14349 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14350 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14352 DrawSizedElement(0, 0, element, TILESIZE);
14353 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14355 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14356 Fail("cannot save level sketch image file '%s'", filename1);
14358 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14359 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14361 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14362 Fail("cannot save level sketch image file '%s'", filename2);
14367 // create corresponding SQL statements (for normal and small images)
14370 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14371 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14374 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14375 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14377 // optional: create content for forum level sketch demonstration post
14379 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14382 FreeBitmap(bitmap1);
14383 FreeBitmap(bitmap2);
14386 fprintf(stderr, "\n");
14388 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14390 CloseAllAndExit(0);
14394 // ----------------------------------------------------------------------------
14395 // create and save images for element collecting animations (raw BMP format)
14396 // ----------------------------------------------------------------------------
14398 static boolean createCollectImage(int element)
14400 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14403 void CreateCollectElementImages(void)
14407 int anim_frames = num_steps - 1;
14408 int tile_size = TILESIZE;
14409 int anim_width = tile_size * anim_frames;
14410 int anim_height = tile_size;
14411 int num_collect_images = 0;
14412 int pos_collect_images = 0;
14414 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14415 if (createCollectImage(i))
14416 num_collect_images++;
14418 Info("Creating %d element collecting animation images ...",
14419 num_collect_images);
14421 int dst_width = anim_width * 2;
14422 int dst_height = anim_height * num_collect_images / 2;
14423 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14424 char *basename_bmp = "RocksCollect.bmp";
14425 char *basename_png = "RocksCollect.png";
14426 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14427 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14428 int len_filename_bmp = strlen(filename_bmp);
14429 int len_filename_png = strlen(filename_png);
14430 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14431 char cmd_convert[max_command_len];
14433 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14437 // force using RGBA surface for destination bitmap
14438 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14439 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14441 dst_bitmap->surface =
14442 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14444 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14446 if (!createCollectImage(i))
14449 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14450 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14451 int graphic = el2img(i);
14452 char *token_name = element_info[i].token_name;
14453 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14454 Bitmap *src_bitmap;
14457 Info("- creating collecting image for '%s' ...", token_name);
14459 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14461 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14462 tile_size, tile_size, 0, 0);
14464 // force using RGBA surface for temporary bitmap (using transparent black)
14465 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14466 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14468 tmp_bitmap->surface =
14469 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14471 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14473 for (j = 0; j < anim_frames; j++)
14475 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14476 int frame_size = frame_size_final * num_steps;
14477 int offset = (tile_size - frame_size_final) / 2;
14478 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14480 while (frame_size > frame_size_final)
14484 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14486 FreeBitmap(frame_bitmap);
14488 frame_bitmap = half_bitmap;
14491 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14492 frame_size_final, frame_size_final,
14493 dst_x + j * tile_size + offset, dst_y + offset);
14495 FreeBitmap(frame_bitmap);
14498 tmp_bitmap->surface_masked = NULL;
14500 FreeBitmap(tmp_bitmap);
14502 pos_collect_images++;
14505 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14506 Fail("cannot save element collecting image file '%s'", filename_bmp);
14508 FreeBitmap(dst_bitmap);
14510 Info("Converting image file from BMP to PNG ...");
14512 if (system(cmd_convert) != 0)
14513 Fail("converting image file failed");
14515 unlink(filename_bmp);
14519 CloseAllAndExit(0);
14523 // ----------------------------------------------------------------------------
14524 // create and save images for custom and group elements (raw BMP format)
14525 // ----------------------------------------------------------------------------
14527 void CreateCustomElementImages(char *directory)
14529 char *src_basename = "RocksCE-template.ilbm";
14530 char *dst_basename = "RocksCE.bmp";
14531 char *src_filename = getPath2(directory, src_basename);
14532 char *dst_filename = getPath2(directory, dst_basename);
14533 Bitmap *src_bitmap;
14535 int yoffset_ce = 0;
14536 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14539 InitVideoDefaults();
14541 ReCreateBitmap(&backbuffer, video.width, video.height);
14543 src_bitmap = LoadImage(src_filename);
14545 bitmap = CreateBitmap(TILEX * 16 * 2,
14546 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14549 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14556 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14557 TILEX * x, TILEY * y + yoffset_ce);
14559 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14561 TILEX * x + TILEX * 16,
14562 TILEY * y + yoffset_ce);
14564 for (j = 2; j >= 0; j--)
14568 BlitBitmap(src_bitmap, bitmap,
14569 TILEX + c * 7, 0, 6, 10,
14570 TILEX * x + 6 + j * 7,
14571 TILEY * y + 11 + yoffset_ce);
14573 BlitBitmap(src_bitmap, bitmap,
14574 TILEX + c * 8, TILEY, 6, 10,
14575 TILEX * 16 + TILEX * x + 6 + j * 8,
14576 TILEY * y + 10 + yoffset_ce);
14582 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14589 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14590 TILEX * x, TILEY * y + yoffset_ge);
14592 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14594 TILEX * x + TILEX * 16,
14595 TILEY * y + yoffset_ge);
14597 for (j = 1; j >= 0; j--)
14601 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14602 TILEX * x + 6 + j * 10,
14603 TILEY * y + 11 + yoffset_ge);
14605 BlitBitmap(src_bitmap, bitmap,
14606 TILEX + c * 8, TILEY + 12, 6, 10,
14607 TILEX * 16 + TILEX * x + 10 + j * 8,
14608 TILEY * y + 10 + yoffset_ge);
14614 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14615 Fail("cannot save CE graphics file '%s'", dst_filename);
14617 FreeBitmap(bitmap);
14619 CloseAllAndExit(0);