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
739 // (the following values are related to various game elements)
743 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
744 &li.score[SC_EMERALD], 10
749 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
750 &li.score[SC_DIAMOND], 10
755 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
756 &li.score[SC_BUG], 10
761 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
762 &li.score[SC_SPACESHIP], 10
767 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
768 &li.score[SC_PACMAN], 10
773 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
774 &li.score[SC_NUT], 10
779 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
780 &li.score[SC_DYNAMITE], 10
785 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
786 &li.score[SC_KEY], 10
791 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
792 &li.score[SC_PEARL], 10
797 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
798 &li.score[SC_CRYSTAL], 10
801 // (amoeba values used by R'n'D game engine only)
804 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
805 &li.amoeba_content, EL_DIAMOND
809 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
814 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
815 &li.grow_into_diggable, TRUE
817 // (amoeba values used by BD game engine only)
820 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
821 &li.bd_amoeba_wait_for_hatching, FALSE
825 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
826 &li.bd_amoeba_start_immediately, TRUE
830 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
831 &li.bd_amoeba_2_explode_by_amoeba, TRUE
835 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
836 &li.bd_amoeba_threshold_too_big, 200
840 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
841 &li.bd_amoeba_slow_growth_time, 200
845 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
846 &li.bd_amoeba_slow_growth_rate, 3
850 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
851 &li.bd_amoeba_fast_growth_rate, 25
855 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
856 &li.bd_amoeba_content_too_big, EL_BD_ROCK
860 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
861 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
866 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
867 &li.bd_amoeba_2_threshold_too_big, 200
871 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
872 &li.bd_amoeba_2_slow_growth_time, 200
876 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
877 &li.bd_amoeba_2_slow_growth_rate, 3
881 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
882 &li.bd_amoeba_2_fast_growth_rate, 25
886 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
887 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
891 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
892 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
896 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
897 &li.bd_amoeba_2_content_exploding, EL_EMPTY
901 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
902 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
907 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
908 &li.yamyam_content, EL_ROCK, NULL,
909 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
913 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
914 &li.score[SC_YAMYAM], 10
919 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
920 &li.score[SC_ROBOT], 10
924 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
930 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
936 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
937 &li.time_magic_wall, 10
942 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
943 &li.game_of_life[0], 2
947 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
948 &li.game_of_life[1], 3
952 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
953 &li.game_of_life[2], 3
957 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
958 &li.game_of_life[3], 3
962 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
963 &li.use_life_bugs, FALSE
968 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
973 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
978 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
983 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
988 EL_TIMEGATE_SWITCH, -1,
989 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
990 &li.time_timegate, 10
994 EL_LIGHT_SWITCH_ACTIVE, -1,
995 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1000 EL_SHIELD_NORMAL, -1,
1001 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1002 &li.shield_normal_time, 10
1005 EL_SHIELD_NORMAL, -1,
1006 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1007 &li.score[SC_SHIELD], 10
1011 EL_SHIELD_DEADLY, -1,
1012 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1013 &li.shield_deadly_time, 10
1016 EL_SHIELD_DEADLY, -1,
1017 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1018 &li.score[SC_SHIELD], 10
1023 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1028 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1029 &li.extra_time_score, 10
1033 EL_TIME_ORB_FULL, -1,
1034 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1035 &li.time_orb_time, 10
1038 EL_TIME_ORB_FULL, -1,
1039 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1040 &li.use_time_orb_bug, FALSE
1045 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1046 &li.use_spring_bug, FALSE
1051 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1052 &li.android_move_time, 10
1056 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1057 &li.android_clone_time, 10
1060 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1061 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1062 &li.android_clone_element[0], EL_EMPTY, NULL,
1063 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1067 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1068 &li.android_clone_element[0], EL_EMPTY, NULL,
1069 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1074 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1075 &li.lenses_score, 10
1079 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1084 EL_EMC_MAGNIFIER, -1,
1085 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1086 &li.magnify_score, 10
1089 EL_EMC_MAGNIFIER, -1,
1090 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1091 &li.magnify_time, 10
1095 EL_EMC_MAGIC_BALL, -1,
1096 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1100 EL_EMC_MAGIC_BALL, -1,
1101 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1102 &li.ball_random, FALSE
1105 EL_EMC_MAGIC_BALL, -1,
1106 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1107 &li.ball_active_initial, FALSE
1110 EL_EMC_MAGIC_BALL, -1,
1111 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1112 &li.ball_content, EL_EMPTY, NULL,
1113 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1117 EL_SOKOBAN_FIELD_EMPTY, -1,
1118 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1119 &li.sb_fields_needed, TRUE
1123 EL_SOKOBAN_OBJECT, -1,
1124 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1125 &li.sb_objects_needed, TRUE
1130 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1131 &li.mm_laser_red, FALSE
1135 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1136 &li.mm_laser_green, FALSE
1140 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1141 &li.mm_laser_blue, TRUE
1146 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1147 &li.df_laser_red, TRUE
1151 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1152 &li.df_laser_green, TRUE
1156 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1157 &li.df_laser_blue, FALSE
1161 EL_MM_FUSE_ACTIVE, -1,
1162 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1163 &li.mm_time_fuse, 25
1167 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1168 &li.mm_time_bomb, 75
1172 EL_MM_GRAY_BALL, -1,
1173 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1174 &li.mm_time_ball, 75
1177 EL_MM_GRAY_BALL, -1,
1178 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1179 &li.mm_ball_choice_mode, ANIM_RANDOM
1182 EL_MM_GRAY_BALL, -1,
1183 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1184 &li.mm_ball_content, EL_EMPTY, NULL,
1185 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1188 EL_MM_GRAY_BALL, -1,
1189 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1190 &li.rotate_mm_ball_content, TRUE
1193 EL_MM_GRAY_BALL, -1,
1194 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1195 &li.explode_mm_ball, FALSE
1199 EL_MM_STEEL_BLOCK, -1,
1200 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1201 &li.mm_time_block, 75
1204 EL_MM_LIGHTBALL, -1,
1205 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1206 &li.score[SC_ELEM_BONUS], 10
1216 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1220 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1221 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1225 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1226 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1231 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1232 &xx_envelope.autowrap, FALSE
1236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1237 &xx_envelope.centered, FALSE
1242 TYPE_STRING, CONF_VALUE_BYTES(1),
1243 &xx_envelope.text, -1, NULL,
1244 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1245 &xx_default_string_empty[0]
1255 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1259 TYPE_STRING, CONF_VALUE_BYTES(1),
1260 &xx_ei.description[0], -1,
1261 &yy_ei.description[0],
1262 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1263 &xx_default_description[0]
1268 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1269 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1270 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1272 #if ENABLE_RESERVED_CODE
1273 // (reserved for later use)
1276 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1277 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1278 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1284 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1285 &xx_ei.use_gfx_element, FALSE,
1286 &yy_ei.use_gfx_element
1290 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1291 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1292 &yy_ei.gfx_element_initial
1297 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1298 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1299 &yy_ei.access_direction
1304 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1305 &xx_ei.collect_score_initial, 10,
1306 &yy_ei.collect_score_initial
1310 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1311 &xx_ei.collect_count_initial, 1,
1312 &yy_ei.collect_count_initial
1317 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1318 &xx_ei.ce_value_fixed_initial, 0,
1319 &yy_ei.ce_value_fixed_initial
1323 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1324 &xx_ei.ce_value_random_initial, 0,
1325 &yy_ei.ce_value_random_initial
1329 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1330 &xx_ei.use_last_ce_value, FALSE,
1331 &yy_ei.use_last_ce_value
1336 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1337 &xx_ei.push_delay_fixed, 8,
1338 &yy_ei.push_delay_fixed
1342 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1343 &xx_ei.push_delay_random, 8,
1344 &yy_ei.push_delay_random
1348 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1349 &xx_ei.drop_delay_fixed, 0,
1350 &yy_ei.drop_delay_fixed
1354 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1355 &xx_ei.drop_delay_random, 0,
1356 &yy_ei.drop_delay_random
1360 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1361 &xx_ei.move_delay_fixed, 0,
1362 &yy_ei.move_delay_fixed
1366 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1367 &xx_ei.move_delay_random, 0,
1368 &yy_ei.move_delay_random
1372 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1373 &xx_ei.step_delay_fixed, 0,
1374 &yy_ei.step_delay_fixed
1378 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1379 &xx_ei.step_delay_random, 0,
1380 &yy_ei.step_delay_random
1385 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1386 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1391 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1392 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1393 &yy_ei.move_direction_initial
1397 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1398 &xx_ei.move_stepsize, TILEX / 8,
1399 &yy_ei.move_stepsize
1404 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1405 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1406 &yy_ei.move_enter_element
1410 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1411 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1412 &yy_ei.move_leave_element
1416 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1417 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1418 &yy_ei.move_leave_type
1423 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1424 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1425 &yy_ei.slippery_type
1430 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1431 &xx_ei.explosion_type, EXPLODES_3X3,
1432 &yy_ei.explosion_type
1436 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1437 &xx_ei.explosion_delay, 16,
1438 &yy_ei.explosion_delay
1442 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1443 &xx_ei.ignition_delay, 8,
1444 &yy_ei.ignition_delay
1449 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1450 &xx_ei.content, EL_EMPTY_SPACE,
1452 &xx_num_contents, 1, 1
1455 // ---------- "num_change_pages" must be the last entry ---------------------
1458 -1, SAVE_CONF_ALWAYS,
1459 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1460 &xx_ei.num_change_pages, 1,
1461 &yy_ei.num_change_pages
1472 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1474 // ---------- "current_change_page" must be the first entry -----------------
1477 -1, SAVE_CONF_ALWAYS,
1478 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1479 &xx_current_change_page, -1
1482 // ---------- (the remaining entries can be in any order) -------------------
1486 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1487 &xx_change.can_change, FALSE
1492 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1493 &xx_event_bits[0], 0
1497 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1498 &xx_event_bits[1], 0
1503 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1504 &xx_change.trigger_player, CH_PLAYER_ANY
1508 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1509 &xx_change.trigger_side, CH_SIDE_ANY
1513 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1514 &xx_change.trigger_page, CH_PAGE_ANY
1519 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1520 &xx_change.target_element, EL_EMPTY_SPACE
1525 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1526 &xx_change.delay_fixed, 0
1530 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1531 &xx_change.delay_random, 0
1535 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1536 &xx_change.delay_frames, FRAMES_PER_SECOND
1541 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1542 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1547 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1548 &xx_change.explode, FALSE
1552 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1553 &xx_change.use_target_content, FALSE
1557 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1558 &xx_change.only_if_complete, FALSE
1562 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1563 &xx_change.use_random_replace, FALSE
1567 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1568 &xx_change.random_percentage, 100
1572 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1573 &xx_change.replace_when, CP_WHEN_EMPTY
1578 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1579 &xx_change.has_action, FALSE
1583 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1584 &xx_change.action_type, CA_NO_ACTION
1588 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1589 &xx_change.action_mode, CA_MODE_UNDEFINED
1593 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1594 &xx_change.action_arg, CA_ARG_UNDEFINED
1599 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1600 &xx_change.action_element, EL_EMPTY_SPACE
1605 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1606 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1607 &xx_num_contents, 1, 1
1617 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1621 TYPE_STRING, CONF_VALUE_BYTES(1),
1622 &xx_ei.description[0], -1, NULL,
1623 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1624 &xx_default_description[0]
1629 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1630 &xx_ei.use_gfx_element, FALSE
1634 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1635 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1640 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1641 &xx_group.choice_mode, ANIM_RANDOM
1646 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1647 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1648 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1658 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1662 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1663 &xx_ei.use_gfx_element, FALSE
1667 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1668 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1678 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1682 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1683 &li.block_snap_field, TRUE
1687 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1688 &li.continuous_snapping, TRUE
1692 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1693 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1697 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1698 &li.use_start_element[0], FALSE
1702 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1703 &li.start_element[0], EL_PLAYER_1
1707 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1708 &li.use_artwork_element[0], FALSE
1712 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1713 &li.artwork_element[0], EL_PLAYER_1
1717 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1718 &li.use_explosion_element[0], FALSE
1722 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1723 &li.explosion_element[0], EL_PLAYER_1
1738 filetype_id_list[] =
1740 { LEVEL_FILE_TYPE_RND, "RND" },
1741 { LEVEL_FILE_TYPE_BD, "BD" },
1742 { LEVEL_FILE_TYPE_EM, "EM" },
1743 { LEVEL_FILE_TYPE_SP, "SP" },
1744 { LEVEL_FILE_TYPE_DX, "DX" },
1745 { LEVEL_FILE_TYPE_SB, "SB" },
1746 { LEVEL_FILE_TYPE_DC, "DC" },
1747 { LEVEL_FILE_TYPE_MM, "MM" },
1748 { LEVEL_FILE_TYPE_MM, "DF" },
1753 // ============================================================================
1754 // level file functions
1755 // ============================================================================
1757 static boolean check_special_flags(char *flag)
1759 if (strEqual(options.special_flags, flag) ||
1760 strEqual(leveldir_current->special_flags, flag))
1766 static struct DateInfo getCurrentDate(void)
1768 time_t epoch_seconds = time(NULL);
1769 struct tm *now = localtime(&epoch_seconds);
1770 struct DateInfo date;
1772 date.year = now->tm_year + 1900;
1773 date.month = now->tm_mon + 1;
1774 date.day = now->tm_mday;
1776 date.src = DATE_SRC_CLOCK;
1781 static void resetEventFlags(struct ElementChangeInfo *change)
1785 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1786 change->has_event[i] = FALSE;
1789 static void resetEventBits(void)
1793 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1794 xx_event_bits[i] = 0;
1797 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1801 /* important: only change event flag if corresponding event bit is set
1802 (this is because all xx_event_bits[] values are loaded separately,
1803 and all xx_event_bits[] values are set back to zero before loading
1804 another value xx_event_bits[x] (each value representing 32 flags)) */
1806 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1807 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1808 change->has_event[i] = TRUE;
1811 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1815 /* in contrast to the above function setEventFlagsFromEventBits(), it
1816 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1817 depending on the corresponding change->has_event[i] values here, as
1818 all xx_event_bits[] values are reset in resetEventBits() before */
1820 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1821 if (change->has_event[i])
1822 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1825 static char *getDefaultElementDescription(struct ElementInfo *ei)
1827 static char description[MAX_ELEMENT_NAME_LEN + 1];
1828 char *default_description = (ei->custom_description != NULL ?
1829 ei->custom_description :
1830 ei->editor_description);
1833 // always start with reliable default values
1834 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1835 description[i] = '\0';
1837 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1838 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1840 return &description[0];
1843 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1845 char *default_description = getDefaultElementDescription(ei);
1848 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1849 ei->description[i] = default_description[i];
1852 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1856 for (i = 0; conf[i].data_type != -1; i++)
1858 int default_value = conf[i].default_value;
1859 int data_type = conf[i].data_type;
1860 int conf_type = conf[i].conf_type;
1861 int byte_mask = conf_type & CONF_MASK_BYTES;
1863 if (byte_mask == CONF_MASK_MULTI_BYTES)
1865 int default_num_entities = conf[i].default_num_entities;
1866 int max_num_entities = conf[i].max_num_entities;
1868 *(int *)(conf[i].num_entities) = default_num_entities;
1870 if (data_type == TYPE_STRING)
1872 char *default_string = conf[i].default_string;
1873 char *string = (char *)(conf[i].value);
1875 strncpy(string, default_string, max_num_entities);
1877 else if (data_type == TYPE_ELEMENT_LIST)
1879 int *element_array = (int *)(conf[i].value);
1882 for (j = 0; j < max_num_entities; j++)
1883 element_array[j] = default_value;
1885 else if (data_type == TYPE_CONTENT_LIST)
1887 struct Content *content = (struct Content *)(conf[i].value);
1890 for (c = 0; c < max_num_entities; c++)
1891 for (y = 0; y < 3; y++)
1892 for (x = 0; x < 3; x++)
1893 content[c].e[x][y] = default_value;
1896 else // constant size configuration data (1, 2 or 4 bytes)
1898 if (data_type == TYPE_BOOLEAN)
1899 *(boolean *)(conf[i].value) = default_value;
1901 *(int *) (conf[i].value) = default_value;
1906 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1910 for (i = 0; conf[i].data_type != -1; i++)
1912 int data_type = conf[i].data_type;
1913 int conf_type = conf[i].conf_type;
1914 int byte_mask = conf_type & CONF_MASK_BYTES;
1916 if (byte_mask == CONF_MASK_MULTI_BYTES)
1918 int max_num_entities = conf[i].max_num_entities;
1920 if (data_type == TYPE_STRING)
1922 char *string = (char *)(conf[i].value);
1923 char *string_copy = (char *)(conf[i].value_copy);
1925 strncpy(string_copy, string, max_num_entities);
1927 else if (data_type == TYPE_ELEMENT_LIST)
1929 int *element_array = (int *)(conf[i].value);
1930 int *element_array_copy = (int *)(conf[i].value_copy);
1933 for (j = 0; j < max_num_entities; j++)
1934 element_array_copy[j] = element_array[j];
1936 else if (data_type == TYPE_CONTENT_LIST)
1938 struct Content *content = (struct Content *)(conf[i].value);
1939 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1942 for (c = 0; c < max_num_entities; c++)
1943 for (y = 0; y < 3; y++)
1944 for (x = 0; x < 3; x++)
1945 content_copy[c].e[x][y] = content[c].e[x][y];
1948 else // constant size configuration data (1, 2 or 4 bytes)
1950 if (data_type == TYPE_BOOLEAN)
1951 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1953 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1958 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1962 xx_ei = *ei_from; // copy element data into temporary buffer
1963 yy_ei = *ei_to; // copy element data into temporary buffer
1965 copyConfigFromConfigList(chunk_config_CUSX_base);
1970 // ---------- reinitialize and copy change pages ----------
1972 ei_to->num_change_pages = ei_from->num_change_pages;
1973 ei_to->current_change_page = ei_from->current_change_page;
1975 setElementChangePages(ei_to, ei_to->num_change_pages);
1977 for (i = 0; i < ei_to->num_change_pages; i++)
1978 ei_to->change_page[i] = ei_from->change_page[i];
1980 // ---------- copy group element info ----------
1981 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1982 *ei_to->group = *ei_from->group;
1984 // mark this custom element as modified
1985 ei_to->modified_settings = TRUE;
1988 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1990 int change_page_size = sizeof(struct ElementChangeInfo);
1992 ei->num_change_pages = MAX(1, change_pages);
1995 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1997 if (ei->current_change_page >= ei->num_change_pages)
1998 ei->current_change_page = ei->num_change_pages - 1;
2000 ei->change = &ei->change_page[ei->current_change_page];
2003 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2005 xx_change = *change; // copy change data into temporary buffer
2007 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2009 *change = xx_change;
2011 resetEventFlags(change);
2013 change->direct_action = 0;
2014 change->other_action = 0;
2016 change->pre_change_function = NULL;
2017 change->change_function = NULL;
2018 change->post_change_function = NULL;
2021 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2025 li = *level; // copy level data into temporary buffer
2026 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2027 *level = li; // copy temporary buffer back to level data
2029 setLevelInfoToDefaults_BD();
2030 setLevelInfoToDefaults_EM();
2031 setLevelInfoToDefaults_SP();
2032 setLevelInfoToDefaults_MM();
2034 level->native_bd_level = &native_bd_level;
2035 level->native_em_level = &native_em_level;
2036 level->native_sp_level = &native_sp_level;
2037 level->native_mm_level = &native_mm_level;
2039 level->file_version = FILE_VERSION_ACTUAL;
2040 level->game_version = GAME_VERSION_ACTUAL;
2042 level->creation_date = getCurrentDate();
2044 level->encoding_16bit_field = TRUE;
2045 level->encoding_16bit_yamyam = TRUE;
2046 level->encoding_16bit_amoeba = TRUE;
2048 // clear level name and level author string buffers
2049 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2050 level->name[i] = '\0';
2051 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2052 level->author[i] = '\0';
2054 // set level name and level author to default values
2055 strcpy(level->name, NAMELESS_LEVEL_NAME);
2056 strcpy(level->author, ANONYMOUS_NAME);
2058 // set level playfield to playable default level with player and exit
2059 for (x = 0; x < MAX_LEV_FIELDX; x++)
2060 for (y = 0; y < MAX_LEV_FIELDY; y++)
2061 level->field[x][y] = EL_SAND;
2063 level->field[0][0] = EL_PLAYER_1;
2064 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2066 BorderElement = EL_STEELWALL;
2068 // detect custom elements when loading them
2069 level->file_has_custom_elements = FALSE;
2071 // set all bug compatibility flags to "false" => do not emulate this bug
2072 level->use_action_after_change_bug = FALSE;
2074 if (leveldir_current)
2076 // try to determine better author name than 'anonymous'
2077 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2079 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2080 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2084 switch (LEVELCLASS(leveldir_current))
2086 case LEVELCLASS_TUTORIAL:
2087 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2090 case LEVELCLASS_CONTRIB:
2091 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2092 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2095 case LEVELCLASS_PRIVATE:
2096 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2097 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2101 // keep default value
2108 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2110 static boolean clipboard_elements_initialized = FALSE;
2113 InitElementPropertiesStatic();
2115 li = *level; // copy level data into temporary buffer
2116 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2117 *level = li; // copy temporary buffer back to level data
2119 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2122 struct ElementInfo *ei = &element_info[element];
2124 if (element == EL_MM_GRAY_BALL)
2126 struct LevelInfo_MM *level_mm = level->native_mm_level;
2129 for (j = 0; j < level->num_mm_ball_contents; j++)
2130 level->mm_ball_content[j] =
2131 map_element_MM_to_RND(level_mm->ball_content[j]);
2134 // never initialize clipboard elements after the very first time
2135 // (to be able to use clipboard elements between several levels)
2136 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2139 if (IS_ENVELOPE(element))
2141 int envelope_nr = element - EL_ENVELOPE_1;
2143 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2145 level->envelope[envelope_nr] = xx_envelope;
2148 if (IS_CUSTOM_ELEMENT(element) ||
2149 IS_GROUP_ELEMENT(element) ||
2150 IS_INTERNAL_ELEMENT(element))
2152 xx_ei = *ei; // copy element data into temporary buffer
2154 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2159 setElementChangePages(ei, 1);
2160 setElementChangeInfoToDefaults(ei->change);
2162 if (IS_CUSTOM_ELEMENT(element) ||
2163 IS_GROUP_ELEMENT(element))
2165 setElementDescriptionToDefault(ei);
2167 ei->modified_settings = FALSE;
2170 if (IS_CUSTOM_ELEMENT(element) ||
2171 IS_INTERNAL_ELEMENT(element))
2173 // internal values used in level editor
2175 ei->access_type = 0;
2176 ei->access_layer = 0;
2177 ei->access_protected = 0;
2178 ei->walk_to_action = 0;
2179 ei->smash_targets = 0;
2182 ei->can_explode_by_fire = FALSE;
2183 ei->can_explode_smashed = FALSE;
2184 ei->can_explode_impact = FALSE;
2186 ei->current_change_page = 0;
2189 if (IS_GROUP_ELEMENT(element) ||
2190 IS_INTERNAL_ELEMENT(element))
2192 struct ElementGroupInfo *group;
2194 // initialize memory for list of elements in group
2195 if (ei->group == NULL)
2196 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2200 xx_group = *group; // copy group data into temporary buffer
2202 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2207 if (IS_EMPTY_ELEMENT(element) ||
2208 IS_INTERNAL_ELEMENT(element))
2210 xx_ei = *ei; // copy element data into temporary buffer
2212 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2218 clipboard_elements_initialized = TRUE;
2221 static void setLevelInfoToDefaults(struct LevelInfo *level,
2222 boolean level_info_only,
2223 boolean reset_file_status)
2225 setLevelInfoToDefaults_Level(level);
2227 if (!level_info_only)
2228 setLevelInfoToDefaults_Elements(level);
2230 if (reset_file_status)
2232 level->no_valid_file = FALSE;
2233 level->no_level_file = FALSE;
2236 level->changed = FALSE;
2239 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2241 level_file_info->nr = 0;
2242 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2243 level_file_info->packed = FALSE;
2245 setString(&level_file_info->basename, NULL);
2246 setString(&level_file_info->filename, NULL);
2249 int getMappedElement_SB(int, boolean);
2251 static void ActivateLevelTemplate(void)
2255 if (check_special_flags("load_xsb_to_ces"))
2257 // fill smaller playfields with padding "beyond border wall" elements
2258 if (level.fieldx < level_template.fieldx ||
2259 level.fieldy < level_template.fieldy)
2261 short field[level.fieldx][level.fieldy];
2262 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2263 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2264 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2265 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2267 // copy old playfield (which is smaller than the visible area)
2268 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2269 field[x][y] = level.field[x][y];
2271 // fill new, larger playfield with "beyond border wall" elements
2272 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2273 level.field[x][y] = getMappedElement_SB('_', TRUE);
2275 // copy the old playfield to the middle of the new playfield
2276 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2277 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2279 level.fieldx = new_fieldx;
2280 level.fieldy = new_fieldy;
2284 // Currently there is no special action needed to activate the template
2285 // data, because 'element_info' property settings overwrite the original
2286 // level data, while all other variables do not change.
2288 // Exception: 'from_level_template' elements in the original level playfield
2289 // are overwritten with the corresponding elements at the same position in
2290 // playfield from the level template.
2292 for (x = 0; x < level.fieldx; x++)
2293 for (y = 0; y < level.fieldy; y++)
2294 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2295 level.field[x][y] = level_template.field[x][y];
2297 if (check_special_flags("load_xsb_to_ces"))
2299 struct LevelInfo level_backup = level;
2301 // overwrite all individual level settings from template level settings
2302 level = level_template;
2304 // restore level file info
2305 level.file_info = level_backup.file_info;
2307 // restore playfield size
2308 level.fieldx = level_backup.fieldx;
2309 level.fieldy = level_backup.fieldy;
2311 // restore playfield content
2312 for (x = 0; x < level.fieldx; x++)
2313 for (y = 0; y < level.fieldy; y++)
2314 level.field[x][y] = level_backup.field[x][y];
2316 // restore name and author from individual level
2317 strcpy(level.name, level_backup.name);
2318 strcpy(level.author, level_backup.author);
2320 // restore flag "use_custom_template"
2321 level.use_custom_template = level_backup.use_custom_template;
2325 static boolean checkForPackageFromBasename_BD(char *basename)
2327 // check for native BD level file extensions
2328 if (!strSuffixLower(basename, ".bd") &&
2329 !strSuffixLower(basename, ".bdr") &&
2330 !strSuffixLower(basename, ".brc") &&
2331 !strSuffixLower(basename, ".gds"))
2334 // check for standard single-level BD files (like "001.bd")
2335 if (strSuffixLower(basename, ".bd") &&
2336 strlen(basename) == 6 &&
2337 basename[0] >= '0' && basename[0] <= '9' &&
2338 basename[1] >= '0' && basename[1] <= '9' &&
2339 basename[2] >= '0' && basename[2] <= '9')
2342 // this is a level package in native BD file format
2346 static char *getLevelFilenameFromBasename(char *basename)
2348 static char *filename = NULL;
2350 checked_free(filename);
2352 filename = getPath2(getCurrentLevelDir(), basename);
2357 static int getFileTypeFromBasename(char *basename)
2359 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2361 static char *filename = NULL;
2362 struct stat file_status;
2364 // ---------- try to determine file type from filename ----------
2366 // check for typical filename of a Supaplex level package file
2367 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2368 return LEVEL_FILE_TYPE_SP;
2370 // check for typical filename of a Diamond Caves II level package file
2371 if (strSuffixLower(basename, ".dc") ||
2372 strSuffixLower(basename, ".dc2"))
2373 return LEVEL_FILE_TYPE_DC;
2375 // check for typical filename of a Sokoban level package file
2376 if (strSuffixLower(basename, ".xsb") &&
2377 strchr(basename, '%') == NULL)
2378 return LEVEL_FILE_TYPE_SB;
2380 // check for typical filename of a Boulder Dash (GDash) level package file
2381 if (checkForPackageFromBasename_BD(basename))
2382 return LEVEL_FILE_TYPE_BD;
2384 // ---------- try to determine file type from filesize ----------
2386 checked_free(filename);
2387 filename = getPath2(getCurrentLevelDir(), basename);
2389 if (stat(filename, &file_status) == 0)
2391 // check for typical filesize of a Supaplex level package file
2392 if (file_status.st_size == 170496)
2393 return LEVEL_FILE_TYPE_SP;
2396 return LEVEL_FILE_TYPE_UNKNOWN;
2399 static int getFileTypeFromMagicBytes(char *filename, int type)
2403 if ((file = openFile(filename, MODE_READ)))
2405 char chunk_name[CHUNK_ID_LEN + 1];
2407 getFileChunkBE(file, chunk_name, NULL);
2409 if (strEqual(chunk_name, "MMII") ||
2410 strEqual(chunk_name, "MIRR"))
2411 type = LEVEL_FILE_TYPE_MM;
2419 static boolean checkForPackageFromBasename(char *basename)
2421 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2422 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2424 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2427 static char *getSingleLevelBasenameExt(int nr, char *extension)
2429 static char basename[MAX_FILENAME_LEN];
2432 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2434 sprintf(basename, "%03d.%s", nr, extension);
2439 static char *getSingleLevelBasename(int nr)
2441 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2444 static char *getPackedLevelBasename(int type)
2446 static char basename[MAX_FILENAME_LEN];
2447 char *directory = getCurrentLevelDir();
2449 DirectoryEntry *dir_entry;
2451 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2453 if ((dir = openDirectory(directory)) == NULL)
2455 Warn("cannot read current level directory '%s'", directory);
2460 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2462 char *entry_basename = dir_entry->basename;
2463 int entry_type = getFileTypeFromBasename(entry_basename);
2465 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2467 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2470 strcpy(basename, entry_basename);
2477 closeDirectory(dir);
2482 static char *getSingleLevelFilename(int nr)
2484 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2487 #if ENABLE_UNUSED_CODE
2488 static char *getPackedLevelFilename(int type)
2490 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2494 char *getDefaultLevelFilename(int nr)
2496 return getSingleLevelFilename(nr);
2499 #if ENABLE_UNUSED_CODE
2500 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2504 lfi->packed = FALSE;
2506 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2507 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2511 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2512 int type, char *format, ...)
2514 static char basename[MAX_FILENAME_LEN];
2517 va_start(ap, format);
2518 vsprintf(basename, format, ap);
2522 lfi->packed = FALSE;
2524 setString(&lfi->basename, basename);
2525 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2528 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2534 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2535 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2538 static int getFiletypeFromID(char *filetype_id)
2540 char *filetype_id_lower;
2541 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2544 if (filetype_id == NULL)
2545 return LEVEL_FILE_TYPE_UNKNOWN;
2547 filetype_id_lower = getStringToLower(filetype_id);
2549 for (i = 0; filetype_id_list[i].id != NULL; i++)
2551 char *id_lower = getStringToLower(filetype_id_list[i].id);
2553 if (strEqual(filetype_id_lower, id_lower))
2554 filetype = filetype_id_list[i].filetype;
2558 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2562 free(filetype_id_lower);
2567 char *getLocalLevelTemplateFilename(void)
2569 return getDefaultLevelFilename(-1);
2572 char *getGlobalLevelTemplateFilename(void)
2574 // global variable "leveldir_current" must be modified in the loop below
2575 LevelDirTree *leveldir_current_last = leveldir_current;
2576 char *filename = NULL;
2578 // check for template level in path from current to topmost tree node
2580 while (leveldir_current != NULL)
2582 filename = getDefaultLevelFilename(-1);
2584 if (fileExists(filename))
2587 leveldir_current = leveldir_current->node_parent;
2590 // restore global variable "leveldir_current" modified in above loop
2591 leveldir_current = leveldir_current_last;
2596 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2600 // special case: level number is negative => check for level template file
2603 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2604 getSingleLevelBasename(-1));
2606 // replace local level template filename with global template filename
2607 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2609 // no fallback if template file not existing
2613 // special case: check for file name/pattern specified in "levelinfo.conf"
2614 if (leveldir_current->level_filename != NULL)
2616 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2618 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2619 leveldir_current->level_filename, nr);
2621 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2623 if (fileExists(lfi->filename))
2626 else if (leveldir_current->level_filetype != NULL)
2628 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2630 // check for specified native level file with standard file name
2631 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2632 "%03d.%s", nr, LEVELFILE_EXTENSION);
2633 if (fileExists(lfi->filename))
2637 // check for native Rocks'n'Diamonds level file
2638 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2639 "%03d.%s", nr, LEVELFILE_EXTENSION);
2640 if (fileExists(lfi->filename))
2643 // check for native Boulder Dash level file
2644 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2645 if (fileExists(lfi->filename))
2648 // check for Emerald Mine level file (V1)
2649 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2650 'a' + (nr / 10) % 26, '0' + nr % 10);
2651 if (fileExists(lfi->filename))
2653 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2654 'A' + (nr / 10) % 26, '0' + nr % 10);
2655 if (fileExists(lfi->filename))
2658 // check for Emerald Mine level file (V2 to V5)
2659 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2660 if (fileExists(lfi->filename))
2663 // check for Emerald Mine level file (V6 / single mode)
2664 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2665 if (fileExists(lfi->filename))
2667 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2668 if (fileExists(lfi->filename))
2671 // check for Emerald Mine level file (V6 / teamwork mode)
2672 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2673 if (fileExists(lfi->filename))
2675 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2676 if (fileExists(lfi->filename))
2679 // check for various packed level file formats
2680 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2681 if (fileExists(lfi->filename))
2684 // no known level file found -- use default values (and fail later)
2685 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2686 "%03d.%s", nr, LEVELFILE_EXTENSION);
2689 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2691 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2692 lfi->type = getFileTypeFromBasename(lfi->basename);
2694 if (lfi->type == LEVEL_FILE_TYPE_RND)
2695 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2698 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2700 // always start with reliable default values
2701 setFileInfoToDefaults(level_file_info);
2703 level_file_info->nr = nr; // set requested level number
2705 determineLevelFileInfo_Filename(level_file_info);
2706 determineLevelFileInfo_Filetype(level_file_info);
2709 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2710 struct LevelFileInfo *lfi_to)
2712 lfi_to->nr = lfi_from->nr;
2713 lfi_to->type = lfi_from->type;
2714 lfi_to->packed = lfi_from->packed;
2716 setString(&lfi_to->basename, lfi_from->basename);
2717 setString(&lfi_to->filename, lfi_from->filename);
2720 // ----------------------------------------------------------------------------
2721 // functions for loading R'n'D level
2722 // ----------------------------------------------------------------------------
2724 int getMappedElement(int element)
2726 // remap some (historic, now obsolete) elements
2730 case EL_PLAYER_OBSOLETE:
2731 element = EL_PLAYER_1;
2734 case EL_KEY_OBSOLETE:
2738 case EL_EM_KEY_1_FILE_OBSOLETE:
2739 element = EL_EM_KEY_1;
2742 case EL_EM_KEY_2_FILE_OBSOLETE:
2743 element = EL_EM_KEY_2;
2746 case EL_EM_KEY_3_FILE_OBSOLETE:
2747 element = EL_EM_KEY_3;
2750 case EL_EM_KEY_4_FILE_OBSOLETE:
2751 element = EL_EM_KEY_4;
2754 case EL_ENVELOPE_OBSOLETE:
2755 element = EL_ENVELOPE_1;
2763 if (element >= NUM_FILE_ELEMENTS)
2765 Warn("invalid level element %d", element);
2767 element = EL_UNKNOWN;
2775 static int getMappedElementByVersion(int element, int game_version)
2777 // remap some elements due to certain game version
2779 if (game_version <= VERSION_IDENT(2,2,0,0))
2781 // map game font elements
2782 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2783 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2784 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2785 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2788 if (game_version < VERSION_IDENT(3,0,0,0))
2790 // map Supaplex gravity tube elements
2791 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2792 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2793 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2794 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2801 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2803 level->file_version = getFileVersion(file);
2804 level->game_version = getFileVersion(file);
2809 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2811 level->creation_date.year = getFile16BitBE(file);
2812 level->creation_date.month = getFile8Bit(file);
2813 level->creation_date.day = getFile8Bit(file);
2815 level->creation_date.src = DATE_SRC_LEVELFILE;
2820 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2822 int initial_player_stepsize;
2823 int initial_player_gravity;
2826 level->fieldx = getFile8Bit(file);
2827 level->fieldy = getFile8Bit(file);
2829 level->time = getFile16BitBE(file);
2830 level->gems_needed = getFile16BitBE(file);
2832 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2833 level->name[i] = getFile8Bit(file);
2834 level->name[MAX_LEVEL_NAME_LEN] = 0;
2836 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2837 level->score[i] = getFile8Bit(file);
2839 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2840 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2841 for (y = 0; y < 3; y++)
2842 for (x = 0; x < 3; x++)
2843 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2845 level->amoeba_speed = getFile8Bit(file);
2846 level->time_magic_wall = getFile8Bit(file);
2847 level->time_wheel = getFile8Bit(file);
2848 level->amoeba_content = getMappedElement(getFile8Bit(file));
2850 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2853 for (i = 0; i < MAX_PLAYERS; i++)
2854 level->initial_player_stepsize[i] = initial_player_stepsize;
2856 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2858 for (i = 0; i < MAX_PLAYERS; i++)
2859 level->initial_player_gravity[i] = initial_player_gravity;
2861 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2862 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2864 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2866 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2867 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2868 level->can_move_into_acid_bits = getFile32BitBE(file);
2869 level->dont_collide_with_bits = getFile8Bit(file);
2871 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2872 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2874 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2875 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2876 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2878 level->game_engine_type = getFile8Bit(file);
2880 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2885 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2889 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2890 level->name[i] = getFile8Bit(file);
2891 level->name[MAX_LEVEL_NAME_LEN] = 0;
2896 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2900 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2901 level->author[i] = getFile8Bit(file);
2902 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2907 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2910 int chunk_size_expected = level->fieldx * level->fieldy;
2912 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2913 stored with 16-bit encoding (and should be twice as big then).
2914 Even worse, playfield data was stored 16-bit when only yamyam content
2915 contained 16-bit elements and vice versa. */
2917 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2918 chunk_size_expected *= 2;
2920 if (chunk_size_expected != chunk_size)
2922 ReadUnusedBytesFromFile(file, chunk_size);
2923 return chunk_size_expected;
2926 for (y = 0; y < level->fieldy; y++)
2927 for (x = 0; x < level->fieldx; x++)
2928 level->field[x][y] =
2929 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2934 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2937 int header_size = 4;
2938 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2939 int chunk_size_expected = header_size + content_size;
2941 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2942 stored with 16-bit encoding (and should be twice as big then).
2943 Even worse, playfield data was stored 16-bit when only yamyam content
2944 contained 16-bit elements and vice versa. */
2946 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2947 chunk_size_expected += content_size;
2949 if (chunk_size_expected != chunk_size)
2951 ReadUnusedBytesFromFile(file, chunk_size);
2952 return chunk_size_expected;
2956 level->num_yamyam_contents = getFile8Bit(file);
2960 // correct invalid number of content fields -- should never happen
2961 if (level->num_yamyam_contents < 1 ||
2962 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2963 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2965 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2966 for (y = 0; y < 3; y++)
2967 for (x = 0; x < 3; x++)
2968 level->yamyam_content[i].e[x][y] =
2969 getMappedElement(level->encoding_16bit_field ?
2970 getFile16BitBE(file) : getFile8Bit(file));
2974 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2979 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2981 element = getMappedElement(getFile16BitBE(file));
2982 num_contents = getFile8Bit(file);
2984 getFile8Bit(file); // content x size (unused)
2985 getFile8Bit(file); // content y size (unused)
2987 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2989 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2990 for (y = 0; y < 3; y++)
2991 for (x = 0; x < 3; x++)
2992 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2994 // correct invalid number of content fields -- should never happen
2995 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2996 num_contents = STD_ELEMENT_CONTENTS;
2998 if (element == EL_YAMYAM)
3000 level->num_yamyam_contents = num_contents;
3002 for (i = 0; i < num_contents; i++)
3003 for (y = 0; y < 3; y++)
3004 for (x = 0; x < 3; x++)
3005 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3007 else if (element == EL_BD_AMOEBA)
3009 level->amoeba_content = content_array[0][0][0];
3013 Warn("cannot load content for element '%d'", element);
3019 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3025 int chunk_size_expected;
3027 element = getMappedElement(getFile16BitBE(file));
3028 if (!IS_ENVELOPE(element))
3029 element = EL_ENVELOPE_1;
3031 envelope_nr = element - EL_ENVELOPE_1;
3033 envelope_len = getFile16BitBE(file);
3035 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3036 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3038 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3040 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3041 if (chunk_size_expected != chunk_size)
3043 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3044 return chunk_size_expected;
3047 for (i = 0; i < envelope_len; i++)
3048 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3053 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3055 int num_changed_custom_elements = getFile16BitBE(file);
3056 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3059 if (chunk_size_expected != chunk_size)
3061 ReadUnusedBytesFromFile(file, chunk_size - 2);
3062 return chunk_size_expected;
3065 for (i = 0; i < num_changed_custom_elements; i++)
3067 int element = getMappedElement(getFile16BitBE(file));
3068 int properties = getFile32BitBE(file);
3070 if (IS_CUSTOM_ELEMENT(element))
3071 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3073 Warn("invalid custom element number %d", element);
3075 // older game versions that wrote level files with CUS1 chunks used
3076 // different default push delay values (not yet stored in level file)
3077 element_info[element].push_delay_fixed = 2;
3078 element_info[element].push_delay_random = 8;
3081 level->file_has_custom_elements = TRUE;
3086 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3088 int num_changed_custom_elements = getFile16BitBE(file);
3089 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3092 if (chunk_size_expected != chunk_size)
3094 ReadUnusedBytesFromFile(file, chunk_size - 2);
3095 return chunk_size_expected;
3098 for (i = 0; i < num_changed_custom_elements; i++)
3100 int element = getMappedElement(getFile16BitBE(file));
3101 int custom_target_element = getMappedElement(getFile16BitBE(file));
3103 if (IS_CUSTOM_ELEMENT(element))
3104 element_info[element].change->target_element = custom_target_element;
3106 Warn("invalid custom element number %d", element);
3109 level->file_has_custom_elements = TRUE;
3114 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3116 int num_changed_custom_elements = getFile16BitBE(file);
3117 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3120 if (chunk_size_expected != chunk_size)
3122 ReadUnusedBytesFromFile(file, chunk_size - 2);
3123 return chunk_size_expected;
3126 for (i = 0; i < num_changed_custom_elements; i++)
3128 int element = getMappedElement(getFile16BitBE(file));
3129 struct ElementInfo *ei = &element_info[element];
3130 unsigned int event_bits;
3132 if (!IS_CUSTOM_ELEMENT(element))
3134 Warn("invalid custom element number %d", element);
3136 element = EL_INTERNAL_DUMMY;
3139 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3140 ei->description[j] = getFile8Bit(file);
3141 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3143 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3145 // some free bytes for future properties and padding
3146 ReadUnusedBytesFromFile(file, 7);
3148 ei->use_gfx_element = getFile8Bit(file);
3149 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3151 ei->collect_score_initial = getFile8Bit(file);
3152 ei->collect_count_initial = getFile8Bit(file);
3154 ei->push_delay_fixed = getFile16BitBE(file);
3155 ei->push_delay_random = getFile16BitBE(file);
3156 ei->move_delay_fixed = getFile16BitBE(file);
3157 ei->move_delay_random = getFile16BitBE(file);
3159 ei->move_pattern = getFile16BitBE(file);
3160 ei->move_direction_initial = getFile8Bit(file);
3161 ei->move_stepsize = getFile8Bit(file);
3163 for (y = 0; y < 3; y++)
3164 for (x = 0; x < 3; x++)
3165 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3167 // bits 0 - 31 of "has_event[]"
3168 event_bits = getFile32BitBE(file);
3169 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3170 if (event_bits & (1u << j))
3171 ei->change->has_event[j] = TRUE;
3173 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3175 ei->change->delay_fixed = getFile16BitBE(file);
3176 ei->change->delay_random = getFile16BitBE(file);
3177 ei->change->delay_frames = getFile16BitBE(file);
3179 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3181 ei->change->explode = getFile8Bit(file);
3182 ei->change->use_target_content = getFile8Bit(file);
3183 ei->change->only_if_complete = getFile8Bit(file);
3184 ei->change->use_random_replace = getFile8Bit(file);
3186 ei->change->random_percentage = getFile8Bit(file);
3187 ei->change->replace_when = getFile8Bit(file);
3189 for (y = 0; y < 3; y++)
3190 for (x = 0; x < 3; x++)
3191 ei->change->target_content.e[x][y] =
3192 getMappedElement(getFile16BitBE(file));
3194 ei->slippery_type = getFile8Bit(file);
3196 // some free bytes for future properties and padding
3197 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3199 // mark that this custom element has been modified
3200 ei->modified_settings = TRUE;
3203 level->file_has_custom_elements = TRUE;
3208 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3210 struct ElementInfo *ei;
3211 int chunk_size_expected;
3215 // ---------- custom element base property values (96 bytes) ----------------
3217 element = getMappedElement(getFile16BitBE(file));
3219 if (!IS_CUSTOM_ELEMENT(element))
3221 Warn("invalid custom element number %d", element);
3223 ReadUnusedBytesFromFile(file, chunk_size - 2);
3228 ei = &element_info[element];
3230 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3231 ei->description[i] = getFile8Bit(file);
3232 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3234 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3236 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3238 ei->num_change_pages = getFile8Bit(file);
3240 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3241 if (chunk_size_expected != chunk_size)
3243 ReadUnusedBytesFromFile(file, chunk_size - 43);
3244 return chunk_size_expected;
3247 ei->ce_value_fixed_initial = getFile16BitBE(file);
3248 ei->ce_value_random_initial = getFile16BitBE(file);
3249 ei->use_last_ce_value = getFile8Bit(file);
3251 ei->use_gfx_element = getFile8Bit(file);
3252 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3254 ei->collect_score_initial = getFile8Bit(file);
3255 ei->collect_count_initial = getFile8Bit(file);
3257 ei->drop_delay_fixed = getFile8Bit(file);
3258 ei->push_delay_fixed = getFile8Bit(file);
3259 ei->drop_delay_random = getFile8Bit(file);
3260 ei->push_delay_random = getFile8Bit(file);
3261 ei->move_delay_fixed = getFile16BitBE(file);
3262 ei->move_delay_random = getFile16BitBE(file);
3264 // bits 0 - 15 of "move_pattern" ...
3265 ei->move_pattern = getFile16BitBE(file);
3266 ei->move_direction_initial = getFile8Bit(file);
3267 ei->move_stepsize = getFile8Bit(file);
3269 ei->slippery_type = getFile8Bit(file);
3271 for (y = 0; y < 3; y++)
3272 for (x = 0; x < 3; x++)
3273 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3275 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3276 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3277 ei->move_leave_type = getFile8Bit(file);
3279 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3280 ei->move_pattern |= (getFile16BitBE(file) << 16);
3282 ei->access_direction = getFile8Bit(file);
3284 ei->explosion_delay = getFile8Bit(file);
3285 ei->ignition_delay = getFile8Bit(file);
3286 ei->explosion_type = getFile8Bit(file);
3288 // some free bytes for future custom property values and padding
3289 ReadUnusedBytesFromFile(file, 1);
3291 // ---------- change page property values (48 bytes) ------------------------
3293 setElementChangePages(ei, ei->num_change_pages);
3295 for (i = 0; i < ei->num_change_pages; i++)
3297 struct ElementChangeInfo *change = &ei->change_page[i];
3298 unsigned int event_bits;
3300 // always start with reliable default values
3301 setElementChangeInfoToDefaults(change);
3303 // bits 0 - 31 of "has_event[]" ...
3304 event_bits = getFile32BitBE(file);
3305 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3306 if (event_bits & (1u << j))
3307 change->has_event[j] = TRUE;
3309 change->target_element = getMappedElement(getFile16BitBE(file));
3311 change->delay_fixed = getFile16BitBE(file);
3312 change->delay_random = getFile16BitBE(file);
3313 change->delay_frames = getFile16BitBE(file);
3315 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3317 change->explode = getFile8Bit(file);
3318 change->use_target_content = getFile8Bit(file);
3319 change->only_if_complete = getFile8Bit(file);
3320 change->use_random_replace = getFile8Bit(file);
3322 change->random_percentage = getFile8Bit(file);
3323 change->replace_when = getFile8Bit(file);
3325 for (y = 0; y < 3; y++)
3326 for (x = 0; x < 3; x++)
3327 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3329 change->can_change = getFile8Bit(file);
3331 change->trigger_side = getFile8Bit(file);
3333 change->trigger_player = getFile8Bit(file);
3334 change->trigger_page = getFile8Bit(file);
3336 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3337 CH_PAGE_ANY : (1 << change->trigger_page));
3339 change->has_action = getFile8Bit(file);
3340 change->action_type = getFile8Bit(file);
3341 change->action_mode = getFile8Bit(file);
3342 change->action_arg = getFile16BitBE(file);
3344 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3345 event_bits = getFile8Bit(file);
3346 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3347 if (event_bits & (1u << (j - 32)))
3348 change->has_event[j] = TRUE;
3351 // mark this custom element as modified
3352 ei->modified_settings = TRUE;
3354 level->file_has_custom_elements = TRUE;
3359 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3361 struct ElementInfo *ei;
3362 struct ElementGroupInfo *group;
3366 element = getMappedElement(getFile16BitBE(file));
3368 if (!IS_GROUP_ELEMENT(element))
3370 Warn("invalid group element number %d", element);
3372 ReadUnusedBytesFromFile(file, chunk_size - 2);
3377 ei = &element_info[element];
3379 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3380 ei->description[i] = getFile8Bit(file);
3381 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3383 group = element_info[element].group;
3385 group->num_elements = getFile8Bit(file);
3387 ei->use_gfx_element = getFile8Bit(file);
3388 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3390 group->choice_mode = getFile8Bit(file);
3392 // some free bytes for future values and padding
3393 ReadUnusedBytesFromFile(file, 3);
3395 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3396 group->element[i] = getMappedElement(getFile16BitBE(file));
3398 // mark this group element as modified
3399 element_info[element].modified_settings = TRUE;
3401 level->file_has_custom_elements = TRUE;
3406 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3407 int element, int real_element)
3409 int micro_chunk_size = 0;
3410 int conf_type = getFile8Bit(file);
3411 int byte_mask = conf_type & CONF_MASK_BYTES;
3412 boolean element_found = FALSE;
3415 micro_chunk_size += 1;
3417 if (byte_mask == CONF_MASK_MULTI_BYTES)
3419 int num_bytes = getFile16BitBE(file);
3420 byte *buffer = checked_malloc(num_bytes);
3422 ReadBytesFromFile(file, buffer, num_bytes);
3424 for (i = 0; conf[i].data_type != -1; i++)
3426 if (conf[i].element == element &&
3427 conf[i].conf_type == conf_type)
3429 int data_type = conf[i].data_type;
3430 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3431 int max_num_entities = conf[i].max_num_entities;
3433 if (num_entities > max_num_entities)
3435 Warn("truncating number of entities for element %d from %d to %d",
3436 element, num_entities, max_num_entities);
3438 num_entities = max_num_entities;
3441 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3442 data_type == TYPE_CONTENT_LIST))
3444 // for element and content lists, zero entities are not allowed
3445 Warn("found empty list of entities for element %d", element);
3447 // do not set "num_entities" here to prevent reading behind buffer
3449 *(int *)(conf[i].num_entities) = 1; // at least one is required
3453 *(int *)(conf[i].num_entities) = num_entities;
3456 element_found = TRUE;
3458 if (data_type == TYPE_STRING)
3460 char *string = (char *)(conf[i].value);
3463 for (j = 0; j < max_num_entities; j++)
3464 string[j] = (j < num_entities ? buffer[j] : '\0');
3466 else if (data_type == TYPE_ELEMENT_LIST)
3468 int *element_array = (int *)(conf[i].value);
3471 for (j = 0; j < num_entities; j++)
3473 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3475 else if (data_type == TYPE_CONTENT_LIST)
3477 struct Content *content= (struct Content *)(conf[i].value);
3480 for (c = 0; c < num_entities; c++)
3481 for (y = 0; y < 3; y++)
3482 for (x = 0; x < 3; x++)
3483 content[c].e[x][y] =
3484 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3487 element_found = FALSE;
3493 checked_free(buffer);
3495 micro_chunk_size += 2 + num_bytes;
3497 else // constant size configuration data (1, 2 or 4 bytes)
3499 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3500 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3501 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3503 for (i = 0; conf[i].data_type != -1; i++)
3505 if (conf[i].element == element &&
3506 conf[i].conf_type == conf_type)
3508 int data_type = conf[i].data_type;
3510 if (data_type == TYPE_ELEMENT)
3511 value = getMappedElement(value);
3513 if (data_type == TYPE_BOOLEAN)
3514 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3516 *(int *) (conf[i].value) = value;
3518 element_found = TRUE;
3524 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3529 char *error_conf_chunk_bytes =
3530 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3531 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3532 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3533 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3534 int error_element = real_element;
3536 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3537 error_conf_chunk_bytes, error_conf_chunk_token,
3538 error_element, EL_NAME(error_element));
3541 return micro_chunk_size;
3544 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3546 int real_chunk_size = 0;
3548 li = *level; // copy level data into temporary buffer
3550 while (!checkEndOfFile(file))
3552 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3554 if (real_chunk_size >= chunk_size)
3558 *level = li; // copy temporary buffer back to level data
3560 return real_chunk_size;
3563 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3565 int real_chunk_size = 0;
3567 li = *level; // copy level data into temporary buffer
3569 while (!checkEndOfFile(file))
3571 int element = getMappedElement(getFile16BitBE(file));
3573 real_chunk_size += 2;
3574 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3576 if (real_chunk_size >= chunk_size)
3580 *level = li; // copy temporary buffer back to level data
3582 return real_chunk_size;
3585 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3587 int real_chunk_size = 0;
3589 li = *level; // copy level data into temporary buffer
3591 while (!checkEndOfFile(file))
3593 int element = getMappedElement(getFile16BitBE(file));
3595 real_chunk_size += 2;
3596 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3598 if (real_chunk_size >= chunk_size)
3602 *level = li; // copy temporary buffer back to level data
3604 return real_chunk_size;
3607 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3609 int element = getMappedElement(getFile16BitBE(file));
3610 int envelope_nr = element - EL_ENVELOPE_1;
3611 int real_chunk_size = 2;
3613 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3615 while (!checkEndOfFile(file))
3617 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3620 if (real_chunk_size >= chunk_size)
3624 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3626 return real_chunk_size;
3629 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3631 int element = getMappedElement(getFile16BitBE(file));
3632 int real_chunk_size = 2;
3633 struct ElementInfo *ei = &element_info[element];
3636 xx_ei = *ei; // copy element data into temporary buffer
3638 xx_ei.num_change_pages = -1;
3640 while (!checkEndOfFile(file))
3642 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3644 if (xx_ei.num_change_pages != -1)
3647 if (real_chunk_size >= chunk_size)
3653 if (ei->num_change_pages == -1)
3655 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3658 ei->num_change_pages = 1;
3660 setElementChangePages(ei, 1);
3661 setElementChangeInfoToDefaults(ei->change);
3663 return real_chunk_size;
3666 // initialize number of change pages stored for this custom element
3667 setElementChangePages(ei, ei->num_change_pages);
3668 for (i = 0; i < ei->num_change_pages; i++)
3669 setElementChangeInfoToDefaults(&ei->change_page[i]);
3671 // start with reading properties for the first change page
3672 xx_current_change_page = 0;
3674 while (!checkEndOfFile(file))
3676 // level file might contain invalid change page number
3677 if (xx_current_change_page >= ei->num_change_pages)
3680 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3682 xx_change = *change; // copy change data into temporary buffer
3684 resetEventBits(); // reset bits; change page might have changed
3686 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3689 *change = xx_change;
3691 setEventFlagsFromEventBits(change);
3693 if (real_chunk_size >= chunk_size)
3697 level->file_has_custom_elements = TRUE;
3699 return real_chunk_size;
3702 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3704 int element = getMappedElement(getFile16BitBE(file));
3705 int real_chunk_size = 2;
3706 struct ElementInfo *ei = &element_info[element];
3707 struct ElementGroupInfo *group = ei->group;
3712 xx_ei = *ei; // copy element data into temporary buffer
3713 xx_group = *group; // copy group data into temporary buffer
3715 while (!checkEndOfFile(file))
3717 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3720 if (real_chunk_size >= chunk_size)
3727 level->file_has_custom_elements = TRUE;
3729 return real_chunk_size;
3732 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3734 int element = getMappedElement(getFile16BitBE(file));
3735 int real_chunk_size = 2;
3736 struct ElementInfo *ei = &element_info[element];
3738 xx_ei = *ei; // copy element data into temporary buffer
3740 while (!checkEndOfFile(file))
3742 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3745 if (real_chunk_size >= chunk_size)
3751 level->file_has_custom_elements = TRUE;
3753 return real_chunk_size;
3756 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3757 struct LevelFileInfo *level_file_info,
3758 boolean level_info_only)
3760 char *filename = level_file_info->filename;
3761 char cookie[MAX_LINE_LEN];
3762 char chunk_name[CHUNK_ID_LEN + 1];
3766 if (!(file = openFile(filename, MODE_READ)))
3768 level->no_valid_file = TRUE;
3769 level->no_level_file = TRUE;
3771 if (level_info_only)
3774 Warn("cannot read level '%s' -- using empty level", filename);
3776 if (!setup.editor.use_template_for_new_levels)
3779 // if level file not found, try to initialize level data from template
3780 filename = getGlobalLevelTemplateFilename();
3782 if (!(file = openFile(filename, MODE_READ)))
3785 // default: for empty levels, use level template for custom elements
3786 level->use_custom_template = TRUE;
3788 level->no_valid_file = FALSE;
3791 getFileChunkBE(file, chunk_name, NULL);
3792 if (strEqual(chunk_name, "RND1"))
3794 getFile32BitBE(file); // not used
3796 getFileChunkBE(file, chunk_name, NULL);
3797 if (!strEqual(chunk_name, "CAVE"))
3799 level->no_valid_file = TRUE;
3801 Warn("unknown format of level file '%s'", filename);
3808 else // check for pre-2.0 file format with cookie string
3810 strcpy(cookie, chunk_name);
3811 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3813 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3814 cookie[strlen(cookie) - 1] = '\0';
3816 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3818 level->no_valid_file = TRUE;
3820 Warn("unknown format of level file '%s'", filename);
3827 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3829 level->no_valid_file = TRUE;
3831 Warn("unsupported version of level file '%s'", filename);
3838 // pre-2.0 level files have no game version, so use file version here
3839 level->game_version = level->file_version;
3842 if (level->file_version < FILE_VERSION_1_2)
3844 // level files from versions before 1.2.0 without chunk structure
3845 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3846 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3854 int (*loader)(File *, int, struct LevelInfo *);
3858 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3859 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3860 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3861 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3862 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3863 { "INFO", -1, LoadLevel_INFO },
3864 { "BODY", -1, LoadLevel_BODY },
3865 { "CONT", -1, LoadLevel_CONT },
3866 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3867 { "CNT3", -1, LoadLevel_CNT3 },
3868 { "CUS1", -1, LoadLevel_CUS1 },
3869 { "CUS2", -1, LoadLevel_CUS2 },
3870 { "CUS3", -1, LoadLevel_CUS3 },
3871 { "CUS4", -1, LoadLevel_CUS4 },
3872 { "GRP1", -1, LoadLevel_GRP1 },
3873 { "CONF", -1, LoadLevel_CONF },
3874 { "ELEM", -1, LoadLevel_ELEM },
3875 { "NOTE", -1, LoadLevel_NOTE },
3876 { "CUSX", -1, LoadLevel_CUSX },
3877 { "GRPX", -1, LoadLevel_GRPX },
3878 { "EMPX", -1, LoadLevel_EMPX },
3883 while (getFileChunkBE(file, chunk_name, &chunk_size))
3887 while (chunk_info[i].name != NULL &&
3888 !strEqual(chunk_name, chunk_info[i].name))
3891 if (chunk_info[i].name == NULL)
3893 Warn("unknown chunk '%s' in level file '%s'",
3894 chunk_name, filename);
3896 ReadUnusedBytesFromFile(file, chunk_size);
3898 else if (chunk_info[i].size != -1 &&
3899 chunk_info[i].size != chunk_size)
3901 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3902 chunk_size, chunk_name, filename);
3904 ReadUnusedBytesFromFile(file, chunk_size);
3908 // call function to load this level chunk
3909 int chunk_size_expected =
3910 (chunk_info[i].loader)(file, chunk_size, level);
3912 if (chunk_size_expected < 0)
3914 Warn("error reading chunk '%s' in level file '%s'",
3915 chunk_name, filename);
3920 // the size of some chunks cannot be checked before reading other
3921 // chunks first (like "HEAD" and "BODY") that contain some header
3922 // information, so check them here
3923 if (chunk_size_expected != chunk_size)
3925 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3926 chunk_size, chunk_name, filename);
3938 // ----------------------------------------------------------------------------
3939 // functions for loading BD level
3940 // ----------------------------------------------------------------------------
3942 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3944 struct LevelInfo_BD *level_bd = level->native_bd_level;
3945 GdCave *cave = NULL; // will be changed below
3946 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3947 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3950 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3952 // cave and map newly allocated when set to defaults above
3953 cave = level_bd->cave;
3956 cave->intermission = level->bd_intermission;
3959 cave->level_time[0] = level->time;
3960 cave->level_diamonds[0] = level->gems_needed;
3963 cave->scheduling = level->bd_scheduling_type;
3964 cave->pal_timing = level->bd_pal_timing;
3965 cave->level_speed[0] = level->bd_cycle_delay_ms;
3966 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3967 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3968 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3971 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3972 cave->diamond_value = level->score[SC_EMERALD];
3973 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3975 // compatibility settings
3976 cave->lineshift = level->bd_line_shifting_borders;
3977 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3978 cave->short_explosions = level->bd_short_explosions;
3979 cave->gravity_affects_all = level->bd_gravity_affects_all;
3981 // player properties
3982 cave->diagonal_movements = level->bd_diagonal_movements;
3983 cave->active_is_first_found = level->bd_topmost_player_active;
3984 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3985 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3986 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3987 cave->snap_element = map_element_RND_to_BD(level->bd_snap_element);
3989 // element properties
3990 cave->level_bonus_time[0] = level->bd_clock_extra_time;
3991 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
3992 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
3993 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
3994 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
3995 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
3996 cave->level_magic_wall_time[0] = level->time_magic_wall;
3997 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
3998 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
3999 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4000 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4001 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4002 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4003 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4004 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4005 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4006 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4007 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4008 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4009 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4011 cave->amoeba_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
4012 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
4013 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
4014 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
4015 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
4016 cave->amoeba_2_looks_like = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
4018 cave->slime_predictable = level->bd_slime_is_predictable;
4019 cave->slime_correct_random = level->bd_slime_correct_random;
4020 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4021 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4022 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4023 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4025 cave->acid_eats_this = map_element_RND_to_BD(level->bd_acid_eats_element);
4026 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4027 cave->acid_turns_to = map_element_RND_to_BD(level->bd_acid_turns_to_element);
4029 cave->biter_delay_frame = level->bd_biter_move_delay;
4030 cave->biter_eat = map_element_RND_to_BD(level->bd_biter_eats_element);
4033 strncpy(cave->name, level->name, sizeof(GdString));
4034 cave->name[sizeof(GdString) - 1] = '\0';
4036 // playfield elements
4037 for (x = 0; x < cave->w; x++)
4038 for (y = 0; y < cave->h; y++)
4039 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
4042 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4044 struct LevelInfo_BD *level_bd = level->native_bd_level;
4045 GdCave *cave = level_bd->cave;
4046 int bd_level_nr = level_bd->level_nr;
4049 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4050 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4053 level->bd_intermission = cave->intermission;
4056 level->time = cave->level_time[bd_level_nr];
4057 level->gems_needed = cave->level_diamonds[bd_level_nr];
4060 level->bd_scheduling_type = cave->scheduling;
4061 level->bd_pal_timing = cave->pal_timing;
4062 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4063 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4064 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4065 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4068 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4069 level->score[SC_EMERALD] = cave->diamond_value;
4070 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4072 // compatibility settings
4073 level->bd_line_shifting_borders = cave->lineshift;
4074 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4075 level->bd_short_explosions = cave->short_explosions;
4076 level->bd_gravity_affects_all = cave->gravity_affects_all;
4078 // player properties
4079 level->bd_diagonal_movements = cave->diagonal_movements;
4080 level->bd_topmost_player_active = cave->active_is_first_found;
4081 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4082 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4083 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4084 level->bd_snap_element = map_element_BD_to_RND(cave->snap_element);
4086 // element properties
4087 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4088 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4089 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4090 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4091 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4092 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4093 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4094 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4095 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4096 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4097 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4098 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4099 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4100 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4101 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4102 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4103 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4104 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4105 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4106 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4108 level->bd_amoeba_content_too_big = map_element_BD_to_RND(cave->amoeba_too_big_effect);
4109 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4110 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4111 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4112 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4113 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4115 level->bd_slime_is_predictable = cave->slime_predictable;
4116 level->bd_slime_correct_random = cave->slime_correct_random;
4117 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4118 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4119 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4120 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4122 level->bd_acid_eats_element = map_element_BD_to_RND(cave->acid_eats_this);
4123 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4124 level->bd_acid_turns_to_element = map_element_BD_to_RND(cave->acid_turns_to);
4126 level->bd_biter_move_delay = cave->biter_delay_frame;
4127 level->bd_biter_eats_element = map_element_BD_to_RND(cave->biter_eat);
4130 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4132 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4133 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4135 // playfield elements
4136 for (x = 0; x < level->fieldx; x++)
4137 for (y = 0; y < level->fieldy; y++)
4138 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4140 checked_free(cave_name);
4143 static void setTapeInfoToDefaults(void);
4145 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4147 struct LevelInfo_BD *level_bd = level->native_bd_level;
4148 GdCave *cave = level_bd->cave;
4149 GdReplay *replay = level_bd->replay;
4155 // always start with reliable default values
4156 setTapeInfoToDefaults();
4158 tape.level_nr = level_nr; // (currently not used)
4159 tape.random_seed = replay->seed;
4161 TapeSetDateFromIsoDateString(replay->date);
4164 tape.pos[tape.counter].delay = 0;
4166 tape.bd_replay = TRUE;
4168 // all time calculations only used to display approximate tape time
4169 int cave_speed = cave->speed;
4170 int milliseconds_game = 0;
4171 int milliseconds_elapsed = 20;
4173 for (i = 0; i < replay->movements->len; i++)
4175 int replay_action = replay->movements->data[i];
4176 int tape_action = map_action_BD_to_RND(replay_action);
4177 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4178 boolean success = 0;
4182 success = TapeAddAction(action);
4184 milliseconds_game += milliseconds_elapsed;
4186 if (milliseconds_game >= cave_speed)
4188 milliseconds_game -= cave_speed;
4195 tape.pos[tape.counter].delay = 0;
4196 tape.pos[tape.counter].action[0] = 0;
4200 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4206 TapeHaltRecording();
4210 // ----------------------------------------------------------------------------
4211 // functions for loading EM level
4212 // ----------------------------------------------------------------------------
4214 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4216 static int ball_xy[8][2] =
4227 struct LevelInfo_EM *level_em = level->native_em_level;
4228 struct CAVE *cav = level_em->cav;
4231 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4232 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4234 cav->time_seconds = level->time;
4235 cav->gems_needed = level->gems_needed;
4237 cav->emerald_score = level->score[SC_EMERALD];
4238 cav->diamond_score = level->score[SC_DIAMOND];
4239 cav->alien_score = level->score[SC_ROBOT];
4240 cav->tank_score = level->score[SC_SPACESHIP];
4241 cav->bug_score = level->score[SC_BUG];
4242 cav->eater_score = level->score[SC_YAMYAM];
4243 cav->nut_score = level->score[SC_NUT];
4244 cav->dynamite_score = level->score[SC_DYNAMITE];
4245 cav->key_score = level->score[SC_KEY];
4246 cav->exit_score = level->score[SC_TIME_BONUS];
4248 cav->num_eater_arrays = level->num_yamyam_contents;
4250 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4251 for (y = 0; y < 3; y++)
4252 for (x = 0; x < 3; x++)
4253 cav->eater_array[i][y * 3 + x] =
4254 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4256 cav->amoeba_time = level->amoeba_speed;
4257 cav->wonderwall_time = level->time_magic_wall;
4258 cav->wheel_time = level->time_wheel;
4260 cav->android_move_time = level->android_move_time;
4261 cav->android_clone_time = level->android_clone_time;
4262 cav->ball_random = level->ball_random;
4263 cav->ball_active = level->ball_active_initial;
4264 cav->ball_time = level->ball_time;
4265 cav->num_ball_arrays = level->num_ball_contents;
4267 cav->lenses_score = level->lenses_score;
4268 cav->magnify_score = level->magnify_score;
4269 cav->slurp_score = level->slurp_score;
4271 cav->lenses_time = level->lenses_time;
4272 cav->magnify_time = level->magnify_time;
4274 cav->wind_time = 9999;
4275 cav->wind_direction =
4276 map_direction_RND_to_EM(level->wind_direction_initial);
4278 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4279 for (j = 0; j < 8; j++)
4280 cav->ball_array[i][j] =
4281 map_element_RND_to_EM_cave(level->ball_content[i].
4282 e[ball_xy[j][0]][ball_xy[j][1]]);
4284 map_android_clone_elements_RND_to_EM(level);
4286 // first fill the complete playfield with the empty space element
4287 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4288 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4289 cav->cave[x][y] = Cblank;
4291 // then copy the real level contents from level file into the playfield
4292 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4294 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4296 if (level->field[x][y] == EL_AMOEBA_DEAD)
4297 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4299 cav->cave[x][y] = new_element;
4302 for (i = 0; i < MAX_PLAYERS; i++)
4304 cav->player_x[i] = -1;
4305 cav->player_y[i] = -1;
4308 // initialize player positions and delete players from the playfield
4309 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4311 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4313 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4315 cav->player_x[player_nr] = x;
4316 cav->player_y[player_nr] = y;
4318 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4323 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4325 static int ball_xy[8][2] =
4336 struct LevelInfo_EM *level_em = level->native_em_level;
4337 struct CAVE *cav = level_em->cav;
4340 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4341 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4343 level->time = cav->time_seconds;
4344 level->gems_needed = cav->gems_needed;
4346 sprintf(level->name, "Level %d", level->file_info.nr);
4348 level->score[SC_EMERALD] = cav->emerald_score;
4349 level->score[SC_DIAMOND] = cav->diamond_score;
4350 level->score[SC_ROBOT] = cav->alien_score;
4351 level->score[SC_SPACESHIP] = cav->tank_score;
4352 level->score[SC_BUG] = cav->bug_score;
4353 level->score[SC_YAMYAM] = cav->eater_score;
4354 level->score[SC_NUT] = cav->nut_score;
4355 level->score[SC_DYNAMITE] = cav->dynamite_score;
4356 level->score[SC_KEY] = cav->key_score;
4357 level->score[SC_TIME_BONUS] = cav->exit_score;
4359 level->num_yamyam_contents = cav->num_eater_arrays;
4361 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4362 for (y = 0; y < 3; y++)
4363 for (x = 0; x < 3; x++)
4364 level->yamyam_content[i].e[x][y] =
4365 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4367 level->amoeba_speed = cav->amoeba_time;
4368 level->time_magic_wall = cav->wonderwall_time;
4369 level->time_wheel = cav->wheel_time;
4371 level->android_move_time = cav->android_move_time;
4372 level->android_clone_time = cav->android_clone_time;
4373 level->ball_random = cav->ball_random;
4374 level->ball_active_initial = cav->ball_active;
4375 level->ball_time = cav->ball_time;
4376 level->num_ball_contents = cav->num_ball_arrays;
4378 level->lenses_score = cav->lenses_score;
4379 level->magnify_score = cav->magnify_score;
4380 level->slurp_score = cav->slurp_score;
4382 level->lenses_time = cav->lenses_time;
4383 level->magnify_time = cav->magnify_time;
4385 level->wind_direction_initial =
4386 map_direction_EM_to_RND(cav->wind_direction);
4388 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4389 for (j = 0; j < 8; j++)
4390 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4391 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4393 map_android_clone_elements_EM_to_RND(level);
4395 // convert the playfield (some elements need special treatment)
4396 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4398 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4400 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4401 new_element = EL_AMOEBA_DEAD;
4403 level->field[x][y] = new_element;
4406 for (i = 0; i < MAX_PLAYERS; i++)
4408 // in case of all players set to the same field, use the first player
4409 int nr = MAX_PLAYERS - i - 1;
4410 int jx = cav->player_x[nr];
4411 int jy = cav->player_y[nr];
4413 if (jx != -1 && jy != -1)
4414 level->field[jx][jy] = EL_PLAYER_1 + nr;
4417 // time score is counted for each 10 seconds left in Emerald Mine levels
4418 level->time_score_base = 10;
4422 // ----------------------------------------------------------------------------
4423 // functions for loading SP level
4424 // ----------------------------------------------------------------------------
4426 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4428 struct LevelInfo_SP *level_sp = level->native_sp_level;
4429 LevelInfoType *header = &level_sp->header;
4432 level_sp->width = level->fieldx;
4433 level_sp->height = level->fieldy;
4435 for (x = 0; x < level->fieldx; x++)
4436 for (y = 0; y < level->fieldy; y++)
4437 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4439 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4441 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4442 header->LevelTitle[i] = level->name[i];
4443 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4445 header->InfotronsNeeded = level->gems_needed;
4447 header->SpecialPortCount = 0;
4449 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4451 boolean gravity_port_found = FALSE;
4452 boolean gravity_port_valid = FALSE;
4453 int gravity_port_flag;
4454 int gravity_port_base_element;
4455 int element = level->field[x][y];
4457 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4458 element <= EL_SP_GRAVITY_ON_PORT_UP)
4460 gravity_port_found = TRUE;
4461 gravity_port_valid = TRUE;
4462 gravity_port_flag = 1;
4463 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4465 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4466 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4468 gravity_port_found = TRUE;
4469 gravity_port_valid = TRUE;
4470 gravity_port_flag = 0;
4471 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4473 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4474 element <= EL_SP_GRAVITY_PORT_UP)
4476 // change R'n'D style gravity inverting special port to normal port
4477 // (there are no gravity inverting ports in native Supaplex engine)
4479 gravity_port_found = TRUE;
4480 gravity_port_valid = FALSE;
4481 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4484 if (gravity_port_found)
4486 if (gravity_port_valid &&
4487 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4489 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4491 port->PortLocation = (y * level->fieldx + x) * 2;
4492 port->Gravity = gravity_port_flag;
4494 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4496 header->SpecialPortCount++;
4500 // change special gravity port to normal port
4502 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4505 level_sp->playfield[x][y] = element - EL_SP_START;
4510 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4512 struct LevelInfo_SP *level_sp = level->native_sp_level;
4513 LevelInfoType *header = &level_sp->header;
4514 boolean num_invalid_elements = 0;
4517 level->fieldx = level_sp->width;
4518 level->fieldy = level_sp->height;
4520 for (x = 0; x < level->fieldx; x++)
4522 for (y = 0; y < level->fieldy; y++)
4524 int element_old = level_sp->playfield[x][y];
4525 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4527 if (element_new == EL_UNKNOWN)
4529 num_invalid_elements++;
4531 Debug("level:native:SP", "invalid element %d at position %d, %d",
4535 level->field[x][y] = element_new;
4539 if (num_invalid_elements > 0)
4540 Warn("found %d invalid elements%s", num_invalid_elements,
4541 (!options.debug ? " (use '--debug' for more details)" : ""));
4543 for (i = 0; i < MAX_PLAYERS; i++)
4544 level->initial_player_gravity[i] =
4545 (header->InitialGravity == 1 ? TRUE : FALSE);
4547 // skip leading spaces
4548 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4549 if (header->LevelTitle[i] != ' ')
4553 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4554 level->name[j] = header->LevelTitle[i];
4555 level->name[j] = '\0';
4557 // cut trailing spaces
4559 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4560 level->name[j - 1] = '\0';
4562 level->gems_needed = header->InfotronsNeeded;
4564 for (i = 0; i < header->SpecialPortCount; i++)
4566 SpecialPortType *port = &header->SpecialPort[i];
4567 int port_location = port->PortLocation;
4568 int gravity = port->Gravity;
4569 int port_x, port_y, port_element;
4571 port_x = (port_location / 2) % level->fieldx;
4572 port_y = (port_location / 2) / level->fieldx;
4574 if (port_x < 0 || port_x >= level->fieldx ||
4575 port_y < 0 || port_y >= level->fieldy)
4577 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4582 port_element = level->field[port_x][port_y];
4584 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4585 port_element > EL_SP_GRAVITY_PORT_UP)
4587 Warn("no special port at position (%d, %d)", port_x, port_y);
4592 // change previous (wrong) gravity inverting special port to either
4593 // gravity enabling special port or gravity disabling special port
4594 level->field[port_x][port_y] +=
4595 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4596 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4599 // change special gravity ports without database entries to normal ports
4600 for (x = 0; x < level->fieldx; x++)
4601 for (y = 0; y < level->fieldy; y++)
4602 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4603 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4604 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4606 level->time = 0; // no time limit
4607 level->amoeba_speed = 0;
4608 level->time_magic_wall = 0;
4609 level->time_wheel = 0;
4610 level->amoeba_content = EL_EMPTY;
4612 // original Supaplex does not use score values -- rate by playing time
4613 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4614 level->score[i] = 0;
4616 level->rate_time_over_score = TRUE;
4618 // there are no yamyams in supaplex levels
4619 for (i = 0; i < level->num_yamyam_contents; i++)
4620 for (x = 0; x < 3; x++)
4621 for (y = 0; y < 3; y++)
4622 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4625 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4627 struct LevelInfo_SP *level_sp = level->native_sp_level;
4628 struct DemoInfo_SP *demo = &level_sp->demo;
4631 // always start with reliable default values
4632 demo->is_available = FALSE;
4635 if (TAPE_IS_EMPTY(tape))
4638 demo->level_nr = tape.level_nr; // (currently not used)
4640 level_sp->header.DemoRandomSeed = tape.random_seed;
4644 for (i = 0; i < tape.length; i++)
4646 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4647 int demo_repeat = tape.pos[i].delay;
4648 int demo_entries = (demo_repeat + 15) / 16;
4650 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4652 Warn("tape truncated: size exceeds maximum SP demo size %d",
4658 for (j = 0; j < demo_repeat / 16; j++)
4659 demo->data[demo->length++] = 0xf0 | demo_action;
4661 if (demo_repeat % 16)
4662 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4665 demo->is_available = TRUE;
4668 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4670 struct LevelInfo_SP *level_sp = level->native_sp_level;
4671 struct DemoInfo_SP *demo = &level_sp->demo;
4672 char *filename = level->file_info.filename;
4675 // always start with reliable default values
4676 setTapeInfoToDefaults();
4678 if (!demo->is_available)
4681 tape.level_nr = demo->level_nr; // (currently not used)
4682 tape.random_seed = level_sp->header.DemoRandomSeed;
4684 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4687 tape.pos[tape.counter].delay = 0;
4689 for (i = 0; i < demo->length; i++)
4691 int demo_action = demo->data[i] & 0x0f;
4692 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4693 int tape_action = map_key_SP_to_RND(demo_action);
4694 int tape_repeat = demo_repeat + 1;
4695 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4696 boolean success = 0;
4699 for (j = 0; j < tape_repeat; j++)
4700 success = TapeAddAction(action);
4704 Warn("SP demo truncated: size exceeds maximum tape size %d",
4711 TapeHaltRecording();
4715 // ----------------------------------------------------------------------------
4716 // functions for loading MM level
4717 // ----------------------------------------------------------------------------
4719 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4721 struct LevelInfo_MM *level_mm = level->native_mm_level;
4724 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4725 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4727 level_mm->time = level->time;
4728 level_mm->kettles_needed = level->gems_needed;
4729 level_mm->auto_count_kettles = level->auto_count_gems;
4731 level_mm->mm_laser_red = level->mm_laser_red;
4732 level_mm->mm_laser_green = level->mm_laser_green;
4733 level_mm->mm_laser_blue = level->mm_laser_blue;
4735 level_mm->df_laser_red = level->df_laser_red;
4736 level_mm->df_laser_green = level->df_laser_green;
4737 level_mm->df_laser_blue = level->df_laser_blue;
4739 strcpy(level_mm->name, level->name);
4740 strcpy(level_mm->author, level->author);
4742 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4743 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4744 level_mm->score[SC_KEY] = level->score[SC_KEY];
4745 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4746 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4748 level_mm->amoeba_speed = level->amoeba_speed;
4749 level_mm->time_fuse = level->mm_time_fuse;
4750 level_mm->time_bomb = level->mm_time_bomb;
4751 level_mm->time_ball = level->mm_time_ball;
4752 level_mm->time_block = level->mm_time_block;
4754 level_mm->num_ball_contents = level->num_mm_ball_contents;
4755 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4756 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4757 level_mm->explode_ball = level->explode_mm_ball;
4759 for (i = 0; i < level->num_mm_ball_contents; i++)
4760 level_mm->ball_content[i] =
4761 map_element_RND_to_MM(level->mm_ball_content[i]);
4763 for (x = 0; x < level->fieldx; x++)
4764 for (y = 0; y < level->fieldy; y++)
4766 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4769 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4771 struct LevelInfo_MM *level_mm = level->native_mm_level;
4774 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4775 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4777 level->time = level_mm->time;
4778 level->gems_needed = level_mm->kettles_needed;
4779 level->auto_count_gems = level_mm->auto_count_kettles;
4781 level->mm_laser_red = level_mm->mm_laser_red;
4782 level->mm_laser_green = level_mm->mm_laser_green;
4783 level->mm_laser_blue = level_mm->mm_laser_blue;
4785 level->df_laser_red = level_mm->df_laser_red;
4786 level->df_laser_green = level_mm->df_laser_green;
4787 level->df_laser_blue = level_mm->df_laser_blue;
4789 strcpy(level->name, level_mm->name);
4791 // only overwrite author from 'levelinfo.conf' if author defined in level
4792 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4793 strcpy(level->author, level_mm->author);
4795 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4796 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4797 level->score[SC_KEY] = level_mm->score[SC_KEY];
4798 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4799 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4801 level->amoeba_speed = level_mm->amoeba_speed;
4802 level->mm_time_fuse = level_mm->time_fuse;
4803 level->mm_time_bomb = level_mm->time_bomb;
4804 level->mm_time_ball = level_mm->time_ball;
4805 level->mm_time_block = level_mm->time_block;
4807 level->num_mm_ball_contents = level_mm->num_ball_contents;
4808 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4809 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4810 level->explode_mm_ball = level_mm->explode_ball;
4812 for (i = 0; i < level->num_mm_ball_contents; i++)
4813 level->mm_ball_content[i] =
4814 map_element_MM_to_RND(level_mm->ball_content[i]);
4816 for (x = 0; x < level->fieldx; x++)
4817 for (y = 0; y < level->fieldy; y++)
4818 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4822 // ----------------------------------------------------------------------------
4823 // functions for loading DC level
4824 // ----------------------------------------------------------------------------
4826 #define DC_LEVEL_HEADER_SIZE 344
4828 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4831 static int last_data_encoded;
4835 int diff_hi, diff_lo;
4836 int data_hi, data_lo;
4837 unsigned short data_decoded;
4841 last_data_encoded = 0;
4848 diff = data_encoded - last_data_encoded;
4849 diff_hi = diff & ~0xff;
4850 diff_lo = diff & 0xff;
4854 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4855 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4856 data_hi = data_hi & 0xff00;
4858 data_decoded = data_hi | data_lo;
4860 last_data_encoded = data_encoded;
4862 offset1 = (offset1 + 1) % 31;
4863 offset2 = offset2 & 0xff;
4865 return data_decoded;
4868 static int getMappedElement_DC(int element)
4876 // 0x0117 - 0x036e: (?)
4879 // 0x042d - 0x0684: (?)
4895 element = EL_CRYSTAL;
4898 case 0x0e77: // quicksand (boulder)
4899 element = EL_QUICKSAND_FAST_FULL;
4902 case 0x0e99: // slow quicksand (boulder)
4903 element = EL_QUICKSAND_FULL;
4907 element = EL_EM_EXIT_OPEN;
4911 element = EL_EM_EXIT_CLOSED;
4915 element = EL_EM_STEEL_EXIT_OPEN;
4919 element = EL_EM_STEEL_EXIT_CLOSED;
4922 case 0x0f4f: // dynamite (lit 1)
4923 element = EL_EM_DYNAMITE_ACTIVE;
4926 case 0x0f57: // dynamite (lit 2)
4927 element = EL_EM_DYNAMITE_ACTIVE;
4930 case 0x0f5f: // dynamite (lit 3)
4931 element = EL_EM_DYNAMITE_ACTIVE;
4934 case 0x0f67: // dynamite (lit 4)
4935 element = EL_EM_DYNAMITE_ACTIVE;
4942 element = EL_AMOEBA_WET;
4946 element = EL_AMOEBA_DROP;
4950 element = EL_DC_MAGIC_WALL;
4954 element = EL_SPACESHIP_UP;
4958 element = EL_SPACESHIP_DOWN;
4962 element = EL_SPACESHIP_LEFT;
4966 element = EL_SPACESHIP_RIGHT;
4970 element = EL_BUG_UP;
4974 element = EL_BUG_DOWN;
4978 element = EL_BUG_LEFT;
4982 element = EL_BUG_RIGHT;
4986 element = EL_MOLE_UP;
4990 element = EL_MOLE_DOWN;
4994 element = EL_MOLE_LEFT;
4998 element = EL_MOLE_RIGHT;
5006 element = EL_YAMYAM_UP;
5010 element = EL_SWITCHGATE_OPEN;
5014 element = EL_SWITCHGATE_CLOSED;
5018 element = EL_DC_SWITCHGATE_SWITCH_UP;
5022 element = EL_TIMEGATE_CLOSED;
5025 case 0x144c: // conveyor belt switch (green)
5026 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5029 case 0x144f: // conveyor belt switch (red)
5030 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5033 case 0x1452: // conveyor belt switch (blue)
5034 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5038 element = EL_CONVEYOR_BELT_3_MIDDLE;
5042 element = EL_CONVEYOR_BELT_3_LEFT;
5046 element = EL_CONVEYOR_BELT_3_RIGHT;
5050 element = EL_CONVEYOR_BELT_1_MIDDLE;
5054 element = EL_CONVEYOR_BELT_1_LEFT;
5058 element = EL_CONVEYOR_BELT_1_RIGHT;
5062 element = EL_CONVEYOR_BELT_4_MIDDLE;
5066 element = EL_CONVEYOR_BELT_4_LEFT;
5070 element = EL_CONVEYOR_BELT_4_RIGHT;
5074 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5078 element = EL_EXPANDABLE_WALL_VERTICAL;
5082 element = EL_EXPANDABLE_WALL_ANY;
5085 case 0x14ce: // growing steel wall (left/right)
5086 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5089 case 0x14df: // growing steel wall (up/down)
5090 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5093 case 0x14e8: // growing steel wall (up/down/left/right)
5094 element = EL_EXPANDABLE_STEELWALL_ANY;
5098 element = EL_SHIELD_DEADLY;
5102 element = EL_EXTRA_TIME;
5110 element = EL_EMPTY_SPACE;
5113 case 0x1578: // quicksand (empty)
5114 element = EL_QUICKSAND_FAST_EMPTY;
5117 case 0x1579: // slow quicksand (empty)
5118 element = EL_QUICKSAND_EMPTY;
5128 element = EL_EM_DYNAMITE;
5131 case 0x15a1: // key (red)
5132 element = EL_EM_KEY_1;
5135 case 0x15a2: // key (yellow)
5136 element = EL_EM_KEY_2;
5139 case 0x15a3: // key (blue)
5140 element = EL_EM_KEY_4;
5143 case 0x15a4: // key (green)
5144 element = EL_EM_KEY_3;
5147 case 0x15a5: // key (white)
5148 element = EL_DC_KEY_WHITE;
5152 element = EL_WALL_SLIPPERY;
5159 case 0x15a8: // wall (not round)
5163 case 0x15a9: // (blue)
5164 element = EL_CHAR_A;
5167 case 0x15aa: // (blue)
5168 element = EL_CHAR_B;
5171 case 0x15ab: // (blue)
5172 element = EL_CHAR_C;
5175 case 0x15ac: // (blue)
5176 element = EL_CHAR_D;
5179 case 0x15ad: // (blue)
5180 element = EL_CHAR_E;
5183 case 0x15ae: // (blue)
5184 element = EL_CHAR_F;
5187 case 0x15af: // (blue)
5188 element = EL_CHAR_G;
5191 case 0x15b0: // (blue)
5192 element = EL_CHAR_H;
5195 case 0x15b1: // (blue)
5196 element = EL_CHAR_I;
5199 case 0x15b2: // (blue)
5200 element = EL_CHAR_J;
5203 case 0x15b3: // (blue)
5204 element = EL_CHAR_K;
5207 case 0x15b4: // (blue)
5208 element = EL_CHAR_L;
5211 case 0x15b5: // (blue)
5212 element = EL_CHAR_M;
5215 case 0x15b6: // (blue)
5216 element = EL_CHAR_N;
5219 case 0x15b7: // (blue)
5220 element = EL_CHAR_O;
5223 case 0x15b8: // (blue)
5224 element = EL_CHAR_P;
5227 case 0x15b9: // (blue)
5228 element = EL_CHAR_Q;
5231 case 0x15ba: // (blue)
5232 element = EL_CHAR_R;
5235 case 0x15bb: // (blue)
5236 element = EL_CHAR_S;
5239 case 0x15bc: // (blue)
5240 element = EL_CHAR_T;
5243 case 0x15bd: // (blue)
5244 element = EL_CHAR_U;
5247 case 0x15be: // (blue)
5248 element = EL_CHAR_V;
5251 case 0x15bf: // (blue)
5252 element = EL_CHAR_W;
5255 case 0x15c0: // (blue)
5256 element = EL_CHAR_X;
5259 case 0x15c1: // (blue)
5260 element = EL_CHAR_Y;
5263 case 0x15c2: // (blue)
5264 element = EL_CHAR_Z;
5267 case 0x15c3: // (blue)
5268 element = EL_CHAR_AUMLAUT;
5271 case 0x15c4: // (blue)
5272 element = EL_CHAR_OUMLAUT;
5275 case 0x15c5: // (blue)
5276 element = EL_CHAR_UUMLAUT;
5279 case 0x15c6: // (blue)
5280 element = EL_CHAR_0;
5283 case 0x15c7: // (blue)
5284 element = EL_CHAR_1;
5287 case 0x15c8: // (blue)
5288 element = EL_CHAR_2;
5291 case 0x15c9: // (blue)
5292 element = EL_CHAR_3;
5295 case 0x15ca: // (blue)
5296 element = EL_CHAR_4;
5299 case 0x15cb: // (blue)
5300 element = EL_CHAR_5;
5303 case 0x15cc: // (blue)
5304 element = EL_CHAR_6;
5307 case 0x15cd: // (blue)
5308 element = EL_CHAR_7;
5311 case 0x15ce: // (blue)
5312 element = EL_CHAR_8;
5315 case 0x15cf: // (blue)
5316 element = EL_CHAR_9;
5319 case 0x15d0: // (blue)
5320 element = EL_CHAR_PERIOD;
5323 case 0x15d1: // (blue)
5324 element = EL_CHAR_EXCLAM;
5327 case 0x15d2: // (blue)
5328 element = EL_CHAR_COLON;
5331 case 0x15d3: // (blue)
5332 element = EL_CHAR_LESS;
5335 case 0x15d4: // (blue)
5336 element = EL_CHAR_GREATER;
5339 case 0x15d5: // (blue)
5340 element = EL_CHAR_QUESTION;
5343 case 0x15d6: // (blue)
5344 element = EL_CHAR_COPYRIGHT;
5347 case 0x15d7: // (blue)
5348 element = EL_CHAR_UP;
5351 case 0x15d8: // (blue)
5352 element = EL_CHAR_DOWN;
5355 case 0x15d9: // (blue)
5356 element = EL_CHAR_BUTTON;
5359 case 0x15da: // (blue)
5360 element = EL_CHAR_PLUS;
5363 case 0x15db: // (blue)
5364 element = EL_CHAR_MINUS;
5367 case 0x15dc: // (blue)
5368 element = EL_CHAR_APOSTROPHE;
5371 case 0x15dd: // (blue)
5372 element = EL_CHAR_PARENLEFT;
5375 case 0x15de: // (blue)
5376 element = EL_CHAR_PARENRIGHT;
5379 case 0x15df: // (green)
5380 element = EL_CHAR_A;
5383 case 0x15e0: // (green)
5384 element = EL_CHAR_B;
5387 case 0x15e1: // (green)
5388 element = EL_CHAR_C;
5391 case 0x15e2: // (green)
5392 element = EL_CHAR_D;
5395 case 0x15e3: // (green)
5396 element = EL_CHAR_E;
5399 case 0x15e4: // (green)
5400 element = EL_CHAR_F;
5403 case 0x15e5: // (green)
5404 element = EL_CHAR_G;
5407 case 0x15e6: // (green)
5408 element = EL_CHAR_H;
5411 case 0x15e7: // (green)
5412 element = EL_CHAR_I;
5415 case 0x15e8: // (green)
5416 element = EL_CHAR_J;
5419 case 0x15e9: // (green)
5420 element = EL_CHAR_K;
5423 case 0x15ea: // (green)
5424 element = EL_CHAR_L;
5427 case 0x15eb: // (green)
5428 element = EL_CHAR_M;
5431 case 0x15ec: // (green)
5432 element = EL_CHAR_N;
5435 case 0x15ed: // (green)
5436 element = EL_CHAR_O;
5439 case 0x15ee: // (green)
5440 element = EL_CHAR_P;
5443 case 0x15ef: // (green)
5444 element = EL_CHAR_Q;
5447 case 0x15f0: // (green)
5448 element = EL_CHAR_R;
5451 case 0x15f1: // (green)
5452 element = EL_CHAR_S;
5455 case 0x15f2: // (green)
5456 element = EL_CHAR_T;
5459 case 0x15f3: // (green)
5460 element = EL_CHAR_U;
5463 case 0x15f4: // (green)
5464 element = EL_CHAR_V;
5467 case 0x15f5: // (green)
5468 element = EL_CHAR_W;
5471 case 0x15f6: // (green)
5472 element = EL_CHAR_X;
5475 case 0x15f7: // (green)
5476 element = EL_CHAR_Y;
5479 case 0x15f8: // (green)
5480 element = EL_CHAR_Z;
5483 case 0x15f9: // (green)
5484 element = EL_CHAR_AUMLAUT;
5487 case 0x15fa: // (green)
5488 element = EL_CHAR_OUMLAUT;
5491 case 0x15fb: // (green)
5492 element = EL_CHAR_UUMLAUT;
5495 case 0x15fc: // (green)
5496 element = EL_CHAR_0;
5499 case 0x15fd: // (green)
5500 element = EL_CHAR_1;
5503 case 0x15fe: // (green)
5504 element = EL_CHAR_2;
5507 case 0x15ff: // (green)
5508 element = EL_CHAR_3;
5511 case 0x1600: // (green)
5512 element = EL_CHAR_4;
5515 case 0x1601: // (green)
5516 element = EL_CHAR_5;
5519 case 0x1602: // (green)
5520 element = EL_CHAR_6;
5523 case 0x1603: // (green)
5524 element = EL_CHAR_7;
5527 case 0x1604: // (green)
5528 element = EL_CHAR_8;
5531 case 0x1605: // (green)
5532 element = EL_CHAR_9;
5535 case 0x1606: // (green)
5536 element = EL_CHAR_PERIOD;
5539 case 0x1607: // (green)
5540 element = EL_CHAR_EXCLAM;
5543 case 0x1608: // (green)
5544 element = EL_CHAR_COLON;
5547 case 0x1609: // (green)
5548 element = EL_CHAR_LESS;
5551 case 0x160a: // (green)
5552 element = EL_CHAR_GREATER;
5555 case 0x160b: // (green)
5556 element = EL_CHAR_QUESTION;
5559 case 0x160c: // (green)
5560 element = EL_CHAR_COPYRIGHT;
5563 case 0x160d: // (green)
5564 element = EL_CHAR_UP;
5567 case 0x160e: // (green)
5568 element = EL_CHAR_DOWN;
5571 case 0x160f: // (green)
5572 element = EL_CHAR_BUTTON;
5575 case 0x1610: // (green)
5576 element = EL_CHAR_PLUS;
5579 case 0x1611: // (green)
5580 element = EL_CHAR_MINUS;
5583 case 0x1612: // (green)
5584 element = EL_CHAR_APOSTROPHE;
5587 case 0x1613: // (green)
5588 element = EL_CHAR_PARENLEFT;
5591 case 0x1614: // (green)
5592 element = EL_CHAR_PARENRIGHT;
5595 case 0x1615: // (blue steel)
5596 element = EL_STEEL_CHAR_A;
5599 case 0x1616: // (blue steel)
5600 element = EL_STEEL_CHAR_B;
5603 case 0x1617: // (blue steel)
5604 element = EL_STEEL_CHAR_C;
5607 case 0x1618: // (blue steel)
5608 element = EL_STEEL_CHAR_D;
5611 case 0x1619: // (blue steel)
5612 element = EL_STEEL_CHAR_E;
5615 case 0x161a: // (blue steel)
5616 element = EL_STEEL_CHAR_F;
5619 case 0x161b: // (blue steel)
5620 element = EL_STEEL_CHAR_G;
5623 case 0x161c: // (blue steel)
5624 element = EL_STEEL_CHAR_H;
5627 case 0x161d: // (blue steel)
5628 element = EL_STEEL_CHAR_I;
5631 case 0x161e: // (blue steel)
5632 element = EL_STEEL_CHAR_J;
5635 case 0x161f: // (blue steel)
5636 element = EL_STEEL_CHAR_K;
5639 case 0x1620: // (blue steel)
5640 element = EL_STEEL_CHAR_L;
5643 case 0x1621: // (blue steel)
5644 element = EL_STEEL_CHAR_M;
5647 case 0x1622: // (blue steel)
5648 element = EL_STEEL_CHAR_N;
5651 case 0x1623: // (blue steel)
5652 element = EL_STEEL_CHAR_O;
5655 case 0x1624: // (blue steel)
5656 element = EL_STEEL_CHAR_P;
5659 case 0x1625: // (blue steel)
5660 element = EL_STEEL_CHAR_Q;
5663 case 0x1626: // (blue steel)
5664 element = EL_STEEL_CHAR_R;
5667 case 0x1627: // (blue steel)
5668 element = EL_STEEL_CHAR_S;
5671 case 0x1628: // (blue steel)
5672 element = EL_STEEL_CHAR_T;
5675 case 0x1629: // (blue steel)
5676 element = EL_STEEL_CHAR_U;
5679 case 0x162a: // (blue steel)
5680 element = EL_STEEL_CHAR_V;
5683 case 0x162b: // (blue steel)
5684 element = EL_STEEL_CHAR_W;
5687 case 0x162c: // (blue steel)
5688 element = EL_STEEL_CHAR_X;
5691 case 0x162d: // (blue steel)
5692 element = EL_STEEL_CHAR_Y;
5695 case 0x162e: // (blue steel)
5696 element = EL_STEEL_CHAR_Z;
5699 case 0x162f: // (blue steel)
5700 element = EL_STEEL_CHAR_AUMLAUT;
5703 case 0x1630: // (blue steel)
5704 element = EL_STEEL_CHAR_OUMLAUT;
5707 case 0x1631: // (blue steel)
5708 element = EL_STEEL_CHAR_UUMLAUT;
5711 case 0x1632: // (blue steel)
5712 element = EL_STEEL_CHAR_0;
5715 case 0x1633: // (blue steel)
5716 element = EL_STEEL_CHAR_1;
5719 case 0x1634: // (blue steel)
5720 element = EL_STEEL_CHAR_2;
5723 case 0x1635: // (blue steel)
5724 element = EL_STEEL_CHAR_3;
5727 case 0x1636: // (blue steel)
5728 element = EL_STEEL_CHAR_4;
5731 case 0x1637: // (blue steel)
5732 element = EL_STEEL_CHAR_5;
5735 case 0x1638: // (blue steel)
5736 element = EL_STEEL_CHAR_6;
5739 case 0x1639: // (blue steel)
5740 element = EL_STEEL_CHAR_7;
5743 case 0x163a: // (blue steel)
5744 element = EL_STEEL_CHAR_8;
5747 case 0x163b: // (blue steel)
5748 element = EL_STEEL_CHAR_9;
5751 case 0x163c: // (blue steel)
5752 element = EL_STEEL_CHAR_PERIOD;
5755 case 0x163d: // (blue steel)
5756 element = EL_STEEL_CHAR_EXCLAM;
5759 case 0x163e: // (blue steel)
5760 element = EL_STEEL_CHAR_COLON;
5763 case 0x163f: // (blue steel)
5764 element = EL_STEEL_CHAR_LESS;
5767 case 0x1640: // (blue steel)
5768 element = EL_STEEL_CHAR_GREATER;
5771 case 0x1641: // (blue steel)
5772 element = EL_STEEL_CHAR_QUESTION;
5775 case 0x1642: // (blue steel)
5776 element = EL_STEEL_CHAR_COPYRIGHT;
5779 case 0x1643: // (blue steel)
5780 element = EL_STEEL_CHAR_UP;
5783 case 0x1644: // (blue steel)
5784 element = EL_STEEL_CHAR_DOWN;
5787 case 0x1645: // (blue steel)
5788 element = EL_STEEL_CHAR_BUTTON;
5791 case 0x1646: // (blue steel)
5792 element = EL_STEEL_CHAR_PLUS;
5795 case 0x1647: // (blue steel)
5796 element = EL_STEEL_CHAR_MINUS;
5799 case 0x1648: // (blue steel)
5800 element = EL_STEEL_CHAR_APOSTROPHE;
5803 case 0x1649: // (blue steel)
5804 element = EL_STEEL_CHAR_PARENLEFT;
5807 case 0x164a: // (blue steel)
5808 element = EL_STEEL_CHAR_PARENRIGHT;
5811 case 0x164b: // (green steel)
5812 element = EL_STEEL_CHAR_A;
5815 case 0x164c: // (green steel)
5816 element = EL_STEEL_CHAR_B;
5819 case 0x164d: // (green steel)
5820 element = EL_STEEL_CHAR_C;
5823 case 0x164e: // (green steel)
5824 element = EL_STEEL_CHAR_D;
5827 case 0x164f: // (green steel)
5828 element = EL_STEEL_CHAR_E;
5831 case 0x1650: // (green steel)
5832 element = EL_STEEL_CHAR_F;
5835 case 0x1651: // (green steel)
5836 element = EL_STEEL_CHAR_G;
5839 case 0x1652: // (green steel)
5840 element = EL_STEEL_CHAR_H;
5843 case 0x1653: // (green steel)
5844 element = EL_STEEL_CHAR_I;
5847 case 0x1654: // (green steel)
5848 element = EL_STEEL_CHAR_J;
5851 case 0x1655: // (green steel)
5852 element = EL_STEEL_CHAR_K;
5855 case 0x1656: // (green steel)
5856 element = EL_STEEL_CHAR_L;
5859 case 0x1657: // (green steel)
5860 element = EL_STEEL_CHAR_M;
5863 case 0x1658: // (green steel)
5864 element = EL_STEEL_CHAR_N;
5867 case 0x1659: // (green steel)
5868 element = EL_STEEL_CHAR_O;
5871 case 0x165a: // (green steel)
5872 element = EL_STEEL_CHAR_P;
5875 case 0x165b: // (green steel)
5876 element = EL_STEEL_CHAR_Q;
5879 case 0x165c: // (green steel)
5880 element = EL_STEEL_CHAR_R;
5883 case 0x165d: // (green steel)
5884 element = EL_STEEL_CHAR_S;
5887 case 0x165e: // (green steel)
5888 element = EL_STEEL_CHAR_T;
5891 case 0x165f: // (green steel)
5892 element = EL_STEEL_CHAR_U;
5895 case 0x1660: // (green steel)
5896 element = EL_STEEL_CHAR_V;
5899 case 0x1661: // (green steel)
5900 element = EL_STEEL_CHAR_W;
5903 case 0x1662: // (green steel)
5904 element = EL_STEEL_CHAR_X;
5907 case 0x1663: // (green steel)
5908 element = EL_STEEL_CHAR_Y;
5911 case 0x1664: // (green steel)
5912 element = EL_STEEL_CHAR_Z;
5915 case 0x1665: // (green steel)
5916 element = EL_STEEL_CHAR_AUMLAUT;
5919 case 0x1666: // (green steel)
5920 element = EL_STEEL_CHAR_OUMLAUT;
5923 case 0x1667: // (green steel)
5924 element = EL_STEEL_CHAR_UUMLAUT;
5927 case 0x1668: // (green steel)
5928 element = EL_STEEL_CHAR_0;
5931 case 0x1669: // (green steel)
5932 element = EL_STEEL_CHAR_1;
5935 case 0x166a: // (green steel)
5936 element = EL_STEEL_CHAR_2;
5939 case 0x166b: // (green steel)
5940 element = EL_STEEL_CHAR_3;
5943 case 0x166c: // (green steel)
5944 element = EL_STEEL_CHAR_4;
5947 case 0x166d: // (green steel)
5948 element = EL_STEEL_CHAR_5;
5951 case 0x166e: // (green steel)
5952 element = EL_STEEL_CHAR_6;
5955 case 0x166f: // (green steel)
5956 element = EL_STEEL_CHAR_7;
5959 case 0x1670: // (green steel)
5960 element = EL_STEEL_CHAR_8;
5963 case 0x1671: // (green steel)
5964 element = EL_STEEL_CHAR_9;
5967 case 0x1672: // (green steel)
5968 element = EL_STEEL_CHAR_PERIOD;
5971 case 0x1673: // (green steel)
5972 element = EL_STEEL_CHAR_EXCLAM;
5975 case 0x1674: // (green steel)
5976 element = EL_STEEL_CHAR_COLON;
5979 case 0x1675: // (green steel)
5980 element = EL_STEEL_CHAR_LESS;
5983 case 0x1676: // (green steel)
5984 element = EL_STEEL_CHAR_GREATER;
5987 case 0x1677: // (green steel)
5988 element = EL_STEEL_CHAR_QUESTION;
5991 case 0x1678: // (green steel)
5992 element = EL_STEEL_CHAR_COPYRIGHT;
5995 case 0x1679: // (green steel)
5996 element = EL_STEEL_CHAR_UP;
5999 case 0x167a: // (green steel)
6000 element = EL_STEEL_CHAR_DOWN;
6003 case 0x167b: // (green steel)
6004 element = EL_STEEL_CHAR_BUTTON;
6007 case 0x167c: // (green steel)
6008 element = EL_STEEL_CHAR_PLUS;
6011 case 0x167d: // (green steel)
6012 element = EL_STEEL_CHAR_MINUS;
6015 case 0x167e: // (green steel)
6016 element = EL_STEEL_CHAR_APOSTROPHE;
6019 case 0x167f: // (green steel)
6020 element = EL_STEEL_CHAR_PARENLEFT;
6023 case 0x1680: // (green steel)
6024 element = EL_STEEL_CHAR_PARENRIGHT;
6027 case 0x1681: // gate (red)
6028 element = EL_EM_GATE_1;
6031 case 0x1682: // secret gate (red)
6032 element = EL_EM_GATE_1_GRAY;
6035 case 0x1683: // gate (yellow)
6036 element = EL_EM_GATE_2;
6039 case 0x1684: // secret gate (yellow)
6040 element = EL_EM_GATE_2_GRAY;
6043 case 0x1685: // gate (blue)
6044 element = EL_EM_GATE_4;
6047 case 0x1686: // secret gate (blue)
6048 element = EL_EM_GATE_4_GRAY;
6051 case 0x1687: // gate (green)
6052 element = EL_EM_GATE_3;
6055 case 0x1688: // secret gate (green)
6056 element = EL_EM_GATE_3_GRAY;
6059 case 0x1689: // gate (white)
6060 element = EL_DC_GATE_WHITE;
6063 case 0x168a: // secret gate (white)
6064 element = EL_DC_GATE_WHITE_GRAY;
6067 case 0x168b: // secret gate (no key)
6068 element = EL_DC_GATE_FAKE_GRAY;
6072 element = EL_ROBOT_WHEEL;
6076 element = EL_DC_TIMEGATE_SWITCH;
6080 element = EL_ACID_POOL_BOTTOM;
6084 element = EL_ACID_POOL_TOPLEFT;
6088 element = EL_ACID_POOL_TOPRIGHT;
6092 element = EL_ACID_POOL_BOTTOMLEFT;
6096 element = EL_ACID_POOL_BOTTOMRIGHT;
6100 element = EL_STEELWALL;
6104 element = EL_STEELWALL_SLIPPERY;
6107 case 0x1695: // steel wall (not round)
6108 element = EL_STEELWALL;
6111 case 0x1696: // steel wall (left)
6112 element = EL_DC_STEELWALL_1_LEFT;
6115 case 0x1697: // steel wall (bottom)
6116 element = EL_DC_STEELWALL_1_BOTTOM;
6119 case 0x1698: // steel wall (right)
6120 element = EL_DC_STEELWALL_1_RIGHT;
6123 case 0x1699: // steel wall (top)
6124 element = EL_DC_STEELWALL_1_TOP;
6127 case 0x169a: // steel wall (left/bottom)
6128 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6131 case 0x169b: // steel wall (right/bottom)
6132 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6135 case 0x169c: // steel wall (right/top)
6136 element = EL_DC_STEELWALL_1_TOPRIGHT;
6139 case 0x169d: // steel wall (left/top)
6140 element = EL_DC_STEELWALL_1_TOPLEFT;
6143 case 0x169e: // steel wall (right/bottom small)
6144 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6147 case 0x169f: // steel wall (left/bottom small)
6148 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6151 case 0x16a0: // steel wall (right/top small)
6152 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6155 case 0x16a1: // steel wall (left/top small)
6156 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6159 case 0x16a2: // steel wall (left/right)
6160 element = EL_DC_STEELWALL_1_VERTICAL;
6163 case 0x16a3: // steel wall (top/bottom)
6164 element = EL_DC_STEELWALL_1_HORIZONTAL;
6167 case 0x16a4: // steel wall 2 (left end)
6168 element = EL_DC_STEELWALL_2_LEFT;
6171 case 0x16a5: // steel wall 2 (right end)
6172 element = EL_DC_STEELWALL_2_RIGHT;
6175 case 0x16a6: // steel wall 2 (top end)
6176 element = EL_DC_STEELWALL_2_TOP;
6179 case 0x16a7: // steel wall 2 (bottom end)
6180 element = EL_DC_STEELWALL_2_BOTTOM;
6183 case 0x16a8: // steel wall 2 (left/right)
6184 element = EL_DC_STEELWALL_2_HORIZONTAL;
6187 case 0x16a9: // steel wall 2 (up/down)
6188 element = EL_DC_STEELWALL_2_VERTICAL;
6191 case 0x16aa: // steel wall 2 (mid)
6192 element = EL_DC_STEELWALL_2_MIDDLE;
6196 element = EL_SIGN_EXCLAMATION;
6200 element = EL_SIGN_RADIOACTIVITY;
6204 element = EL_SIGN_STOP;
6208 element = EL_SIGN_WHEELCHAIR;
6212 element = EL_SIGN_PARKING;
6216 element = EL_SIGN_NO_ENTRY;
6220 element = EL_SIGN_HEART;
6224 element = EL_SIGN_GIVE_WAY;
6228 element = EL_SIGN_ENTRY_FORBIDDEN;
6232 element = EL_SIGN_EMERGENCY_EXIT;
6236 element = EL_SIGN_YIN_YANG;
6240 element = EL_WALL_EMERALD;
6244 element = EL_WALL_DIAMOND;
6248 element = EL_WALL_PEARL;
6252 element = EL_WALL_CRYSTAL;
6256 element = EL_INVISIBLE_WALL;
6260 element = EL_INVISIBLE_STEELWALL;
6264 // EL_INVISIBLE_SAND
6267 element = EL_LIGHT_SWITCH;
6271 element = EL_ENVELOPE_1;
6275 if (element >= 0x0117 && element <= 0x036e) // (?)
6276 element = EL_DIAMOND;
6277 else if (element >= 0x042d && element <= 0x0684) // (?)
6278 element = EL_EMERALD;
6279 else if (element >= 0x157c && element <= 0x158b)
6281 else if (element >= 0x1590 && element <= 0x159f)
6282 element = EL_DC_LANDMINE;
6283 else if (element >= 0x16bc && element <= 0x16cb)
6284 element = EL_INVISIBLE_SAND;
6287 Warn("unknown Diamond Caves element 0x%04x", element);
6289 element = EL_UNKNOWN;
6294 return getMappedElement(element);
6297 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6299 byte header[DC_LEVEL_HEADER_SIZE];
6301 int envelope_header_pos = 62;
6302 int envelope_content_pos = 94;
6303 int level_name_pos = 251;
6304 int level_author_pos = 292;
6305 int envelope_header_len;
6306 int envelope_content_len;
6308 int level_author_len;
6310 int num_yamyam_contents;
6313 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6315 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6317 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6319 header[i * 2 + 0] = header_word >> 8;
6320 header[i * 2 + 1] = header_word & 0xff;
6323 // read some values from level header to check level decoding integrity
6324 fieldx = header[6] | (header[7] << 8);
6325 fieldy = header[8] | (header[9] << 8);
6326 num_yamyam_contents = header[60] | (header[61] << 8);
6328 // do some simple sanity checks to ensure that level was correctly decoded
6329 if (fieldx < 1 || fieldx > 256 ||
6330 fieldy < 1 || fieldy > 256 ||
6331 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6333 level->no_valid_file = TRUE;
6335 Warn("cannot decode level from stream -- using empty level");
6340 // maximum envelope header size is 31 bytes
6341 envelope_header_len = header[envelope_header_pos];
6342 // maximum envelope content size is 110 (156?) bytes
6343 envelope_content_len = header[envelope_content_pos];
6345 // maximum level title size is 40 bytes
6346 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6347 // maximum level author size is 30 (51?) bytes
6348 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6352 for (i = 0; i < envelope_header_len; i++)
6353 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6354 level->envelope[0].text[envelope_size++] =
6355 header[envelope_header_pos + 1 + i];
6357 if (envelope_header_len > 0 && envelope_content_len > 0)
6359 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6360 level->envelope[0].text[envelope_size++] = '\n';
6361 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6362 level->envelope[0].text[envelope_size++] = '\n';
6365 for (i = 0; i < envelope_content_len; i++)
6366 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6367 level->envelope[0].text[envelope_size++] =
6368 header[envelope_content_pos + 1 + i];
6370 level->envelope[0].text[envelope_size] = '\0';
6372 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6373 level->envelope[0].ysize = 10;
6374 level->envelope[0].autowrap = TRUE;
6375 level->envelope[0].centered = TRUE;
6377 for (i = 0; i < level_name_len; i++)
6378 level->name[i] = header[level_name_pos + 1 + i];
6379 level->name[level_name_len] = '\0';
6381 for (i = 0; i < level_author_len; i++)
6382 level->author[i] = header[level_author_pos + 1 + i];
6383 level->author[level_author_len] = '\0';
6385 num_yamyam_contents = header[60] | (header[61] << 8);
6386 level->num_yamyam_contents =
6387 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6389 for (i = 0; i < num_yamyam_contents; i++)
6391 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6393 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6394 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6396 if (i < MAX_ELEMENT_CONTENTS)
6397 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6401 fieldx = header[6] | (header[7] << 8);
6402 fieldy = header[8] | (header[9] << 8);
6403 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6404 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6406 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6408 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6409 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6411 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6412 level->field[x][y] = getMappedElement_DC(element_dc);
6415 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6416 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6417 level->field[x][y] = EL_PLAYER_1;
6419 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6420 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6421 level->field[x][y] = EL_PLAYER_2;
6423 level->gems_needed = header[18] | (header[19] << 8);
6425 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6426 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6427 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6428 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6429 level->score[SC_NUT] = header[28] | (header[29] << 8);
6430 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6431 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6432 level->score[SC_BUG] = header[34] | (header[35] << 8);
6433 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6434 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6435 level->score[SC_KEY] = header[40] | (header[41] << 8);
6436 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6438 level->time = header[44] | (header[45] << 8);
6440 level->amoeba_speed = header[46] | (header[47] << 8);
6441 level->time_light = header[48] | (header[49] << 8);
6442 level->time_timegate = header[50] | (header[51] << 8);
6443 level->time_wheel = header[52] | (header[53] << 8);
6444 level->time_magic_wall = header[54] | (header[55] << 8);
6445 level->extra_time = header[56] | (header[57] << 8);
6446 level->shield_normal_time = header[58] | (header[59] << 8);
6448 // shield and extra time elements do not have a score
6449 level->score[SC_SHIELD] = 0;
6450 level->extra_time_score = 0;
6452 // set time for normal and deadly shields to the same value
6453 level->shield_deadly_time = level->shield_normal_time;
6455 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6456 // can slip down from flat walls, like normal walls and steel walls
6457 level->em_slippery_gems = TRUE;
6459 // time score is counted for each 10 seconds left in Diamond Caves levels
6460 level->time_score_base = 10;
6463 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6464 struct LevelFileInfo *level_file_info,
6465 boolean level_info_only)
6467 char *filename = level_file_info->filename;
6469 int num_magic_bytes = 8;
6470 char magic_bytes[num_magic_bytes + 1];
6471 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6473 if (!(file = openFile(filename, MODE_READ)))
6475 level->no_valid_file = TRUE;
6477 if (!level_info_only)
6478 Warn("cannot read level '%s' -- using empty level", filename);
6483 // fseek(file, 0x0000, SEEK_SET);
6485 if (level_file_info->packed)
6487 // read "magic bytes" from start of file
6488 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6489 magic_bytes[0] = '\0';
6491 // check "magic bytes" for correct file format
6492 if (!strPrefix(magic_bytes, "DC2"))
6494 level->no_valid_file = TRUE;
6496 Warn("unknown DC level file '%s' -- using empty level", filename);
6501 if (strPrefix(magic_bytes, "DC2Win95") ||
6502 strPrefix(magic_bytes, "DC2Win98"))
6504 int position_first_level = 0x00fa;
6505 int extra_bytes = 4;
6508 // advance file stream to first level inside the level package
6509 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6511 // each block of level data is followed by block of non-level data
6512 num_levels_to_skip *= 2;
6514 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6515 while (num_levels_to_skip >= 0)
6517 // advance file stream to next level inside the level package
6518 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6520 level->no_valid_file = TRUE;
6522 Warn("cannot fseek in file '%s' -- using empty level", filename);
6527 // skip apparently unused extra bytes following each level
6528 ReadUnusedBytesFromFile(file, extra_bytes);
6530 // read size of next level in level package
6531 skip_bytes = getFile32BitLE(file);
6533 num_levels_to_skip--;
6538 level->no_valid_file = TRUE;
6540 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6546 LoadLevelFromFileStream_DC(file, level);
6552 // ----------------------------------------------------------------------------
6553 // functions for loading SB level
6554 // ----------------------------------------------------------------------------
6556 int getMappedElement_SB(int element_ascii, boolean use_ces)
6564 sb_element_mapping[] =
6566 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6567 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6568 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6569 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6570 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6571 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6572 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6573 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6580 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6581 if (element_ascii == sb_element_mapping[i].ascii)
6582 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6584 return EL_UNDEFINED;
6587 static void SetLevelSettings_SB(struct LevelInfo *level)
6591 level->use_step_counter = TRUE;
6594 level->score[SC_TIME_BONUS] = 0;
6595 level->time_score_base = 1;
6596 level->rate_time_over_score = TRUE;
6599 level->auto_exit_sokoban = TRUE;
6602 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6603 struct LevelFileInfo *level_file_info,
6604 boolean level_info_only)
6606 char *filename = level_file_info->filename;
6607 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6608 char last_comment[MAX_LINE_LEN];
6609 char level_name[MAX_LINE_LEN];
6612 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6613 boolean read_continued_line = FALSE;
6614 boolean reading_playfield = FALSE;
6615 boolean got_valid_playfield_line = FALSE;
6616 boolean invalid_playfield_char = FALSE;
6617 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6618 int file_level_nr = 0;
6619 int x = 0, y = 0; // initialized to make compilers happy
6621 last_comment[0] = '\0';
6622 level_name[0] = '\0';
6624 if (!(file = openFile(filename, MODE_READ)))
6626 level->no_valid_file = TRUE;
6628 if (!level_info_only)
6629 Warn("cannot read level '%s' -- using empty level", filename);
6634 while (!checkEndOfFile(file))
6636 // level successfully read, but next level may follow here
6637 if (!got_valid_playfield_line && reading_playfield)
6639 // read playfield from single level file -- skip remaining file
6640 if (!level_file_info->packed)
6643 if (file_level_nr >= num_levels_to_skip)
6648 last_comment[0] = '\0';
6649 level_name[0] = '\0';
6651 reading_playfield = FALSE;
6654 got_valid_playfield_line = FALSE;
6656 // read next line of input file
6657 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6660 // cut trailing line break (this can be newline and/or carriage return)
6661 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6662 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6665 // copy raw input line for later use (mainly debugging output)
6666 strcpy(line_raw, line);
6668 if (read_continued_line)
6670 // append new line to existing line, if there is enough space
6671 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6672 strcat(previous_line, line_ptr);
6674 strcpy(line, previous_line); // copy storage buffer to line
6676 read_continued_line = FALSE;
6679 // if the last character is '\', continue at next line
6680 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6682 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6683 strcpy(previous_line, line); // copy line to storage buffer
6685 read_continued_line = TRUE;
6691 if (line[0] == '\0')
6694 // extract comment text from comment line
6697 for (line_ptr = line; *line_ptr; line_ptr++)
6698 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6701 strcpy(last_comment, line_ptr);
6706 // extract level title text from line containing level title
6707 if (line[0] == '\'')
6709 strcpy(level_name, &line[1]);
6711 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6712 level_name[strlen(level_name) - 1] = '\0';
6717 // skip lines containing only spaces (or empty lines)
6718 for (line_ptr = line; *line_ptr; line_ptr++)
6719 if (*line_ptr != ' ')
6721 if (*line_ptr == '\0')
6724 // at this point, we have found a line containing part of a playfield
6726 got_valid_playfield_line = TRUE;
6728 if (!reading_playfield)
6730 reading_playfield = TRUE;
6731 invalid_playfield_char = FALSE;
6733 for (x = 0; x < MAX_LEV_FIELDX; x++)
6734 for (y = 0; y < MAX_LEV_FIELDY; y++)
6735 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6740 // start with topmost tile row
6744 // skip playfield line if larger row than allowed
6745 if (y >= MAX_LEV_FIELDY)
6748 // start with leftmost tile column
6751 // read playfield elements from line
6752 for (line_ptr = line; *line_ptr; line_ptr++)
6754 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6756 // stop parsing playfield line if larger column than allowed
6757 if (x >= MAX_LEV_FIELDX)
6760 if (mapped_sb_element == EL_UNDEFINED)
6762 invalid_playfield_char = TRUE;
6767 level->field[x][y] = mapped_sb_element;
6769 // continue with next tile column
6772 level->fieldx = MAX(x, level->fieldx);
6775 if (invalid_playfield_char)
6777 // if first playfield line, treat invalid lines as comment lines
6779 reading_playfield = FALSE;
6784 // continue with next tile row
6792 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6793 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6795 if (!reading_playfield)
6797 level->no_valid_file = TRUE;
6799 Warn("cannot read level '%s' -- using empty level", filename);
6804 if (*level_name != '\0')
6806 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6807 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6809 else if (*last_comment != '\0')
6811 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6812 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6816 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6819 // set all empty fields beyond the border walls to invisible steel wall
6820 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6822 if ((x == 0 || x == level->fieldx - 1 ||
6823 y == 0 || y == level->fieldy - 1) &&
6824 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6825 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6826 level->field, level->fieldx, level->fieldy);
6829 // set special level settings for Sokoban levels
6830 SetLevelSettings_SB(level);
6832 if (load_xsb_to_ces)
6834 // special global settings can now be set in level template
6835 level->use_custom_template = TRUE;
6840 // -------------------------------------------------------------------------
6841 // functions for handling native levels
6842 // -------------------------------------------------------------------------
6844 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6845 struct LevelFileInfo *level_file_info,
6846 boolean level_info_only)
6850 // determine position of requested level inside level package
6851 if (level_file_info->packed)
6852 pos = level_file_info->nr - leveldir_current->first_level;
6854 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6855 level->no_valid_file = TRUE;
6858 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6859 struct LevelFileInfo *level_file_info,
6860 boolean level_info_only)
6862 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6863 level->no_valid_file = TRUE;
6866 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6867 struct LevelFileInfo *level_file_info,
6868 boolean level_info_only)
6872 // determine position of requested level inside level package
6873 if (level_file_info->packed)
6874 pos = level_file_info->nr - leveldir_current->first_level;
6876 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6877 level->no_valid_file = TRUE;
6880 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6881 struct LevelFileInfo *level_file_info,
6882 boolean level_info_only)
6884 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6885 level->no_valid_file = TRUE;
6888 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6890 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6891 CopyNativeLevel_RND_to_BD(level);
6892 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6893 CopyNativeLevel_RND_to_EM(level);
6894 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6895 CopyNativeLevel_RND_to_SP(level);
6896 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6897 CopyNativeLevel_RND_to_MM(level);
6900 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6902 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6903 CopyNativeLevel_BD_to_RND(level);
6904 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6905 CopyNativeLevel_EM_to_RND(level);
6906 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6907 CopyNativeLevel_SP_to_RND(level);
6908 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6909 CopyNativeLevel_MM_to_RND(level);
6912 void SaveNativeLevel(struct LevelInfo *level)
6914 // saving native level files only supported for some game engines
6915 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6916 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6919 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6920 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6921 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6922 char *filename = getLevelFilenameFromBasename(basename);
6924 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6927 boolean success = FALSE;
6929 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6931 CopyNativeLevel_RND_to_BD(level);
6932 // CopyNativeTape_RND_to_BD(level);
6934 success = SaveNativeLevel_BD(filename);
6936 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6938 CopyNativeLevel_RND_to_SP(level);
6939 CopyNativeTape_RND_to_SP(level);
6941 success = SaveNativeLevel_SP(filename);
6945 Request("Native level file saved!", REQ_CONFIRM);
6947 Request("Failed to save native level file!", REQ_CONFIRM);
6951 // ----------------------------------------------------------------------------
6952 // functions for loading generic level
6953 // ----------------------------------------------------------------------------
6955 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6956 struct LevelFileInfo *level_file_info,
6957 boolean level_info_only)
6959 // always start with reliable default values
6960 setLevelInfoToDefaults(level, level_info_only, TRUE);
6962 switch (level_file_info->type)
6964 case LEVEL_FILE_TYPE_RND:
6965 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6968 case LEVEL_FILE_TYPE_BD:
6969 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6970 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6973 case LEVEL_FILE_TYPE_EM:
6974 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6975 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6978 case LEVEL_FILE_TYPE_SP:
6979 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6980 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6983 case LEVEL_FILE_TYPE_MM:
6984 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6985 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6988 case LEVEL_FILE_TYPE_DC:
6989 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6992 case LEVEL_FILE_TYPE_SB:
6993 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6997 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7001 // if level file is invalid, restore level structure to default values
7002 if (level->no_valid_file)
7003 setLevelInfoToDefaults(level, level_info_only, FALSE);
7005 if (check_special_flags("use_native_bd_game_engine"))
7006 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7008 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7009 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7011 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7012 CopyNativeLevel_Native_to_RND(level);
7015 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7017 static struct LevelFileInfo level_file_info;
7019 // always start with reliable default values
7020 setFileInfoToDefaults(&level_file_info);
7022 level_file_info.nr = 0; // unknown level number
7023 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7025 setString(&level_file_info.filename, filename);
7027 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7030 static void LoadLevel_InitVersion(struct LevelInfo *level)
7034 if (leveldir_current == NULL) // only when dumping level
7037 // all engine modifications also valid for levels which use latest engine
7038 if (level->game_version < VERSION_IDENT(3,2,0,5))
7040 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7041 level->time_score_base = 10;
7044 if (leveldir_current->latest_engine)
7046 // ---------- use latest game engine --------------------------------------
7048 /* For all levels which are forced to use the latest game engine version
7049 (normally all but user contributed, private and undefined levels), set
7050 the game engine version to the actual version; this allows for actual
7051 corrections in the game engine to take effect for existing, converted
7052 levels (from "classic" or other existing games) to make the emulation
7053 of the corresponding game more accurate, while (hopefully) not breaking
7054 existing levels created from other players. */
7056 level->game_version = GAME_VERSION_ACTUAL;
7058 /* Set special EM style gems behaviour: EM style gems slip down from
7059 normal, steel and growing wall. As this is a more fundamental change,
7060 it seems better to set the default behaviour to "off" (as it is more
7061 natural) and make it configurable in the level editor (as a property
7062 of gem style elements). Already existing converted levels (neither
7063 private nor contributed levels) are changed to the new behaviour. */
7065 if (level->file_version < FILE_VERSION_2_0)
7066 level->em_slippery_gems = TRUE;
7071 // ---------- use game engine the level was created with --------------------
7073 /* For all levels which are not forced to use the latest game engine
7074 version (normally user contributed, private and undefined levels),
7075 use the version of the game engine the levels were created for.
7077 Since 2.0.1, the game engine version is now directly stored
7078 in the level file (chunk "VERS"), so there is no need anymore
7079 to set the game version from the file version (except for old,
7080 pre-2.0 levels, where the game version is still taken from the
7081 file format version used to store the level -- see above). */
7083 // player was faster than enemies in 1.0.0 and before
7084 if (level->file_version == FILE_VERSION_1_0)
7085 for (i = 0; i < MAX_PLAYERS; i++)
7086 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7088 // default behaviour for EM style gems was "slippery" only in 2.0.1
7089 if (level->game_version == VERSION_IDENT(2,0,1,0))
7090 level->em_slippery_gems = TRUE;
7092 // springs could be pushed over pits before (pre-release version) 2.2.0
7093 if (level->game_version < VERSION_IDENT(2,2,0,0))
7094 level->use_spring_bug = TRUE;
7096 if (level->game_version < VERSION_IDENT(3,2,0,5))
7098 // time orb caused limited time in endless time levels before 3.2.0-5
7099 level->use_time_orb_bug = TRUE;
7101 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7102 level->block_snap_field = FALSE;
7104 // extra time score was same value as time left score before 3.2.0-5
7105 level->extra_time_score = level->score[SC_TIME_BONUS];
7108 if (level->game_version < VERSION_IDENT(3,2,0,7))
7110 // default behaviour for snapping was "not continuous" before 3.2.0-7
7111 level->continuous_snapping = FALSE;
7114 // only few elements were able to actively move into acid before 3.1.0
7115 // trigger settings did not exist before 3.1.0; set to default "any"
7116 if (level->game_version < VERSION_IDENT(3,1,0,0))
7118 // correct "can move into acid" settings (all zero in old levels)
7120 level->can_move_into_acid_bits = 0; // nothing can move into acid
7121 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7123 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7124 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7125 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7126 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7128 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7129 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7131 // correct trigger settings (stored as zero == "none" in old levels)
7133 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7135 int element = EL_CUSTOM_START + i;
7136 struct ElementInfo *ei = &element_info[element];
7138 for (j = 0; j < ei->num_change_pages; j++)
7140 struct ElementChangeInfo *change = &ei->change_page[j];
7142 change->trigger_player = CH_PLAYER_ANY;
7143 change->trigger_page = CH_PAGE_ANY;
7148 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7150 int element = EL_CUSTOM_256;
7151 struct ElementInfo *ei = &element_info[element];
7152 struct ElementChangeInfo *change = &ei->change_page[0];
7154 /* This is needed to fix a problem that was caused by a bugfix in function
7155 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7156 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7157 not replace walkable elements, but instead just placed the player on it,
7158 without placing the Sokoban field under the player). Unfortunately, this
7159 breaks "Snake Bite" style levels when the snake is halfway through a door
7160 that just closes (the snake head is still alive and can be moved in this
7161 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7162 player (without Sokoban element) which then gets killed as designed). */
7164 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7165 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7166 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7167 change->target_element = EL_PLAYER_1;
7170 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7171 if (level->game_version < VERSION_IDENT(3,2,5,0))
7173 /* This is needed to fix a problem that was caused by a bugfix in function
7174 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7175 corrects the behaviour when a custom element changes to another custom
7176 element with a higher element number that has change actions defined.
7177 Normally, only one change per frame is allowed for custom elements.
7178 Therefore, it is checked if a custom element already changed in the
7179 current frame; if it did, subsequent changes are suppressed.
7180 Unfortunately, this is only checked for element changes, but not for
7181 change actions, which are still executed. As the function above loops
7182 through all custom elements from lower to higher, an element change
7183 resulting in a lower CE number won't be checked again, while a target
7184 element with a higher number will also be checked, and potential change
7185 actions will get executed for this CE, too (which is wrong), while
7186 further changes are ignored (which is correct). As this bugfix breaks
7187 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7188 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7189 behaviour for existing levels and tapes that make use of this bug */
7191 level->use_action_after_change_bug = TRUE;
7194 // not centering level after relocating player was default only in 3.2.3
7195 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7196 level->shifted_relocation = TRUE;
7198 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7199 if (level->game_version < VERSION_IDENT(3,2,6,0))
7200 level->em_explodes_by_fire = TRUE;
7202 // levels were solved by the first player entering an exit up to 4.1.0.0
7203 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7204 level->solved_by_one_player = TRUE;
7206 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7207 if (level->game_version < VERSION_IDENT(4,1,1,1))
7208 level->use_life_bugs = TRUE;
7210 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7211 if (level->game_version < VERSION_IDENT(4,1,1,1))
7212 level->sb_objects_needed = FALSE;
7214 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7215 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7216 level->finish_dig_collect = FALSE;
7218 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7219 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7220 level->keep_walkable_ce = TRUE;
7223 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7225 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7228 // check if this level is (not) a Sokoban level
7229 for (y = 0; y < level->fieldy; y++)
7230 for (x = 0; x < level->fieldx; x++)
7231 if (!IS_SB_ELEMENT(Tile[x][y]))
7232 is_sokoban_level = FALSE;
7234 if (is_sokoban_level)
7236 // set special level settings for Sokoban levels
7237 SetLevelSettings_SB(level);
7241 static void LoadLevel_InitSettings(struct LevelInfo *level)
7243 // adjust level settings for (non-native) Sokoban-style levels
7244 LoadLevel_InitSettings_SB(level);
7246 // rename levels with title "nameless level" or if renaming is forced
7247 if (leveldir_current->empty_level_name != NULL &&
7248 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7249 leveldir_current->force_level_name))
7250 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7251 leveldir_current->empty_level_name, level_nr);
7254 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7258 // map elements that have changed in newer versions
7259 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7260 level->game_version);
7261 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7262 for (x = 0; x < 3; x++)
7263 for (y = 0; y < 3; y++)
7264 level->yamyam_content[i].e[x][y] =
7265 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7266 level->game_version);
7270 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7274 // map custom element change events that have changed in newer versions
7275 // (these following values were accidentally changed in version 3.0.1)
7276 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7277 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7279 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7281 int element = EL_CUSTOM_START + i;
7283 // order of checking and copying events to be mapped is important
7284 // (do not change the start and end value -- they are constant)
7285 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7287 if (HAS_CHANGE_EVENT(element, j - 2))
7289 SET_CHANGE_EVENT(element, j - 2, FALSE);
7290 SET_CHANGE_EVENT(element, j, TRUE);
7294 // order of checking and copying events to be mapped is important
7295 // (do not change the start and end value -- they are constant)
7296 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7298 if (HAS_CHANGE_EVENT(element, j - 1))
7300 SET_CHANGE_EVENT(element, j - 1, FALSE);
7301 SET_CHANGE_EVENT(element, j, TRUE);
7307 // initialize "can_change" field for old levels with only one change page
7308 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7310 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7312 int element = EL_CUSTOM_START + i;
7314 if (CAN_CHANGE(element))
7315 element_info[element].change->can_change = TRUE;
7319 // correct custom element values (for old levels without these options)
7320 if (level->game_version < VERSION_IDENT(3,1,1,0))
7322 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7324 int element = EL_CUSTOM_START + i;
7325 struct ElementInfo *ei = &element_info[element];
7327 if (ei->access_direction == MV_NO_DIRECTION)
7328 ei->access_direction = MV_ALL_DIRECTIONS;
7332 // correct custom element values (fix invalid values for all versions)
7335 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7337 int element = EL_CUSTOM_START + i;
7338 struct ElementInfo *ei = &element_info[element];
7340 for (j = 0; j < ei->num_change_pages; j++)
7342 struct ElementChangeInfo *change = &ei->change_page[j];
7344 if (change->trigger_player == CH_PLAYER_NONE)
7345 change->trigger_player = CH_PLAYER_ANY;
7347 if (change->trigger_side == CH_SIDE_NONE)
7348 change->trigger_side = CH_SIDE_ANY;
7353 // initialize "can_explode" field for old levels which did not store this
7354 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7355 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7357 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7359 int element = EL_CUSTOM_START + i;
7361 if (EXPLODES_1X1_OLD(element))
7362 element_info[element].explosion_type = EXPLODES_1X1;
7364 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7365 EXPLODES_SMASHED(element) ||
7366 EXPLODES_IMPACT(element)));
7370 // correct previously hard-coded move delay values for maze runner style
7371 if (level->game_version < VERSION_IDENT(3,1,1,0))
7373 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7375 int element = EL_CUSTOM_START + i;
7377 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7379 // previously hard-coded and therefore ignored
7380 element_info[element].move_delay_fixed = 9;
7381 element_info[element].move_delay_random = 0;
7386 // set some other uninitialized values of custom elements in older levels
7387 if (level->game_version < VERSION_IDENT(3,1,0,0))
7389 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7391 int element = EL_CUSTOM_START + i;
7393 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7395 element_info[element].explosion_delay = 17;
7396 element_info[element].ignition_delay = 8;
7400 // set mouse click change events to work for left/middle/right mouse button
7401 if (level->game_version < VERSION_IDENT(4,2,3,0))
7403 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7405 int element = EL_CUSTOM_START + i;
7406 struct ElementInfo *ei = &element_info[element];
7408 for (j = 0; j < ei->num_change_pages; j++)
7410 struct ElementChangeInfo *change = &ei->change_page[j];
7412 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7413 change->has_event[CE_PRESSED_BY_MOUSE] ||
7414 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7415 change->has_event[CE_MOUSE_PRESSED_ON_X])
7416 change->trigger_side = CH_SIDE_ANY;
7422 static void LoadLevel_InitElements(struct LevelInfo *level)
7424 LoadLevel_InitStandardElements(level);
7426 if (level->file_has_custom_elements)
7427 LoadLevel_InitCustomElements(level);
7429 // initialize element properties for level editor etc.
7430 InitElementPropertiesEngine(level->game_version);
7431 InitElementPropertiesGfxElement();
7434 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7438 // map elements that have changed in newer versions
7439 for (y = 0; y < level->fieldy; y++)
7440 for (x = 0; x < level->fieldx; x++)
7441 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7442 level->game_version);
7444 // clear unused playfield data (nicer if level gets resized in editor)
7445 for (x = 0; x < MAX_LEV_FIELDX; x++)
7446 for (y = 0; y < MAX_LEV_FIELDY; y++)
7447 if (x >= level->fieldx || y >= level->fieldy)
7448 level->field[x][y] = EL_EMPTY;
7450 // copy elements to runtime playfield array
7451 for (x = 0; x < MAX_LEV_FIELDX; x++)
7452 for (y = 0; y < MAX_LEV_FIELDY; y++)
7453 Tile[x][y] = level->field[x][y];
7455 // initialize level size variables for faster access
7456 lev_fieldx = level->fieldx;
7457 lev_fieldy = level->fieldy;
7459 // determine border element for this level
7460 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7461 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7466 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7468 struct LevelFileInfo *level_file_info = &level->file_info;
7470 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7471 CopyNativeLevel_RND_to_Native(level);
7474 static void LoadLevelTemplate_LoadAndInit(void)
7476 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7478 LoadLevel_InitVersion(&level_template);
7479 LoadLevel_InitElements(&level_template);
7480 LoadLevel_InitSettings(&level_template);
7482 ActivateLevelTemplate();
7485 void LoadLevelTemplate(int nr)
7487 if (!fileExists(getGlobalLevelTemplateFilename()))
7489 Warn("no level template found for this level");
7494 setLevelFileInfo(&level_template.file_info, nr);
7496 LoadLevelTemplate_LoadAndInit();
7499 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7501 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7503 LoadLevelTemplate_LoadAndInit();
7506 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7508 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7510 if (level.use_custom_template)
7512 if (network_level != NULL)
7513 LoadNetworkLevelTemplate(network_level);
7515 LoadLevelTemplate(-1);
7518 LoadLevel_InitVersion(&level);
7519 LoadLevel_InitElements(&level);
7520 LoadLevel_InitPlayfield(&level);
7521 LoadLevel_InitSettings(&level);
7523 LoadLevel_InitNativeEngines(&level);
7526 void LoadLevel(int nr)
7528 SetLevelSetInfo(leveldir_current->identifier, nr);
7530 setLevelFileInfo(&level.file_info, nr);
7532 LoadLevel_LoadAndInit(NULL);
7535 void LoadLevelInfoOnly(int nr)
7537 setLevelFileInfo(&level.file_info, nr);
7539 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7542 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7544 SetLevelSetInfo(network_level->leveldir_identifier,
7545 network_level->file_info.nr);
7547 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7549 LoadLevel_LoadAndInit(network_level);
7552 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7556 chunk_size += putFileVersion(file, level->file_version);
7557 chunk_size += putFileVersion(file, level->game_version);
7562 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7566 chunk_size += putFile16BitBE(file, level->creation_date.year);
7567 chunk_size += putFile8Bit(file, level->creation_date.month);
7568 chunk_size += putFile8Bit(file, level->creation_date.day);
7573 #if ENABLE_HISTORIC_CHUNKS
7574 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7578 putFile8Bit(file, level->fieldx);
7579 putFile8Bit(file, level->fieldy);
7581 putFile16BitBE(file, level->time);
7582 putFile16BitBE(file, level->gems_needed);
7584 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7585 putFile8Bit(file, level->name[i]);
7587 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7588 putFile8Bit(file, level->score[i]);
7590 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7591 for (y = 0; y < 3; y++)
7592 for (x = 0; x < 3; x++)
7593 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7594 level->yamyam_content[i].e[x][y]));
7595 putFile8Bit(file, level->amoeba_speed);
7596 putFile8Bit(file, level->time_magic_wall);
7597 putFile8Bit(file, level->time_wheel);
7598 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7599 level->amoeba_content));
7600 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7601 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7602 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7603 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7605 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7607 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7608 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7609 putFile32BitBE(file, level->can_move_into_acid_bits);
7610 putFile8Bit(file, level->dont_collide_with_bits);
7612 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7613 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7615 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7616 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7617 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7619 putFile8Bit(file, level->game_engine_type);
7621 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7625 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7630 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7631 chunk_size += putFile8Bit(file, level->name[i]);
7636 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7641 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7642 chunk_size += putFile8Bit(file, level->author[i]);
7647 #if ENABLE_HISTORIC_CHUNKS
7648 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7653 for (y = 0; y < level->fieldy; y++)
7654 for (x = 0; x < level->fieldx; x++)
7655 if (level->encoding_16bit_field)
7656 chunk_size += putFile16BitBE(file, level->field[x][y]);
7658 chunk_size += putFile8Bit(file, level->field[x][y]);
7664 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7669 for (y = 0; y < level->fieldy; y++)
7670 for (x = 0; x < level->fieldx; x++)
7671 chunk_size += putFile16BitBE(file, level->field[x][y]);
7676 #if ENABLE_HISTORIC_CHUNKS
7677 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7681 putFile8Bit(file, EL_YAMYAM);
7682 putFile8Bit(file, level->num_yamyam_contents);
7683 putFile8Bit(file, 0);
7684 putFile8Bit(file, 0);
7686 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7687 for (y = 0; y < 3; y++)
7688 for (x = 0; x < 3; x++)
7689 if (level->encoding_16bit_field)
7690 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7692 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7696 #if ENABLE_HISTORIC_CHUNKS
7697 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7700 int num_contents, content_xsize, content_ysize;
7701 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7703 if (element == EL_YAMYAM)
7705 num_contents = level->num_yamyam_contents;
7709 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7710 for (y = 0; y < 3; y++)
7711 for (x = 0; x < 3; x++)
7712 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7714 else if (element == EL_BD_AMOEBA)
7720 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7721 for (y = 0; y < 3; y++)
7722 for (x = 0; x < 3; x++)
7723 content_array[i][x][y] = EL_EMPTY;
7724 content_array[0][0][0] = level->amoeba_content;
7728 // chunk header already written -- write empty chunk data
7729 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7731 Warn("cannot save content for element '%d'", element);
7736 putFile16BitBE(file, element);
7737 putFile8Bit(file, num_contents);
7738 putFile8Bit(file, content_xsize);
7739 putFile8Bit(file, content_ysize);
7741 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7743 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7744 for (y = 0; y < 3; y++)
7745 for (x = 0; x < 3; x++)
7746 putFile16BitBE(file, content_array[i][x][y]);
7750 #if ENABLE_HISTORIC_CHUNKS
7751 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7753 int envelope_nr = element - EL_ENVELOPE_1;
7754 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7758 chunk_size += putFile16BitBE(file, element);
7759 chunk_size += putFile16BitBE(file, envelope_len);
7760 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7761 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7763 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7764 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7766 for (i = 0; i < envelope_len; i++)
7767 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7773 #if ENABLE_HISTORIC_CHUNKS
7774 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7775 int num_changed_custom_elements)
7779 putFile16BitBE(file, num_changed_custom_elements);
7781 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7783 int element = EL_CUSTOM_START + i;
7785 struct ElementInfo *ei = &element_info[element];
7787 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7789 if (check < num_changed_custom_elements)
7791 putFile16BitBE(file, element);
7792 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7799 if (check != num_changed_custom_elements) // should not happen
7800 Warn("inconsistent number of custom element properties");
7804 #if ENABLE_HISTORIC_CHUNKS
7805 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7806 int num_changed_custom_elements)
7810 putFile16BitBE(file, num_changed_custom_elements);
7812 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7814 int element = EL_CUSTOM_START + i;
7816 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7818 if (check < num_changed_custom_elements)
7820 putFile16BitBE(file, element);
7821 putFile16BitBE(file, element_info[element].change->target_element);
7828 if (check != num_changed_custom_elements) // should not happen
7829 Warn("inconsistent number of custom target elements");
7833 #if ENABLE_HISTORIC_CHUNKS
7834 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7835 int num_changed_custom_elements)
7837 int i, j, x, y, check = 0;
7839 putFile16BitBE(file, num_changed_custom_elements);
7841 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7843 int element = EL_CUSTOM_START + i;
7844 struct ElementInfo *ei = &element_info[element];
7846 if (ei->modified_settings)
7848 if (check < num_changed_custom_elements)
7850 putFile16BitBE(file, element);
7852 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7853 putFile8Bit(file, ei->description[j]);
7855 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7857 // some free bytes for future properties and padding
7858 WriteUnusedBytesToFile(file, 7);
7860 putFile8Bit(file, ei->use_gfx_element);
7861 putFile16BitBE(file, ei->gfx_element_initial);
7863 putFile8Bit(file, ei->collect_score_initial);
7864 putFile8Bit(file, ei->collect_count_initial);
7866 putFile16BitBE(file, ei->push_delay_fixed);
7867 putFile16BitBE(file, ei->push_delay_random);
7868 putFile16BitBE(file, ei->move_delay_fixed);
7869 putFile16BitBE(file, ei->move_delay_random);
7871 putFile16BitBE(file, ei->move_pattern);
7872 putFile8Bit(file, ei->move_direction_initial);
7873 putFile8Bit(file, ei->move_stepsize);
7875 for (y = 0; y < 3; y++)
7876 for (x = 0; x < 3; x++)
7877 putFile16BitBE(file, ei->content.e[x][y]);
7879 putFile32BitBE(file, ei->change->events);
7881 putFile16BitBE(file, ei->change->target_element);
7883 putFile16BitBE(file, ei->change->delay_fixed);
7884 putFile16BitBE(file, ei->change->delay_random);
7885 putFile16BitBE(file, ei->change->delay_frames);
7887 putFile16BitBE(file, ei->change->initial_trigger_element);
7889 putFile8Bit(file, ei->change->explode);
7890 putFile8Bit(file, ei->change->use_target_content);
7891 putFile8Bit(file, ei->change->only_if_complete);
7892 putFile8Bit(file, ei->change->use_random_replace);
7894 putFile8Bit(file, ei->change->random_percentage);
7895 putFile8Bit(file, ei->change->replace_when);
7897 for (y = 0; y < 3; y++)
7898 for (x = 0; x < 3; x++)
7899 putFile16BitBE(file, ei->change->content.e[x][y]);
7901 putFile8Bit(file, ei->slippery_type);
7903 // some free bytes for future properties and padding
7904 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7911 if (check != num_changed_custom_elements) // should not happen
7912 Warn("inconsistent number of custom element properties");
7916 #if ENABLE_HISTORIC_CHUNKS
7917 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7919 struct ElementInfo *ei = &element_info[element];
7922 // ---------- custom element base property values (96 bytes) ----------------
7924 putFile16BitBE(file, element);
7926 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7927 putFile8Bit(file, ei->description[i]);
7929 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7931 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7933 putFile8Bit(file, ei->num_change_pages);
7935 putFile16BitBE(file, ei->ce_value_fixed_initial);
7936 putFile16BitBE(file, ei->ce_value_random_initial);
7937 putFile8Bit(file, ei->use_last_ce_value);
7939 putFile8Bit(file, ei->use_gfx_element);
7940 putFile16BitBE(file, ei->gfx_element_initial);
7942 putFile8Bit(file, ei->collect_score_initial);
7943 putFile8Bit(file, ei->collect_count_initial);
7945 putFile8Bit(file, ei->drop_delay_fixed);
7946 putFile8Bit(file, ei->push_delay_fixed);
7947 putFile8Bit(file, ei->drop_delay_random);
7948 putFile8Bit(file, ei->push_delay_random);
7949 putFile16BitBE(file, ei->move_delay_fixed);
7950 putFile16BitBE(file, ei->move_delay_random);
7952 // bits 0 - 15 of "move_pattern" ...
7953 putFile16BitBE(file, ei->move_pattern & 0xffff);
7954 putFile8Bit(file, ei->move_direction_initial);
7955 putFile8Bit(file, ei->move_stepsize);
7957 putFile8Bit(file, ei->slippery_type);
7959 for (y = 0; y < 3; y++)
7960 for (x = 0; x < 3; x++)
7961 putFile16BitBE(file, ei->content.e[x][y]);
7963 putFile16BitBE(file, ei->move_enter_element);
7964 putFile16BitBE(file, ei->move_leave_element);
7965 putFile8Bit(file, ei->move_leave_type);
7967 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7968 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7970 putFile8Bit(file, ei->access_direction);
7972 putFile8Bit(file, ei->explosion_delay);
7973 putFile8Bit(file, ei->ignition_delay);
7974 putFile8Bit(file, ei->explosion_type);
7976 // some free bytes for future custom property values and padding
7977 WriteUnusedBytesToFile(file, 1);
7979 // ---------- change page property values (48 bytes) ------------------------
7981 for (i = 0; i < ei->num_change_pages; i++)
7983 struct ElementChangeInfo *change = &ei->change_page[i];
7984 unsigned int event_bits;
7986 // bits 0 - 31 of "has_event[]" ...
7988 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7989 if (change->has_event[j])
7990 event_bits |= (1u << j);
7991 putFile32BitBE(file, event_bits);
7993 putFile16BitBE(file, change->target_element);
7995 putFile16BitBE(file, change->delay_fixed);
7996 putFile16BitBE(file, change->delay_random);
7997 putFile16BitBE(file, change->delay_frames);
7999 putFile16BitBE(file, change->initial_trigger_element);
8001 putFile8Bit(file, change->explode);
8002 putFile8Bit(file, change->use_target_content);
8003 putFile8Bit(file, change->only_if_complete);
8004 putFile8Bit(file, change->use_random_replace);
8006 putFile8Bit(file, change->random_percentage);
8007 putFile8Bit(file, change->replace_when);
8009 for (y = 0; y < 3; y++)
8010 for (x = 0; x < 3; x++)
8011 putFile16BitBE(file, change->target_content.e[x][y]);
8013 putFile8Bit(file, change->can_change);
8015 putFile8Bit(file, change->trigger_side);
8017 putFile8Bit(file, change->trigger_player);
8018 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8019 log_2(change->trigger_page)));
8021 putFile8Bit(file, change->has_action);
8022 putFile8Bit(file, change->action_type);
8023 putFile8Bit(file, change->action_mode);
8024 putFile16BitBE(file, change->action_arg);
8026 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8028 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8029 if (change->has_event[j])
8030 event_bits |= (1u << (j - 32));
8031 putFile8Bit(file, event_bits);
8036 #if ENABLE_HISTORIC_CHUNKS
8037 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8039 struct ElementInfo *ei = &element_info[element];
8040 struct ElementGroupInfo *group = ei->group;
8043 putFile16BitBE(file, element);
8045 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8046 putFile8Bit(file, ei->description[i]);
8048 putFile8Bit(file, group->num_elements);
8050 putFile8Bit(file, ei->use_gfx_element);
8051 putFile16BitBE(file, ei->gfx_element_initial);
8053 putFile8Bit(file, group->choice_mode);
8055 // some free bytes for future values and padding
8056 WriteUnusedBytesToFile(file, 3);
8058 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8059 putFile16BitBE(file, group->element[i]);
8063 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8064 boolean write_element)
8066 int save_type = entry->save_type;
8067 int data_type = entry->data_type;
8068 int conf_type = entry->conf_type;
8069 int byte_mask = conf_type & CONF_MASK_BYTES;
8070 int element = entry->element;
8071 int default_value = entry->default_value;
8073 boolean modified = FALSE;
8075 if (byte_mask != CONF_MASK_MULTI_BYTES)
8077 void *value_ptr = entry->value;
8078 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8081 // check if any settings have been modified before saving them
8082 if (value != default_value)
8085 // do not save if explicitly told or if unmodified default settings
8086 if ((save_type == SAVE_CONF_NEVER) ||
8087 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8091 num_bytes += putFile16BitBE(file, element);
8093 num_bytes += putFile8Bit(file, conf_type);
8094 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8095 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8096 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8099 else if (data_type == TYPE_STRING)
8101 char *default_string = entry->default_string;
8102 char *string = (char *)(entry->value);
8103 int string_length = strlen(string);
8106 // check if any settings have been modified before saving them
8107 if (!strEqual(string, default_string))
8110 // do not save if explicitly told or if unmodified default settings
8111 if ((save_type == SAVE_CONF_NEVER) ||
8112 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8116 num_bytes += putFile16BitBE(file, element);
8118 num_bytes += putFile8Bit(file, conf_type);
8119 num_bytes += putFile16BitBE(file, string_length);
8121 for (i = 0; i < string_length; i++)
8122 num_bytes += putFile8Bit(file, string[i]);
8124 else if (data_type == TYPE_ELEMENT_LIST)
8126 int *element_array = (int *)(entry->value);
8127 int num_elements = *(int *)(entry->num_entities);
8130 // check if any settings have been modified before saving them
8131 for (i = 0; i < num_elements; i++)
8132 if (element_array[i] != default_value)
8135 // do not save if explicitly told or if unmodified default settings
8136 if ((save_type == SAVE_CONF_NEVER) ||
8137 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8141 num_bytes += putFile16BitBE(file, element);
8143 num_bytes += putFile8Bit(file, conf_type);
8144 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8146 for (i = 0; i < num_elements; i++)
8147 num_bytes += putFile16BitBE(file, element_array[i]);
8149 else if (data_type == TYPE_CONTENT_LIST)
8151 struct Content *content = (struct Content *)(entry->value);
8152 int num_contents = *(int *)(entry->num_entities);
8155 // check if any settings have been modified before saving them
8156 for (i = 0; i < num_contents; i++)
8157 for (y = 0; y < 3; y++)
8158 for (x = 0; x < 3; x++)
8159 if (content[i].e[x][y] != default_value)
8162 // do not save if explicitly told or if unmodified default settings
8163 if ((save_type == SAVE_CONF_NEVER) ||
8164 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8168 num_bytes += putFile16BitBE(file, element);
8170 num_bytes += putFile8Bit(file, conf_type);
8171 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8173 for (i = 0; i < num_contents; i++)
8174 for (y = 0; y < 3; y++)
8175 for (x = 0; x < 3; x++)
8176 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8182 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8187 li = *level; // copy level data into temporary buffer
8189 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8190 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8195 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8200 li = *level; // copy level data into temporary buffer
8202 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8203 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8208 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8210 int envelope_nr = element - EL_ENVELOPE_1;
8214 chunk_size += putFile16BitBE(file, element);
8216 // copy envelope data into temporary buffer
8217 xx_envelope = level->envelope[envelope_nr];
8219 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8220 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8225 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8227 struct ElementInfo *ei = &element_info[element];
8231 chunk_size += putFile16BitBE(file, element);
8233 xx_ei = *ei; // copy element data into temporary buffer
8235 // set default description string for this specific element
8236 strcpy(xx_default_description, getDefaultElementDescription(ei));
8238 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8239 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8241 for (i = 0; i < ei->num_change_pages; i++)
8243 struct ElementChangeInfo *change = &ei->change_page[i];
8245 xx_current_change_page = i;
8247 xx_change = *change; // copy change data into temporary buffer
8250 setEventBitsFromEventFlags(change);
8252 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8253 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8260 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8262 struct ElementInfo *ei = &element_info[element];
8263 struct ElementGroupInfo *group = ei->group;
8267 chunk_size += putFile16BitBE(file, element);
8269 xx_ei = *ei; // copy element data into temporary buffer
8270 xx_group = *group; // copy group 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_GRPX[i].data_type != -1; i++)
8276 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8281 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8283 struct ElementInfo *ei = &element_info[element];
8287 chunk_size += putFile16BitBE(file, element);
8289 xx_ei = *ei; // copy element data into temporary buffer
8291 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8292 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8297 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8298 boolean save_as_template)
8304 if (!(file = fopen(filename, MODE_WRITE)))
8306 Warn("cannot save level file '%s'", filename);
8311 level->file_version = FILE_VERSION_ACTUAL;
8312 level->game_version = GAME_VERSION_ACTUAL;
8314 level->creation_date = getCurrentDate();
8316 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8317 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8319 chunk_size = SaveLevel_VERS(NULL, level);
8320 putFileChunkBE(file, "VERS", chunk_size);
8321 SaveLevel_VERS(file, level);
8323 chunk_size = SaveLevel_DATE(NULL, level);
8324 putFileChunkBE(file, "DATE", chunk_size);
8325 SaveLevel_DATE(file, level);
8327 chunk_size = SaveLevel_NAME(NULL, level);
8328 putFileChunkBE(file, "NAME", chunk_size);
8329 SaveLevel_NAME(file, level);
8331 chunk_size = SaveLevel_AUTH(NULL, level);
8332 putFileChunkBE(file, "AUTH", chunk_size);
8333 SaveLevel_AUTH(file, level);
8335 chunk_size = SaveLevel_INFO(NULL, level);
8336 putFileChunkBE(file, "INFO", chunk_size);
8337 SaveLevel_INFO(file, level);
8339 chunk_size = SaveLevel_BODY(NULL, level);
8340 putFileChunkBE(file, "BODY", chunk_size);
8341 SaveLevel_BODY(file, level);
8343 chunk_size = SaveLevel_ELEM(NULL, level);
8344 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8346 putFileChunkBE(file, "ELEM", chunk_size);
8347 SaveLevel_ELEM(file, level);
8350 for (i = 0; i < NUM_ENVELOPES; i++)
8352 int element = EL_ENVELOPE_1 + i;
8354 chunk_size = SaveLevel_NOTE(NULL, level, element);
8355 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8357 putFileChunkBE(file, "NOTE", chunk_size);
8358 SaveLevel_NOTE(file, level, element);
8362 // if not using template level, check for non-default custom/group elements
8363 if (!level->use_custom_template || save_as_template)
8365 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8367 int element = EL_CUSTOM_START + i;
8369 chunk_size = SaveLevel_CUSX(NULL, level, element);
8370 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8372 putFileChunkBE(file, "CUSX", chunk_size);
8373 SaveLevel_CUSX(file, level, element);
8377 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8379 int element = EL_GROUP_START + i;
8381 chunk_size = SaveLevel_GRPX(NULL, level, element);
8382 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8384 putFileChunkBE(file, "GRPX", chunk_size);
8385 SaveLevel_GRPX(file, level, element);
8389 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8391 int element = GET_EMPTY_ELEMENT(i);
8393 chunk_size = SaveLevel_EMPX(NULL, level, element);
8394 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8396 putFileChunkBE(file, "EMPX", chunk_size);
8397 SaveLevel_EMPX(file, level, element);
8404 SetFilePermissions(filename, PERMS_PRIVATE);
8407 void SaveLevel(int nr)
8409 char *filename = getDefaultLevelFilename(nr);
8411 SaveLevelFromFilename(&level, filename, FALSE);
8414 void SaveLevelTemplate(void)
8416 char *filename = getLocalLevelTemplateFilename();
8418 SaveLevelFromFilename(&level, filename, TRUE);
8421 boolean SaveLevelChecked(int nr)
8423 char *filename = getDefaultLevelFilename(nr);
8424 boolean new_level = !fileExists(filename);
8425 boolean level_saved = FALSE;
8427 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8432 Request("Level saved!", REQ_CONFIRM);
8440 void DumpLevel(struct LevelInfo *level)
8442 if (level->no_level_file || level->no_valid_file)
8444 Warn("cannot dump -- no valid level file found");
8450 Print("Level xxx (file version %08d, game version %08d)\n",
8451 level->file_version, level->game_version);
8454 Print("Level author: '%s'\n", level->author);
8455 Print("Level title: '%s'\n", level->name);
8457 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8459 Print("Level time: %d seconds\n", level->time);
8460 Print("Gems needed: %d\n", level->gems_needed);
8462 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8463 Print("Time for wheel: %d seconds\n", level->time_wheel);
8464 Print("Time for light: %d seconds\n", level->time_light);
8465 Print("Time for timegate: %d seconds\n", level->time_timegate);
8467 Print("Amoeba speed: %d\n", level->amoeba_speed);
8470 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8471 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8472 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8473 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8474 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8475 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8481 for (i = 0; i < NUM_ENVELOPES; i++)
8483 char *text = level->envelope[i].text;
8484 int text_len = strlen(text);
8485 boolean has_text = FALSE;
8487 for (j = 0; j < text_len; j++)
8488 if (text[j] != ' ' && text[j] != '\n')
8494 Print("Envelope %d:\n'%s'\n", i + 1, text);
8502 void DumpLevels(void)
8504 static LevelDirTree *dumplevel_leveldir = NULL;
8506 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8507 global.dumplevel_leveldir);
8509 if (dumplevel_leveldir == NULL)
8510 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8512 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8513 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8514 Fail("no such level number: %d", global.dumplevel_level_nr);
8516 leveldir_current = dumplevel_leveldir;
8518 LoadLevel(global.dumplevel_level_nr);
8525 // ============================================================================
8526 // tape file functions
8527 // ============================================================================
8529 static void setTapeInfoToDefaults(void)
8533 // always start with reliable default values (empty tape)
8536 // default values (also for pre-1.2 tapes) with only the first player
8537 tape.player_participates[0] = TRUE;
8538 for (i = 1; i < MAX_PLAYERS; i++)
8539 tape.player_participates[i] = FALSE;
8541 // at least one (default: the first) player participates in every tape
8542 tape.num_participating_players = 1;
8544 tape.property_bits = TAPE_PROPERTY_NONE;
8546 tape.level_nr = level_nr;
8548 tape.changed = FALSE;
8549 tape.solved = FALSE;
8551 tape.recording = FALSE;
8552 tape.playing = FALSE;
8553 tape.pausing = FALSE;
8555 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8556 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8558 tape.no_info_chunk = TRUE;
8559 tape.no_valid_file = FALSE;
8562 static int getTapePosSize(struct TapeInfo *tape)
8564 int tape_pos_size = 0;
8566 if (tape->use_key_actions)
8567 tape_pos_size += tape->num_participating_players;
8569 if (tape->use_mouse_actions)
8570 tape_pos_size += 3; // x and y position and mouse button mask
8572 tape_pos_size += 1; // tape action delay value
8574 return tape_pos_size;
8577 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8579 tape->use_key_actions = FALSE;
8580 tape->use_mouse_actions = FALSE;
8582 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8583 tape->use_key_actions = TRUE;
8585 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8586 tape->use_mouse_actions = TRUE;
8589 static int getTapeActionValue(struct TapeInfo *tape)
8591 return (tape->use_key_actions &&
8592 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8593 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8594 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8595 TAPE_ACTIONS_DEFAULT);
8598 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8600 tape->file_version = getFileVersion(file);
8601 tape->game_version = getFileVersion(file);
8606 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8610 tape->random_seed = getFile32BitBE(file);
8611 tape->date = getFile32BitBE(file);
8612 tape->length = getFile32BitBE(file);
8614 // read header fields that are new since version 1.2
8615 if (tape->file_version >= FILE_VERSION_1_2)
8617 byte store_participating_players = getFile8Bit(file);
8620 // since version 1.2, tapes store which players participate in the tape
8621 tape->num_participating_players = 0;
8622 for (i = 0; i < MAX_PLAYERS; i++)
8624 tape->player_participates[i] = FALSE;
8626 if (store_participating_players & (1 << i))
8628 tape->player_participates[i] = TRUE;
8629 tape->num_participating_players++;
8633 setTapeActionFlags(tape, getFile8Bit(file));
8635 tape->property_bits = getFile8Bit(file);
8636 tape->solved = getFile8Bit(file);
8638 engine_version = getFileVersion(file);
8639 if (engine_version > 0)
8640 tape->engine_version = engine_version;
8642 tape->engine_version = tape->game_version;
8648 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8650 tape->scr_fieldx = getFile8Bit(file);
8651 tape->scr_fieldy = getFile8Bit(file);
8656 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8658 char *level_identifier = NULL;
8659 int level_identifier_size;
8662 tape->no_info_chunk = FALSE;
8664 level_identifier_size = getFile16BitBE(file);
8666 level_identifier = checked_malloc(level_identifier_size);
8668 for (i = 0; i < level_identifier_size; i++)
8669 level_identifier[i] = getFile8Bit(file);
8671 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8672 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8674 checked_free(level_identifier);
8676 tape->level_nr = getFile16BitBE(file);
8678 chunk_size = 2 + level_identifier_size + 2;
8683 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8686 int tape_pos_size = getTapePosSize(tape);
8687 int chunk_size_expected = tape_pos_size * tape->length;
8689 if (chunk_size_expected != chunk_size)
8691 ReadUnusedBytesFromFile(file, chunk_size);
8692 return chunk_size_expected;
8695 for (i = 0; i < tape->length; i++)
8697 if (i >= MAX_TAPE_LEN)
8699 Warn("tape truncated -- size exceeds maximum tape size %d",
8702 // tape too large; read and ignore remaining tape data from this chunk
8703 for (;i < tape->length; i++)
8704 ReadUnusedBytesFromFile(file, tape_pos_size);
8709 if (tape->use_key_actions)
8711 for (j = 0; j < MAX_PLAYERS; j++)
8713 tape->pos[i].action[j] = MV_NONE;
8715 if (tape->player_participates[j])
8716 tape->pos[i].action[j] = getFile8Bit(file);
8720 if (tape->use_mouse_actions)
8722 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8723 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8724 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8727 tape->pos[i].delay = getFile8Bit(file);
8729 if (tape->file_version == FILE_VERSION_1_0)
8731 // eliminate possible diagonal moves in old tapes
8732 // this is only for backward compatibility
8734 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8735 byte action = tape->pos[i].action[0];
8736 int k, num_moves = 0;
8738 for (k = 0; k < 4; k++)
8740 if (action & joy_dir[k])
8742 tape->pos[i + num_moves].action[0] = joy_dir[k];
8744 tape->pos[i + num_moves].delay = 0;
8753 tape->length += num_moves;
8756 else if (tape->file_version < FILE_VERSION_2_0)
8758 // convert pre-2.0 tapes to new tape format
8760 if (tape->pos[i].delay > 1)
8763 tape->pos[i + 1] = tape->pos[i];
8764 tape->pos[i + 1].delay = 1;
8767 for (j = 0; j < MAX_PLAYERS; j++)
8768 tape->pos[i].action[j] = MV_NONE;
8769 tape->pos[i].delay--;
8776 if (checkEndOfFile(file))
8780 if (i != tape->length)
8781 chunk_size = tape_pos_size * i;
8786 static void LoadTape_SokobanSolution(char *filename)
8789 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8791 if (!(file = openFile(filename, MODE_READ)))
8793 tape.no_valid_file = TRUE;
8798 while (!checkEndOfFile(file))
8800 unsigned char c = getByteFromFile(file);
8802 if (checkEndOfFile(file))
8809 tape.pos[tape.length].action[0] = MV_UP;
8810 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8816 tape.pos[tape.length].action[0] = MV_DOWN;
8817 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8823 tape.pos[tape.length].action[0] = MV_LEFT;
8824 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8830 tape.pos[tape.length].action[0] = MV_RIGHT;
8831 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8839 // ignore white-space characters
8843 tape.no_valid_file = TRUE;
8845 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8853 if (tape.no_valid_file)
8856 tape.length_frames = GetTapeLengthFrames();
8857 tape.length_seconds = GetTapeLengthSeconds();
8860 void LoadTapeFromFilename(char *filename)
8862 char cookie[MAX_LINE_LEN];
8863 char chunk_name[CHUNK_ID_LEN + 1];
8867 // always start with reliable default values
8868 setTapeInfoToDefaults();
8870 if (strSuffix(filename, ".sln"))
8872 LoadTape_SokobanSolution(filename);
8877 if (!(file = openFile(filename, MODE_READ)))
8879 tape.no_valid_file = TRUE;
8884 getFileChunkBE(file, chunk_name, NULL);
8885 if (strEqual(chunk_name, "RND1"))
8887 getFile32BitBE(file); // not used
8889 getFileChunkBE(file, chunk_name, NULL);
8890 if (!strEqual(chunk_name, "TAPE"))
8892 tape.no_valid_file = TRUE;
8894 Warn("unknown format of tape file '%s'", filename);
8901 else // check for pre-2.0 file format with cookie string
8903 strcpy(cookie, chunk_name);
8904 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8906 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8907 cookie[strlen(cookie) - 1] = '\0';
8909 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8911 tape.no_valid_file = TRUE;
8913 Warn("unknown format of tape file '%s'", filename);
8920 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8922 tape.no_valid_file = TRUE;
8924 Warn("unsupported version of tape file '%s'", filename);
8931 // pre-2.0 tape files have no game version, so use file version here
8932 tape.game_version = tape.file_version;
8935 if (tape.file_version < FILE_VERSION_1_2)
8937 // tape files from versions before 1.2.0 without chunk structure
8938 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8939 LoadTape_BODY(file, 2 * tape.length, &tape);
8947 int (*loader)(File *, int, struct TapeInfo *);
8951 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8952 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8953 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8954 { "INFO", -1, LoadTape_INFO },
8955 { "BODY", -1, LoadTape_BODY },
8959 while (getFileChunkBE(file, chunk_name, &chunk_size))
8963 while (chunk_info[i].name != NULL &&
8964 !strEqual(chunk_name, chunk_info[i].name))
8967 if (chunk_info[i].name == NULL)
8969 Warn("unknown chunk '%s' in tape file '%s'",
8970 chunk_name, filename);
8972 ReadUnusedBytesFromFile(file, chunk_size);
8974 else if (chunk_info[i].size != -1 &&
8975 chunk_info[i].size != chunk_size)
8977 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8978 chunk_size, chunk_name, filename);
8980 ReadUnusedBytesFromFile(file, chunk_size);
8984 // call function to load this tape chunk
8985 int chunk_size_expected =
8986 (chunk_info[i].loader)(file, chunk_size, &tape);
8988 // the size of some chunks cannot be checked before reading other
8989 // chunks first (like "HEAD" and "BODY") that contain some header
8990 // information, so check them here
8991 if (chunk_size_expected != chunk_size)
8993 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8994 chunk_size, chunk_name, filename);
9002 tape.length_frames = GetTapeLengthFrames();
9003 tape.length_seconds = GetTapeLengthSeconds();
9006 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9008 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9010 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9011 tape.engine_version);
9015 void LoadTape(int nr)
9017 char *filename = getTapeFilename(nr);
9019 LoadTapeFromFilename(filename);
9022 void LoadSolutionTape(int nr)
9024 char *filename = getSolutionTapeFilename(nr);
9026 LoadTapeFromFilename(filename);
9028 if (TAPE_IS_EMPTY(tape))
9030 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9031 level.native_bd_level->replay != NULL)
9032 CopyNativeTape_BD_to_RND(&level);
9033 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9034 level.native_sp_level->demo.is_available)
9035 CopyNativeTape_SP_to_RND(&level);
9039 void LoadScoreTape(char *score_tape_basename, int nr)
9041 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9043 LoadTapeFromFilename(filename);
9046 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9048 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9050 LoadTapeFromFilename(filename);
9053 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9055 // chunk required for team mode tapes with non-default screen size
9056 return (tape->num_participating_players > 1 &&
9057 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9058 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9061 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9063 putFileVersion(file, tape->file_version);
9064 putFileVersion(file, tape->game_version);
9067 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9070 byte store_participating_players = 0;
9072 // set bits for participating players for compact storage
9073 for (i = 0; i < MAX_PLAYERS; i++)
9074 if (tape->player_participates[i])
9075 store_participating_players |= (1 << i);
9077 putFile32BitBE(file, tape->random_seed);
9078 putFile32BitBE(file, tape->date);
9079 putFile32BitBE(file, tape->length);
9081 putFile8Bit(file, store_participating_players);
9083 putFile8Bit(file, getTapeActionValue(tape));
9085 putFile8Bit(file, tape->property_bits);
9086 putFile8Bit(file, tape->solved);
9088 putFileVersion(file, tape->engine_version);
9091 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9093 putFile8Bit(file, tape->scr_fieldx);
9094 putFile8Bit(file, tape->scr_fieldy);
9097 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9099 int level_identifier_size = strlen(tape->level_identifier) + 1;
9102 putFile16BitBE(file, level_identifier_size);
9104 for (i = 0; i < level_identifier_size; i++)
9105 putFile8Bit(file, tape->level_identifier[i]);
9107 putFile16BitBE(file, tape->level_nr);
9110 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9114 for (i = 0; i < tape->length; i++)
9116 if (tape->use_key_actions)
9118 for (j = 0; j < MAX_PLAYERS; j++)
9119 if (tape->player_participates[j])
9120 putFile8Bit(file, tape->pos[i].action[j]);
9123 if (tape->use_mouse_actions)
9125 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9126 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9127 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9130 putFile8Bit(file, tape->pos[i].delay);
9134 void SaveTapeToFilename(char *filename)
9138 int info_chunk_size;
9139 int body_chunk_size;
9141 if (!(file = fopen(filename, MODE_WRITE)))
9143 Warn("cannot save level recording file '%s'", filename);
9148 tape_pos_size = getTapePosSize(&tape);
9150 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9151 body_chunk_size = tape_pos_size * tape.length;
9153 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9154 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9156 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9157 SaveTape_VERS(file, &tape);
9159 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9160 SaveTape_HEAD(file, &tape);
9162 if (checkSaveTape_SCRN(&tape))
9164 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9165 SaveTape_SCRN(file, &tape);
9168 putFileChunkBE(file, "INFO", info_chunk_size);
9169 SaveTape_INFO(file, &tape);
9171 putFileChunkBE(file, "BODY", body_chunk_size);
9172 SaveTape_BODY(file, &tape);
9176 SetFilePermissions(filename, PERMS_PRIVATE);
9179 static void SaveTapeExt(char *filename)
9183 tape.file_version = FILE_VERSION_ACTUAL;
9184 tape.game_version = GAME_VERSION_ACTUAL;
9186 tape.num_participating_players = 0;
9188 // count number of participating players
9189 for (i = 0; i < MAX_PLAYERS; i++)
9190 if (tape.player_participates[i])
9191 tape.num_participating_players++;
9193 SaveTapeToFilename(filename);
9195 tape.changed = FALSE;
9198 void SaveTape(int nr)
9200 char *filename = getTapeFilename(nr);
9202 InitTapeDirectory(leveldir_current->subdir);
9204 SaveTapeExt(filename);
9207 void SaveScoreTape(int nr)
9209 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9211 // used instead of "leveldir_current->subdir" (for network games)
9212 InitScoreTapeDirectory(levelset.identifier, nr);
9214 SaveTapeExt(filename);
9217 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9218 unsigned int req_state_added)
9220 char *filename = getTapeFilename(nr);
9221 boolean new_tape = !fileExists(filename);
9222 boolean tape_saved = FALSE;
9224 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9229 Request(msg_saved, REQ_CONFIRM | req_state_added);
9237 boolean SaveTapeChecked(int nr)
9239 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9242 boolean SaveTapeChecked_LevelSolved(int nr)
9244 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9245 "Level solved! Tape saved!", REQ_STAY_OPEN);
9248 void DumpTape(struct TapeInfo *tape)
9250 int tape_frame_counter;
9253 if (tape->no_valid_file)
9255 Warn("cannot dump -- no valid tape file found");
9262 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9263 tape->level_nr, tape->file_version, tape->game_version);
9264 Print(" (effective engine version %08d)\n",
9265 tape->engine_version);
9266 Print("Level series identifier: '%s'\n", tape->level_identifier);
9268 Print("Solution tape: %s\n",
9269 tape->solved ? "yes" :
9270 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9272 Print("Special tape properties: ");
9273 if (tape->property_bits == TAPE_PROPERTY_NONE)
9275 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9276 Print("[em_random_bug]");
9277 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9278 Print("[game_speed]");
9279 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9281 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9282 Print("[single_step]");
9283 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9284 Print("[snapshot]");
9285 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9286 Print("[replayed]");
9287 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9288 Print("[tas_keys]");
9289 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9290 Print("[small_graphics]");
9293 int year2 = tape->date / 10000;
9294 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9295 int month_index_raw = (tape->date / 100) % 100;
9296 int month_index = month_index_raw % 12; // prevent invalid index
9297 int month = month_index + 1;
9298 int day = tape->date % 100;
9300 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9304 tape_frame_counter = 0;
9306 for (i = 0; i < tape->length; i++)
9308 if (i >= MAX_TAPE_LEN)
9313 for (j = 0; j < MAX_PLAYERS; j++)
9315 if (tape->player_participates[j])
9317 int action = tape->pos[i].action[j];
9319 Print("%d:%02x ", j, action);
9320 Print("[%c%c%c%c|%c%c] - ",
9321 (action & JOY_LEFT ? '<' : ' '),
9322 (action & JOY_RIGHT ? '>' : ' '),
9323 (action & JOY_UP ? '^' : ' '),
9324 (action & JOY_DOWN ? 'v' : ' '),
9325 (action & JOY_BUTTON_1 ? '1' : ' '),
9326 (action & JOY_BUTTON_2 ? '2' : ' '));
9330 Print("(%03d) ", tape->pos[i].delay);
9331 Print("[%05d]\n", tape_frame_counter);
9333 tape_frame_counter += tape->pos[i].delay;
9339 void DumpTapes(void)
9341 static LevelDirTree *dumptape_leveldir = NULL;
9343 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9344 global.dumptape_leveldir);
9346 if (dumptape_leveldir == NULL)
9347 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9349 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9350 global.dumptape_level_nr > dumptape_leveldir->last_level)
9351 Fail("no such level number: %d", global.dumptape_level_nr);
9353 leveldir_current = dumptape_leveldir;
9355 if (options.mytapes)
9356 LoadTape(global.dumptape_level_nr);
9358 LoadSolutionTape(global.dumptape_level_nr);
9366 // ============================================================================
9367 // score file functions
9368 // ============================================================================
9370 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9374 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9376 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9377 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9378 scores->entry[i].score = 0;
9379 scores->entry[i].time = 0;
9381 scores->entry[i].id = -1;
9382 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9383 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9384 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9385 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9386 strcpy(scores->entry[i].country_code, "??");
9389 scores->num_entries = 0;
9390 scores->last_added = -1;
9391 scores->last_added_local = -1;
9393 scores->updated = FALSE;
9394 scores->uploaded = FALSE;
9395 scores->tape_downloaded = FALSE;
9396 scores->force_last_added = FALSE;
9398 // The following values are intentionally not reset here:
9402 // - continue_playing
9403 // - continue_on_return
9406 static void setScoreInfoToDefaults(void)
9408 setScoreInfoToDefaultsExt(&scores);
9411 static void setServerScoreInfoToDefaults(void)
9413 setScoreInfoToDefaultsExt(&server_scores);
9416 static void LoadScore_OLD(int nr)
9419 char *filename = getScoreFilename(nr);
9420 char cookie[MAX_LINE_LEN];
9421 char line[MAX_LINE_LEN];
9425 if (!(file = fopen(filename, MODE_READ)))
9428 // check file identifier
9429 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9431 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9432 cookie[strlen(cookie) - 1] = '\0';
9434 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9436 Warn("unknown format of score file '%s'", filename);
9443 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9445 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9446 Warn("fscanf() failed; %s", strerror(errno));
9448 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9451 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9452 line[strlen(line) - 1] = '\0';
9454 for (line_ptr = line; *line_ptr; line_ptr++)
9456 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9458 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9459 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9468 static void ConvertScore_OLD(void)
9470 // only convert score to time for levels that rate playing time over score
9471 if (!level.rate_time_over_score)
9474 // convert old score to playing time for score-less levels (like Supaplex)
9475 int time_final_max = 999;
9478 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9480 int score = scores.entry[i].score;
9482 if (score > 0 && score < time_final_max)
9483 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9487 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9489 scores->file_version = getFileVersion(file);
9490 scores->game_version = getFileVersion(file);
9495 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9497 char *level_identifier = NULL;
9498 int level_identifier_size;
9501 level_identifier_size = getFile16BitBE(file);
9503 level_identifier = checked_malloc(level_identifier_size);
9505 for (i = 0; i < level_identifier_size; i++)
9506 level_identifier[i] = getFile8Bit(file);
9508 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9509 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9511 checked_free(level_identifier);
9513 scores->level_nr = getFile16BitBE(file);
9514 scores->num_entries = getFile16BitBE(file);
9516 chunk_size = 2 + level_identifier_size + 2 + 2;
9521 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9525 for (i = 0; i < scores->num_entries; i++)
9527 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9528 scores->entry[i].name[j] = getFile8Bit(file);
9530 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9533 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9538 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9542 for (i = 0; i < scores->num_entries; i++)
9543 scores->entry[i].score = getFile16BitBE(file);
9545 chunk_size = scores->num_entries * 2;
9550 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9554 for (i = 0; i < scores->num_entries; i++)
9555 scores->entry[i].score = getFile32BitBE(file);
9557 chunk_size = scores->num_entries * 4;
9562 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9566 for (i = 0; i < scores->num_entries; i++)
9567 scores->entry[i].time = getFile32BitBE(file);
9569 chunk_size = scores->num_entries * 4;
9574 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9578 for (i = 0; i < scores->num_entries; i++)
9580 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9581 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9583 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9586 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9591 void LoadScore(int nr)
9593 char *filename = getScoreFilename(nr);
9594 char cookie[MAX_LINE_LEN];
9595 char chunk_name[CHUNK_ID_LEN + 1];
9597 boolean old_score_file_format = FALSE;
9600 // always start with reliable default values
9601 setScoreInfoToDefaults();
9603 if (!(file = openFile(filename, MODE_READ)))
9606 getFileChunkBE(file, chunk_name, NULL);
9607 if (strEqual(chunk_name, "RND1"))
9609 getFile32BitBE(file); // not used
9611 getFileChunkBE(file, chunk_name, NULL);
9612 if (!strEqual(chunk_name, "SCOR"))
9614 Warn("unknown format of score file '%s'", filename);
9621 else // check for old file format with cookie string
9623 strcpy(cookie, chunk_name);
9624 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9626 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9627 cookie[strlen(cookie) - 1] = '\0';
9629 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9631 Warn("unknown format of score file '%s'", filename);
9638 old_score_file_format = TRUE;
9641 if (old_score_file_format)
9643 // score files from versions before 4.2.4.0 without chunk structure
9646 // convert score to time, if possible (mainly for Supaplex levels)
9655 int (*loader)(File *, int, struct ScoreInfo *);
9659 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9660 { "INFO", -1, LoadScore_INFO },
9661 { "NAME", -1, LoadScore_NAME },
9662 { "SCOR", -1, LoadScore_SCOR },
9663 { "SC4R", -1, LoadScore_SC4R },
9664 { "TIME", -1, LoadScore_TIME },
9665 { "TAPE", -1, LoadScore_TAPE },
9670 while (getFileChunkBE(file, chunk_name, &chunk_size))
9674 while (chunk_info[i].name != NULL &&
9675 !strEqual(chunk_name, chunk_info[i].name))
9678 if (chunk_info[i].name == NULL)
9680 Warn("unknown chunk '%s' in score file '%s'",
9681 chunk_name, filename);
9683 ReadUnusedBytesFromFile(file, chunk_size);
9685 else if (chunk_info[i].size != -1 &&
9686 chunk_info[i].size != chunk_size)
9688 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9689 chunk_size, chunk_name, filename);
9691 ReadUnusedBytesFromFile(file, chunk_size);
9695 // call function to load this score chunk
9696 int chunk_size_expected =
9697 (chunk_info[i].loader)(file, chunk_size, &scores);
9699 // the size of some chunks cannot be checked before reading other
9700 // chunks first (like "HEAD" and "BODY") that contain some header
9701 // information, so check them here
9702 if (chunk_size_expected != chunk_size)
9704 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9705 chunk_size, chunk_name, filename);
9714 #if ENABLE_HISTORIC_CHUNKS
9715 void SaveScore_OLD(int nr)
9718 char *filename = getScoreFilename(nr);
9721 // used instead of "leveldir_current->subdir" (for network games)
9722 InitScoreDirectory(levelset.identifier);
9724 if (!(file = fopen(filename, MODE_WRITE)))
9726 Warn("cannot save score for level %d", nr);
9731 fprintf(file, "%s\n\n", SCORE_COOKIE);
9733 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9734 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9738 SetFilePermissions(filename, PERMS_PRIVATE);
9742 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9744 putFileVersion(file, scores->file_version);
9745 putFileVersion(file, scores->game_version);
9748 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9750 int level_identifier_size = strlen(scores->level_identifier) + 1;
9753 putFile16BitBE(file, level_identifier_size);
9755 for (i = 0; i < level_identifier_size; i++)
9756 putFile8Bit(file, scores->level_identifier[i]);
9758 putFile16BitBE(file, scores->level_nr);
9759 putFile16BitBE(file, scores->num_entries);
9762 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9766 for (i = 0; i < scores->num_entries; i++)
9768 int name_size = strlen(scores->entry[i].name);
9770 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9771 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9775 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9779 for (i = 0; i < scores->num_entries; i++)
9780 putFile16BitBE(file, scores->entry[i].score);
9783 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9787 for (i = 0; i < scores->num_entries; i++)
9788 putFile32BitBE(file, scores->entry[i].score);
9791 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9795 for (i = 0; i < scores->num_entries; i++)
9796 putFile32BitBE(file, scores->entry[i].time);
9799 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9803 for (i = 0; i < scores->num_entries; i++)
9805 int size = strlen(scores->entry[i].tape_basename);
9807 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9808 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9812 static void SaveScoreToFilename(char *filename)
9815 int info_chunk_size;
9816 int name_chunk_size;
9817 int scor_chunk_size;
9818 int sc4r_chunk_size;
9819 int time_chunk_size;
9820 int tape_chunk_size;
9821 boolean has_large_score_values;
9824 if (!(file = fopen(filename, MODE_WRITE)))
9826 Warn("cannot save score file '%s'", filename);
9831 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9832 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9833 scor_chunk_size = scores.num_entries * 2;
9834 sc4r_chunk_size = scores.num_entries * 4;
9835 time_chunk_size = scores.num_entries * 4;
9836 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9838 has_large_score_values = FALSE;
9839 for (i = 0; i < scores.num_entries; i++)
9840 if (scores.entry[i].score > 0xffff)
9841 has_large_score_values = TRUE;
9843 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9844 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9846 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9847 SaveScore_VERS(file, &scores);
9849 putFileChunkBE(file, "INFO", info_chunk_size);
9850 SaveScore_INFO(file, &scores);
9852 putFileChunkBE(file, "NAME", name_chunk_size);
9853 SaveScore_NAME(file, &scores);
9855 if (has_large_score_values)
9857 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9858 SaveScore_SC4R(file, &scores);
9862 putFileChunkBE(file, "SCOR", scor_chunk_size);
9863 SaveScore_SCOR(file, &scores);
9866 putFileChunkBE(file, "TIME", time_chunk_size);
9867 SaveScore_TIME(file, &scores);
9869 putFileChunkBE(file, "TAPE", tape_chunk_size);
9870 SaveScore_TAPE(file, &scores);
9874 SetFilePermissions(filename, PERMS_PRIVATE);
9877 void SaveScore(int nr)
9879 char *filename = getScoreFilename(nr);
9882 // used instead of "leveldir_current->subdir" (for network games)
9883 InitScoreDirectory(levelset.identifier);
9885 scores.file_version = FILE_VERSION_ACTUAL;
9886 scores.game_version = GAME_VERSION_ACTUAL;
9888 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9889 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9890 scores.level_nr = level_nr;
9892 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9893 if (scores.entry[i].score == 0 &&
9894 scores.entry[i].time == 0 &&
9895 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9898 scores.num_entries = i;
9900 if (scores.num_entries == 0)
9903 SaveScoreToFilename(filename);
9906 static void LoadServerScoreFromCache(int nr)
9908 struct ScoreEntry score_entry;
9917 { &score_entry.score, FALSE, 0 },
9918 { &score_entry.time, FALSE, 0 },
9919 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9920 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9921 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9922 { &score_entry.id, FALSE, 0 },
9923 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9924 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9925 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9926 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9930 char *filename = getScoreCacheFilename(nr);
9931 SetupFileHash *score_hash = loadSetupFileHash(filename);
9934 server_scores.num_entries = 0;
9936 if (score_hash == NULL)
9939 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9941 score_entry = server_scores.entry[i];
9943 for (j = 0; score_mapping[j].value != NULL; j++)
9947 sprintf(token, "%02d.%d", i, j);
9949 char *value = getHashEntry(score_hash, token);
9954 if (score_mapping[j].is_string)
9956 char *score_value = (char *)score_mapping[j].value;
9957 int value_size = score_mapping[j].string_size;
9959 strncpy(score_value, value, value_size);
9960 score_value[value_size] = '\0';
9964 int *score_value = (int *)score_mapping[j].value;
9966 *score_value = atoi(value);
9969 server_scores.num_entries = i + 1;
9972 server_scores.entry[i] = score_entry;
9975 freeSetupFileHash(score_hash);
9978 void LoadServerScore(int nr, boolean download_score)
9980 if (!setup.use_api_server)
9983 // always start with reliable default values
9984 setServerScoreInfoToDefaults();
9986 // 1st step: load server scores from cache file (which may not exist)
9987 // (this should prevent reading it while the thread is writing to it)
9988 LoadServerScoreFromCache(nr);
9990 if (download_score && runtime.use_api_server)
9992 // 2nd step: download server scores from score server to cache file
9993 // (as thread, as it might time out if the server is not reachable)
9994 ApiGetScoreAsThread(nr);
9998 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10000 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10002 // if score tape not uploaded, ask for uploading missing tapes later
10003 if (!setup.has_remaining_tapes)
10004 setup.ask_for_remaining_tapes = TRUE;
10006 setup.provide_uploading_tapes = TRUE;
10007 setup.has_remaining_tapes = TRUE;
10009 SaveSetup_ServerSetup();
10012 void SaveServerScore(int nr, boolean tape_saved)
10014 if (!runtime.use_api_server)
10016 PrepareScoreTapesForUpload(leveldir_current->subdir);
10021 ApiAddScoreAsThread(nr, tape_saved, NULL);
10024 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10025 char *score_tape_filename)
10027 if (!runtime.use_api_server)
10030 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10033 void LoadLocalAndServerScore(int nr, boolean download_score)
10035 int last_added_local = scores.last_added_local;
10036 boolean force_last_added = scores.force_last_added;
10038 // needed if only showing server scores
10039 setScoreInfoToDefaults();
10041 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10044 // restore last added local score entry (before merging server scores)
10045 scores.last_added = scores.last_added_local = last_added_local;
10047 if (setup.use_api_server &&
10048 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10050 // load server scores from cache file and trigger update from server
10051 LoadServerScore(nr, download_score);
10053 // merge local scores with scores from server
10054 MergeServerScore();
10057 if (force_last_added)
10058 scores.force_last_added = force_last_added;
10062 // ============================================================================
10063 // setup file functions
10064 // ============================================================================
10066 #define TOKEN_STR_PLAYER_PREFIX "player_"
10069 static struct TokenInfo global_setup_tokens[] =
10073 &setup.player_name, "player_name"
10077 &setup.multiple_users, "multiple_users"
10081 &setup.sound, "sound"
10085 &setup.sound_loops, "repeating_sound_loops"
10089 &setup.sound_music, "background_music"
10093 &setup.sound_simple, "simple_sound_effects"
10097 &setup.toons, "toons"
10101 &setup.global_animations, "global_animations"
10105 &setup.scroll_delay, "scroll_delay"
10109 &setup.forced_scroll_delay, "forced_scroll_delay"
10113 &setup.scroll_delay_value, "scroll_delay_value"
10117 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10121 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10125 &setup.fade_screens, "fade_screens"
10129 &setup.autorecord, "automatic_tape_recording"
10133 &setup.autorecord_after_replay, "autorecord_after_replay"
10137 &setup.auto_pause_on_start, "auto_pause_on_start"
10141 &setup.show_titlescreen, "show_titlescreen"
10145 &setup.quick_doors, "quick_doors"
10149 &setup.team_mode, "team_mode"
10153 &setup.handicap, "handicap"
10157 &setup.skip_levels, "skip_levels"
10161 &setup.increment_levels, "increment_levels"
10165 &setup.auto_play_next_level, "auto_play_next_level"
10169 &setup.count_score_after_game, "count_score_after_game"
10173 &setup.show_scores_after_game, "show_scores_after_game"
10177 &setup.time_limit, "time_limit"
10181 &setup.fullscreen, "fullscreen"
10185 &setup.window_scaling_percent, "window_scaling_percent"
10189 &setup.window_scaling_quality, "window_scaling_quality"
10193 &setup.screen_rendering_mode, "screen_rendering_mode"
10197 &setup.vsync_mode, "vsync_mode"
10201 &setup.ask_on_escape, "ask_on_escape"
10205 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10209 &setup.ask_on_game_over, "ask_on_game_over"
10213 &setup.ask_on_quit_game, "ask_on_quit_game"
10217 &setup.ask_on_quit_program, "ask_on_quit_program"
10221 &setup.quick_switch, "quick_player_switch"
10225 &setup.input_on_focus, "input_on_focus"
10229 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10233 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10237 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10241 &setup.game_speed_extended, "game_speed_extended"
10245 &setup.game_frame_delay, "game_frame_delay"
10249 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10253 &setup.bd_skip_hatching, "bd_skip_hatching"
10257 &setup.bd_scroll_delay, "bd_scroll_delay"
10261 &setup.bd_smooth_movements, "bd_smooth_movements"
10265 &setup.sp_show_border_elements, "sp_show_border_elements"
10269 &setup.small_game_graphics, "small_game_graphics"
10273 &setup.show_load_save_buttons, "show_load_save_buttons"
10277 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10281 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10285 &setup.graphics_set, "graphics_set"
10289 &setup.sounds_set, "sounds_set"
10293 &setup.music_set, "music_set"
10297 &setup.override_level_graphics, "override_level_graphics"
10301 &setup.override_level_sounds, "override_level_sounds"
10305 &setup.override_level_music, "override_level_music"
10309 &setup.volume_simple, "volume_simple"
10313 &setup.volume_loops, "volume_loops"
10317 &setup.volume_music, "volume_music"
10321 &setup.network_mode, "network_mode"
10325 &setup.network_player_nr, "network_player"
10329 &setup.network_server_hostname, "network_server_hostname"
10333 &setup.touch.control_type, "touch.control_type"
10337 &setup.touch.move_distance, "touch.move_distance"
10341 &setup.touch.drop_distance, "touch.drop_distance"
10345 &setup.touch.transparency, "touch.transparency"
10349 &setup.touch.draw_outlined, "touch.draw_outlined"
10353 &setup.touch.draw_pressed, "touch.draw_pressed"
10357 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10361 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10365 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10369 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10373 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10377 static struct TokenInfo auto_setup_tokens[] =
10381 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10385 static struct TokenInfo server_setup_tokens[] =
10389 &setup.player_uuid, "player_uuid"
10393 &setup.player_version, "player_version"
10397 &setup.use_api_server, TEST_PREFIX "use_api_server"
10401 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10405 &setup.api_server_password, TEST_PREFIX "api_server_password"
10409 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10413 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10417 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10421 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10425 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10429 static struct TokenInfo editor_setup_tokens[] =
10433 &setup.editor.el_classic, "editor.el_classic"
10437 &setup.editor.el_custom, "editor.el_custom"
10441 &setup.editor.el_user_defined, "editor.el_user_defined"
10445 &setup.editor.el_dynamic, "editor.el_dynamic"
10449 &setup.editor.el_headlines, "editor.el_headlines"
10453 &setup.editor.show_element_token, "editor.show_element_token"
10457 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10461 static struct TokenInfo editor_cascade_setup_tokens[] =
10465 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10469 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10473 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10477 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10481 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10485 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10489 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10493 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10497 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10501 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10505 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10509 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10513 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10517 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10521 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10525 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10529 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10533 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10537 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10541 static struct TokenInfo shortcut_setup_tokens[] =
10545 &setup.shortcut.save_game, "shortcut.save_game"
10549 &setup.shortcut.load_game, "shortcut.load_game"
10553 &setup.shortcut.restart_game, "shortcut.restart_game"
10557 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10561 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10565 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10569 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10573 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10577 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10581 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10585 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10589 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10593 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10597 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10601 &setup.shortcut.tape_record, "shortcut.tape_record"
10605 &setup.shortcut.tape_play, "shortcut.tape_play"
10609 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10613 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10617 &setup.shortcut.sound_music, "shortcut.sound_music"
10621 &setup.shortcut.snap_left, "shortcut.snap_left"
10625 &setup.shortcut.snap_right, "shortcut.snap_right"
10629 &setup.shortcut.snap_up, "shortcut.snap_up"
10633 &setup.shortcut.snap_down, "shortcut.snap_down"
10637 static struct SetupInputInfo setup_input;
10638 static struct TokenInfo player_setup_tokens[] =
10642 &setup_input.use_joystick, ".use_joystick"
10646 &setup_input.joy.device_name, ".joy.device_name"
10650 &setup_input.joy.xleft, ".joy.xleft"
10654 &setup_input.joy.xmiddle, ".joy.xmiddle"
10658 &setup_input.joy.xright, ".joy.xright"
10662 &setup_input.joy.yupper, ".joy.yupper"
10666 &setup_input.joy.ymiddle, ".joy.ymiddle"
10670 &setup_input.joy.ylower, ".joy.ylower"
10674 &setup_input.joy.snap, ".joy.snap_field"
10678 &setup_input.joy.drop, ".joy.place_bomb"
10682 &setup_input.key.left, ".key.move_left"
10686 &setup_input.key.right, ".key.move_right"
10690 &setup_input.key.up, ".key.move_up"
10694 &setup_input.key.down, ".key.move_down"
10698 &setup_input.key.snap, ".key.snap_field"
10702 &setup_input.key.drop, ".key.place_bomb"
10706 static struct TokenInfo system_setup_tokens[] =
10710 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10714 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10718 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10722 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10726 static struct TokenInfo internal_setup_tokens[] =
10730 &setup.internal.program_title, "program_title"
10734 &setup.internal.program_version, "program_version"
10738 &setup.internal.program_author, "program_author"
10742 &setup.internal.program_email, "program_email"
10746 &setup.internal.program_website, "program_website"
10750 &setup.internal.program_copyright, "program_copyright"
10754 &setup.internal.program_company, "program_company"
10758 &setup.internal.program_icon_file, "program_icon_file"
10762 &setup.internal.default_graphics_set, "default_graphics_set"
10766 &setup.internal.default_sounds_set, "default_sounds_set"
10770 &setup.internal.default_music_set, "default_music_set"
10774 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10778 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10782 &setup.internal.fallback_music_file, "fallback_music_file"
10786 &setup.internal.default_level_series, "default_level_series"
10790 &setup.internal.default_window_width, "default_window_width"
10794 &setup.internal.default_window_height, "default_window_height"
10798 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10802 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10806 &setup.internal.create_user_levelset, "create_user_levelset"
10810 &setup.internal.info_screens_from_main, "info_screens_from_main"
10814 &setup.internal.menu_game, "menu_game"
10818 &setup.internal.menu_engines, "menu_engines"
10822 &setup.internal.menu_editor, "menu_editor"
10826 &setup.internal.menu_graphics, "menu_graphics"
10830 &setup.internal.menu_sound, "menu_sound"
10834 &setup.internal.menu_artwork, "menu_artwork"
10838 &setup.internal.menu_input, "menu_input"
10842 &setup.internal.menu_touch, "menu_touch"
10846 &setup.internal.menu_shortcuts, "menu_shortcuts"
10850 &setup.internal.menu_exit, "menu_exit"
10854 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10858 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10862 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10866 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10870 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10874 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10878 &setup.internal.info_title, "info_title"
10882 &setup.internal.info_elements, "info_elements"
10886 &setup.internal.info_music, "info_music"
10890 &setup.internal.info_credits, "info_credits"
10894 &setup.internal.info_program, "info_program"
10898 &setup.internal.info_version, "info_version"
10902 &setup.internal.info_levelset, "info_levelset"
10906 &setup.internal.info_exit, "info_exit"
10910 static struct TokenInfo debug_setup_tokens[] =
10914 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10918 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10922 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10926 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10930 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10934 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10938 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10942 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10946 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10950 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10954 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10958 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10962 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10966 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10970 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10974 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10978 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10982 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10986 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10990 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10994 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10997 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11001 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11005 &setup.debug.xsn_mode, "debug.xsn_mode"
11009 &setup.debug.xsn_percent, "debug.xsn_percent"
11013 static struct TokenInfo options_setup_tokens[] =
11017 &setup.options.verbose, "options.verbose"
11021 &setup.options.debug, "options.debug"
11025 &setup.options.debug_mode, "options.debug_mode"
11029 static void setSetupInfoToDefaults(struct SetupInfo *si)
11033 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11035 si->multiple_users = TRUE;
11038 si->sound_loops = TRUE;
11039 si->sound_music = TRUE;
11040 si->sound_simple = TRUE;
11042 si->global_animations = TRUE;
11043 si->scroll_delay = TRUE;
11044 si->forced_scroll_delay = FALSE;
11045 si->scroll_delay_value = STD_SCROLL_DELAY;
11046 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11047 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11048 si->fade_screens = TRUE;
11049 si->autorecord = TRUE;
11050 si->autorecord_after_replay = TRUE;
11051 si->auto_pause_on_start = FALSE;
11052 si->show_titlescreen = TRUE;
11053 si->quick_doors = FALSE;
11054 si->team_mode = FALSE;
11055 si->handicap = TRUE;
11056 si->skip_levels = TRUE;
11057 si->increment_levels = TRUE;
11058 si->auto_play_next_level = TRUE;
11059 si->count_score_after_game = TRUE;
11060 si->show_scores_after_game = TRUE;
11061 si->time_limit = TRUE;
11062 si->fullscreen = FALSE;
11063 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11064 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11065 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11066 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11067 si->ask_on_escape = TRUE;
11068 si->ask_on_escape_editor = TRUE;
11069 si->ask_on_game_over = TRUE;
11070 si->ask_on_quit_game = TRUE;
11071 si->ask_on_quit_program = TRUE;
11072 si->quick_switch = FALSE;
11073 si->input_on_focus = FALSE;
11074 si->prefer_aga_graphics = TRUE;
11075 si->prefer_lowpass_sounds = FALSE;
11076 si->prefer_extra_panel_items = TRUE;
11077 si->game_speed_extended = FALSE;
11078 si->game_frame_delay = GAME_FRAME_DELAY;
11079 si->bd_skip_uncovering = FALSE;
11080 si->bd_skip_hatching = FALSE;
11081 si->bd_scroll_delay = TRUE;
11082 si->bd_smooth_movements = AUTO;
11083 si->sp_show_border_elements = FALSE;
11084 si->small_game_graphics = FALSE;
11085 si->show_load_save_buttons = FALSE;
11086 si->show_undo_redo_buttons = FALSE;
11087 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11089 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11090 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11091 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11093 si->override_level_graphics = FALSE;
11094 si->override_level_sounds = FALSE;
11095 si->override_level_music = FALSE;
11097 si->volume_simple = 100; // percent
11098 si->volume_loops = 100; // percent
11099 si->volume_music = 100; // percent
11101 si->network_mode = FALSE;
11102 si->network_player_nr = 0; // first player
11103 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11105 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11106 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11107 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11108 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11109 si->touch.draw_outlined = TRUE;
11110 si->touch.draw_pressed = TRUE;
11112 for (i = 0; i < 2; i++)
11114 char *default_grid_button[6][2] =
11120 { "111222", " vv " },
11121 { "111222", " vv " }
11123 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11124 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11125 int min_xsize = MIN(6, grid_xsize);
11126 int min_ysize = MIN(6, grid_ysize);
11127 int startx = grid_xsize - min_xsize;
11128 int starty = grid_ysize - min_ysize;
11131 // virtual buttons grid can only be set to defaults if video is initialized
11132 // (this will be repeated if virtual buttons are not loaded from setup file)
11133 if (video.initialized)
11135 si->touch.grid_xsize[i] = grid_xsize;
11136 si->touch.grid_ysize[i] = grid_ysize;
11140 si->touch.grid_xsize[i] = -1;
11141 si->touch.grid_ysize[i] = -1;
11144 for (x = 0; x < MAX_GRID_XSIZE; x++)
11145 for (y = 0; y < MAX_GRID_YSIZE; y++)
11146 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11148 for (x = 0; x < min_xsize; x++)
11149 for (y = 0; y < min_ysize; y++)
11150 si->touch.grid_button[i][x][starty + y] =
11151 default_grid_button[y][0][x];
11153 for (x = 0; x < min_xsize; x++)
11154 for (y = 0; y < min_ysize; y++)
11155 si->touch.grid_button[i][startx + x][starty + y] =
11156 default_grid_button[y][1][x];
11159 si->touch.grid_initialized = video.initialized;
11161 si->touch.overlay_buttons = FALSE;
11163 si->editor.el_boulderdash = TRUE;
11164 si->editor.el_boulderdash_native = TRUE;
11165 si->editor.el_emerald_mine = TRUE;
11166 si->editor.el_emerald_mine_club = TRUE;
11167 si->editor.el_more = TRUE;
11168 si->editor.el_sokoban = TRUE;
11169 si->editor.el_supaplex = TRUE;
11170 si->editor.el_diamond_caves = TRUE;
11171 si->editor.el_dx_boulderdash = TRUE;
11173 si->editor.el_mirror_magic = TRUE;
11174 si->editor.el_deflektor = TRUE;
11176 si->editor.el_chars = TRUE;
11177 si->editor.el_steel_chars = TRUE;
11179 si->editor.el_classic = TRUE;
11180 si->editor.el_custom = TRUE;
11182 si->editor.el_user_defined = FALSE;
11183 si->editor.el_dynamic = TRUE;
11185 si->editor.el_headlines = TRUE;
11187 si->editor.show_element_token = FALSE;
11189 si->editor.show_read_only_warning = TRUE;
11191 si->editor.use_template_for_new_levels = TRUE;
11193 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11194 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11195 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11196 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11197 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11199 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11200 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11201 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11202 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11203 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11205 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11206 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11207 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11208 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11209 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11210 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11212 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11213 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11214 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11216 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11217 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11218 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11219 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11221 for (i = 0; i < MAX_PLAYERS; i++)
11223 si->input[i].use_joystick = FALSE;
11224 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11225 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11226 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11227 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11228 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11229 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11230 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11231 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11232 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11233 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11234 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11235 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11236 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11237 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11238 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11241 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11242 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11243 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11244 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11246 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11247 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11248 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11249 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11250 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11251 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11252 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11254 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11256 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11257 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11258 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11260 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11261 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11262 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11264 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11265 si->internal.choose_from_top_leveldir = FALSE;
11266 si->internal.show_scaling_in_title = TRUE;
11267 si->internal.create_user_levelset = TRUE;
11268 si->internal.info_screens_from_main = FALSE;
11270 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11271 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11273 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11274 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11275 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11276 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11277 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11278 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11279 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11280 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11281 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11282 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11284 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11285 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11286 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11287 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11288 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11289 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11290 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11291 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11292 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11293 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11295 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11296 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11298 si->debug.show_frames_per_second = FALSE;
11300 si->debug.xsn_mode = AUTO;
11301 si->debug.xsn_percent = 0;
11303 si->options.verbose = FALSE;
11304 si->options.debug = FALSE;
11305 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11307 #if defined(PLATFORM_ANDROID)
11308 si->fullscreen = TRUE;
11309 si->touch.overlay_buttons = TRUE;
11312 setHideSetupEntry(&setup.debug.xsn_mode);
11315 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11317 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11320 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11322 si->player_uuid = NULL; // (will be set later)
11323 si->player_version = 1; // (will be set later)
11325 si->use_api_server = TRUE;
11326 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11327 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11328 si->ask_for_uploading_tapes = TRUE;
11329 si->ask_for_remaining_tapes = FALSE;
11330 si->provide_uploading_tapes = TRUE;
11331 si->ask_for_using_api_server = TRUE;
11332 si->has_remaining_tapes = FALSE;
11335 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11337 si->editor_cascade.el_bd = TRUE;
11338 si->editor_cascade.el_bd_native = TRUE;
11339 si->editor_cascade.el_em = TRUE;
11340 si->editor_cascade.el_emc = TRUE;
11341 si->editor_cascade.el_rnd = TRUE;
11342 si->editor_cascade.el_sb = TRUE;
11343 si->editor_cascade.el_sp = TRUE;
11344 si->editor_cascade.el_dc = TRUE;
11345 si->editor_cascade.el_dx = TRUE;
11347 si->editor_cascade.el_mm = TRUE;
11348 si->editor_cascade.el_df = TRUE;
11350 si->editor_cascade.el_chars = FALSE;
11351 si->editor_cascade.el_steel_chars = FALSE;
11352 si->editor_cascade.el_ce = FALSE;
11353 si->editor_cascade.el_ge = FALSE;
11354 si->editor_cascade.el_es = FALSE;
11355 si->editor_cascade.el_ref = FALSE;
11356 si->editor_cascade.el_user = FALSE;
11357 si->editor_cascade.el_dynamic = FALSE;
11360 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11362 static char *getHideSetupToken(void *setup_value)
11364 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11366 if (setup_value != NULL)
11367 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11369 return hide_setup_token;
11372 void setHideSetupEntry(void *setup_value)
11374 char *hide_setup_token = getHideSetupToken(setup_value);
11376 if (hide_setup_hash == NULL)
11377 hide_setup_hash = newSetupFileHash();
11379 if (setup_value != NULL)
11380 setHashEntry(hide_setup_hash, hide_setup_token, "");
11383 void removeHideSetupEntry(void *setup_value)
11385 char *hide_setup_token = getHideSetupToken(setup_value);
11387 if (setup_value != NULL)
11388 removeHashEntry(hide_setup_hash, hide_setup_token);
11391 boolean hideSetupEntry(void *setup_value)
11393 char *hide_setup_token = getHideSetupToken(setup_value);
11395 return (setup_value != NULL &&
11396 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11399 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11400 struct TokenInfo *token_info,
11401 int token_nr, char *token_text)
11403 char *token_hide_text = getStringCat2(token_text, ".hide");
11404 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11406 // set the value of this setup option in the setup option structure
11407 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11409 // check if this setup option should be hidden in the setup menu
11410 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11411 setHideSetupEntry(token_info[token_nr].value);
11413 free(token_hide_text);
11416 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11417 struct TokenInfo *token_info,
11420 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11421 token_info[token_nr].text);
11424 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11428 if (!setup_file_hash)
11431 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11432 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11434 setup.touch.grid_initialized = TRUE;
11435 for (i = 0; i < 2; i++)
11437 int grid_xsize = setup.touch.grid_xsize[i];
11438 int grid_ysize = setup.touch.grid_ysize[i];
11441 // if virtual buttons are not loaded from setup file, repeat initializing
11442 // virtual buttons grid with default values later when video is initialized
11443 if (grid_xsize == -1 ||
11446 setup.touch.grid_initialized = FALSE;
11451 for (y = 0; y < grid_ysize; y++)
11453 char token_string[MAX_LINE_LEN];
11455 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11457 char *value_string = getHashEntry(setup_file_hash, token_string);
11459 if (value_string == NULL)
11462 for (x = 0; x < grid_xsize; x++)
11464 char c = value_string[x];
11466 setup.touch.grid_button[i][x][y] =
11467 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11472 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11473 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11475 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11476 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11478 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11482 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11484 setup_input = setup.input[pnr];
11485 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11487 char full_token[100];
11489 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11490 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11493 setup.input[pnr] = setup_input;
11496 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11497 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11499 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11500 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11502 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11503 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11505 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11506 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11508 setHideRelatedSetupEntries();
11511 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11515 if (!setup_file_hash)
11518 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11519 setSetupInfo(auto_setup_tokens, i,
11520 getHashEntry(setup_file_hash,
11521 auto_setup_tokens[i].text));
11524 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11528 if (!setup_file_hash)
11531 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11532 setSetupInfo(server_setup_tokens, i,
11533 getHashEntry(setup_file_hash,
11534 server_setup_tokens[i].text));
11537 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11541 if (!setup_file_hash)
11544 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11545 setSetupInfo(editor_cascade_setup_tokens, i,
11546 getHashEntry(setup_file_hash,
11547 editor_cascade_setup_tokens[i].text));
11550 void LoadUserNames(void)
11552 int last_user_nr = user.nr;
11555 if (global.user_names != NULL)
11557 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11558 checked_free(global.user_names[i]);
11560 checked_free(global.user_names);
11563 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11565 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11569 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11571 if (setup_file_hash)
11573 char *player_name = getHashEntry(setup_file_hash, "player_name");
11575 global.user_names[i] = getFixedUserName(player_name);
11577 freeSetupFileHash(setup_file_hash);
11580 if (global.user_names[i] == NULL)
11581 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11584 user.nr = last_user_nr;
11587 void LoadSetupFromFilename(char *filename)
11589 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11591 if (setup_file_hash)
11593 decodeSetupFileHash_Default(setup_file_hash);
11595 freeSetupFileHash(setup_file_hash);
11599 Debug("setup", "using default setup values");
11603 static void LoadSetup_SpecialPostProcessing(void)
11605 char *player_name_new;
11607 // needed to work around problems with fixed length strings
11608 player_name_new = getFixedUserName(setup.player_name);
11609 free(setup.player_name);
11610 setup.player_name = player_name_new;
11612 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11613 if (setup.scroll_delay == FALSE)
11615 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11616 setup.scroll_delay = TRUE; // now always "on"
11619 // make sure that scroll delay value stays inside valid range
11620 setup.scroll_delay_value =
11621 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11624 void LoadSetup_Default(void)
11628 // always start with reliable default values
11629 setSetupInfoToDefaults(&setup);
11631 // try to load setup values from default setup file
11632 filename = getDefaultSetupFilename();
11634 if (fileExists(filename))
11635 LoadSetupFromFilename(filename);
11637 // try to load setup values from platform setup file
11638 filename = getPlatformSetupFilename();
11640 if (fileExists(filename))
11641 LoadSetupFromFilename(filename);
11643 // try to load setup values from user setup file
11644 filename = getSetupFilename();
11646 LoadSetupFromFilename(filename);
11648 LoadSetup_SpecialPostProcessing();
11651 void LoadSetup_AutoSetup(void)
11653 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11654 SetupFileHash *setup_file_hash = NULL;
11656 // always start with reliable default values
11657 setSetupInfoToDefaults_AutoSetup(&setup);
11659 setup_file_hash = loadSetupFileHash(filename);
11661 if (setup_file_hash)
11663 decodeSetupFileHash_AutoSetup(setup_file_hash);
11665 freeSetupFileHash(setup_file_hash);
11671 void LoadSetup_ServerSetup(void)
11673 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11674 SetupFileHash *setup_file_hash = NULL;
11676 // always start with reliable default values
11677 setSetupInfoToDefaults_ServerSetup(&setup);
11679 setup_file_hash = loadSetupFileHash(filename);
11681 if (setup_file_hash)
11683 decodeSetupFileHash_ServerSetup(setup_file_hash);
11685 freeSetupFileHash(setup_file_hash);
11690 if (setup.player_uuid == NULL)
11692 // player UUID does not yet exist in setup file
11693 setup.player_uuid = getStringCopy(getUUID());
11694 setup.player_version = 2;
11696 SaveSetup_ServerSetup();
11700 void LoadSetup_EditorCascade(void)
11702 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11703 SetupFileHash *setup_file_hash = NULL;
11705 // always start with reliable default values
11706 setSetupInfoToDefaults_EditorCascade(&setup);
11708 setup_file_hash = loadSetupFileHash(filename);
11710 if (setup_file_hash)
11712 decodeSetupFileHash_EditorCascade(setup_file_hash);
11714 freeSetupFileHash(setup_file_hash);
11720 void LoadSetup(void)
11722 LoadSetup_Default();
11723 LoadSetup_AutoSetup();
11724 LoadSetup_ServerSetup();
11725 LoadSetup_EditorCascade();
11728 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11729 char *mapping_line)
11731 char mapping_guid[MAX_LINE_LEN];
11732 char *mapping_start, *mapping_end;
11734 // get GUID from game controller mapping line: copy complete line
11735 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11736 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11738 // get GUID from game controller mapping line: cut after GUID part
11739 mapping_start = strchr(mapping_guid, ',');
11740 if (mapping_start != NULL)
11741 *mapping_start = '\0';
11743 // cut newline from game controller mapping line
11744 mapping_end = strchr(mapping_line, '\n');
11745 if (mapping_end != NULL)
11746 *mapping_end = '\0';
11748 // add mapping entry to game controller mappings hash
11749 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11752 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11757 if (!(file = fopen(filename, MODE_READ)))
11759 Warn("cannot read game controller mappings file '%s'", filename);
11764 while (!feof(file))
11766 char line[MAX_LINE_LEN];
11768 if (!fgets(line, MAX_LINE_LEN, file))
11771 addGameControllerMappingToHash(mappings_hash, line);
11777 void SaveSetup_Default(void)
11779 char *filename = getSetupFilename();
11783 InitUserDataDirectory();
11785 if (!(file = fopen(filename, MODE_WRITE)))
11787 Warn("cannot write setup file '%s'", filename);
11792 fprintFileHeader(file, SETUP_FILENAME);
11794 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11796 // just to make things nicer :)
11797 if (global_setup_tokens[i].value == &setup.multiple_users ||
11798 global_setup_tokens[i].value == &setup.sound ||
11799 global_setup_tokens[i].value == &setup.graphics_set ||
11800 global_setup_tokens[i].value == &setup.volume_simple ||
11801 global_setup_tokens[i].value == &setup.network_mode ||
11802 global_setup_tokens[i].value == &setup.touch.control_type ||
11803 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11804 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11805 fprintf(file, "\n");
11807 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11810 for (i = 0; i < 2; i++)
11812 int grid_xsize = setup.touch.grid_xsize[i];
11813 int grid_ysize = setup.touch.grid_ysize[i];
11816 fprintf(file, "\n");
11818 for (y = 0; y < grid_ysize; y++)
11820 char token_string[MAX_LINE_LEN];
11821 char value_string[MAX_LINE_LEN];
11823 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11825 for (x = 0; x < grid_xsize; x++)
11827 char c = setup.touch.grid_button[i][x][y];
11829 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11832 value_string[grid_xsize] = '\0';
11834 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11838 fprintf(file, "\n");
11839 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11840 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11842 fprintf(file, "\n");
11843 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11844 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11846 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11850 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11851 fprintf(file, "\n");
11853 setup_input = setup.input[pnr];
11854 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11855 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11858 fprintf(file, "\n");
11859 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11860 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11862 // (internal setup values not saved to user setup file)
11864 fprintf(file, "\n");
11865 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11866 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11867 setup.debug.xsn_mode != AUTO)
11868 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11870 fprintf(file, "\n");
11871 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11872 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11876 SetFilePermissions(filename, PERMS_PRIVATE);
11879 void SaveSetup_AutoSetup(void)
11881 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11885 InitUserDataDirectory();
11887 if (!(file = fopen(filename, MODE_WRITE)))
11889 Warn("cannot write auto setup file '%s'", filename);
11896 fprintFileHeader(file, AUTOSETUP_FILENAME);
11898 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11899 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11903 SetFilePermissions(filename, PERMS_PRIVATE);
11908 void SaveSetup_ServerSetup(void)
11910 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11914 InitUserDataDirectory();
11916 if (!(file = fopen(filename, MODE_WRITE)))
11918 Warn("cannot write server setup file '%s'", filename);
11925 fprintFileHeader(file, SERVERSETUP_FILENAME);
11927 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11929 // just to make things nicer :)
11930 if (server_setup_tokens[i].value == &setup.use_api_server)
11931 fprintf(file, "\n");
11933 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11938 SetFilePermissions(filename, PERMS_PRIVATE);
11943 void SaveSetup_EditorCascade(void)
11945 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11949 InitUserDataDirectory();
11951 if (!(file = fopen(filename, MODE_WRITE)))
11953 Warn("cannot write editor cascade state file '%s'", filename);
11960 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11962 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11963 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11967 SetFilePermissions(filename, PERMS_PRIVATE);
11972 void SaveSetup(void)
11974 SaveSetup_Default();
11975 SaveSetup_AutoSetup();
11976 SaveSetup_ServerSetup();
11977 SaveSetup_EditorCascade();
11980 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11985 if (!(file = fopen(filename, MODE_WRITE)))
11987 Warn("cannot write game controller mappings file '%s'", filename);
11992 BEGIN_HASH_ITERATION(mappings_hash, itr)
11994 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11996 END_HASH_ITERATION(mappings_hash, itr)
12001 void SaveSetup_AddGameControllerMapping(char *mapping)
12003 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12004 SetupFileHash *mappings_hash = newSetupFileHash();
12006 InitUserDataDirectory();
12008 // load existing personal game controller mappings
12009 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12011 // add new mapping to personal game controller mappings
12012 addGameControllerMappingToHash(mappings_hash, mapping);
12014 // save updated personal game controller mappings
12015 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12017 freeSetupFileHash(mappings_hash);
12021 void LoadCustomElementDescriptions(void)
12023 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12024 SetupFileHash *setup_file_hash;
12027 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12029 if (element_info[i].custom_description != NULL)
12031 free(element_info[i].custom_description);
12032 element_info[i].custom_description = NULL;
12036 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12039 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12041 char *token = getStringCat2(element_info[i].token_name, ".name");
12042 char *value = getHashEntry(setup_file_hash, token);
12045 element_info[i].custom_description = getStringCopy(value);
12050 freeSetupFileHash(setup_file_hash);
12053 static int getElementFromToken(char *token)
12055 char *value = getHashEntry(element_token_hash, token);
12058 return atoi(value);
12060 Warn("unknown element token '%s'", token);
12062 return EL_UNDEFINED;
12065 void FreeGlobalAnimEventInfo(void)
12067 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12069 if (gaei->event_list == NULL)
12074 for (i = 0; i < gaei->num_event_lists; i++)
12076 checked_free(gaei->event_list[i]->event_value);
12077 checked_free(gaei->event_list[i]);
12080 checked_free(gaei->event_list);
12082 gaei->event_list = NULL;
12083 gaei->num_event_lists = 0;
12086 static int AddGlobalAnimEventList(void)
12088 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12089 int list_pos = gaei->num_event_lists++;
12091 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12092 sizeof(struct GlobalAnimEventListInfo *));
12094 gaei->event_list[list_pos] =
12095 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12097 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12099 gaeli->event_value = NULL;
12100 gaeli->num_event_values = 0;
12105 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12107 // do not add empty global animation events
12108 if (event_value == ANIM_EVENT_NONE)
12111 // if list position is undefined, create new list
12112 if (list_pos == ANIM_EVENT_UNDEFINED)
12113 list_pos = AddGlobalAnimEventList();
12115 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12116 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12117 int value_pos = gaeli->num_event_values++;
12119 gaeli->event_value = checked_realloc(gaeli->event_value,
12120 gaeli->num_event_values * sizeof(int *));
12122 gaeli->event_value[value_pos] = event_value;
12127 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12129 if (list_pos == ANIM_EVENT_UNDEFINED)
12130 return ANIM_EVENT_NONE;
12132 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12133 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12135 return gaeli->event_value[value_pos];
12138 int GetGlobalAnimEventValueCount(int list_pos)
12140 if (list_pos == ANIM_EVENT_UNDEFINED)
12143 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12144 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12146 return gaeli->num_event_values;
12149 // This function checks if a string <s> of the format "string1, string2, ..."
12150 // exactly contains a string <s_contained>.
12152 static boolean string_has_parameter(char *s, char *s_contained)
12156 if (s == NULL || s_contained == NULL)
12159 if (strlen(s_contained) > strlen(s))
12162 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12164 char next_char = s[strlen(s_contained)];
12166 // check if next character is delimiter or whitespace
12167 if (next_char == ',' || next_char == '\0' ||
12168 next_char == ' ' || next_char == '\t')
12172 // check if string contains another parameter string after a comma
12173 substring = strchr(s, ',');
12174 if (substring == NULL) // string does not contain a comma
12177 // advance string pointer to next character after the comma
12180 // skip potential whitespaces after the comma
12181 while (*substring == ' ' || *substring == '\t')
12184 return string_has_parameter(substring, s_contained);
12187 static int get_anim_parameter_value_ce(char *s)
12190 char *pattern_1 = "ce_change:custom_";
12191 char *pattern_2 = ".page_";
12192 int pattern_1_len = strlen(pattern_1);
12193 char *matching_char = strstr(s_ptr, pattern_1);
12194 int result = ANIM_EVENT_NONE;
12196 if (matching_char == NULL)
12197 return ANIM_EVENT_NONE;
12199 result = ANIM_EVENT_CE_CHANGE;
12201 s_ptr = matching_char + pattern_1_len;
12203 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12204 if (*s_ptr >= '0' && *s_ptr <= '9')
12206 int gic_ce_nr = (*s_ptr++ - '0');
12208 if (*s_ptr >= '0' && *s_ptr <= '9')
12210 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12212 if (*s_ptr >= '0' && *s_ptr <= '9')
12213 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12216 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12217 return ANIM_EVENT_NONE;
12219 // custom element stored as 0 to 255
12222 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12226 // invalid custom element number specified
12228 return ANIM_EVENT_NONE;
12231 // check for change page number ("page_X" or "page_XX") (optional)
12232 if (strPrefix(s_ptr, pattern_2))
12234 s_ptr += strlen(pattern_2);
12236 if (*s_ptr >= '0' && *s_ptr <= '9')
12238 int gic_page_nr = (*s_ptr++ - '0');
12240 if (*s_ptr >= '0' && *s_ptr <= '9')
12241 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12243 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12244 return ANIM_EVENT_NONE;
12246 // change page stored as 1 to 32 (0 means "all change pages")
12248 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12252 // invalid animation part number specified
12254 return ANIM_EVENT_NONE;
12258 // discard result if next character is neither delimiter nor whitespace
12259 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12260 *s_ptr == ' ' || *s_ptr == '\t'))
12261 return ANIM_EVENT_NONE;
12266 static int get_anim_parameter_value(char *s)
12268 int event_value[] =
12276 char *pattern_1[] =
12284 char *pattern_2 = ".part_";
12285 char *matching_char = NULL;
12287 int pattern_1_len = 0;
12288 int result = ANIM_EVENT_NONE;
12291 result = get_anim_parameter_value_ce(s);
12293 if (result != ANIM_EVENT_NONE)
12296 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12298 matching_char = strstr(s_ptr, pattern_1[i]);
12299 pattern_1_len = strlen(pattern_1[i]);
12300 result = event_value[i];
12302 if (matching_char != NULL)
12306 if (matching_char == NULL)
12307 return ANIM_EVENT_NONE;
12309 s_ptr = matching_char + pattern_1_len;
12311 // check for main animation number ("anim_X" or "anim_XX")
12312 if (*s_ptr >= '0' && *s_ptr <= '9')
12314 int gic_anim_nr = (*s_ptr++ - '0');
12316 if (*s_ptr >= '0' && *s_ptr <= '9')
12317 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12319 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12320 return ANIM_EVENT_NONE;
12322 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12326 // invalid main animation number specified
12328 return ANIM_EVENT_NONE;
12331 // check for animation part number ("part_X" or "part_XX") (optional)
12332 if (strPrefix(s_ptr, pattern_2))
12334 s_ptr += strlen(pattern_2);
12336 if (*s_ptr >= '0' && *s_ptr <= '9')
12338 int gic_part_nr = (*s_ptr++ - '0');
12340 if (*s_ptr >= '0' && *s_ptr <= '9')
12341 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12343 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12344 return ANIM_EVENT_NONE;
12346 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12350 // invalid animation part number specified
12352 return ANIM_EVENT_NONE;
12356 // discard result if next character is neither delimiter nor whitespace
12357 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12358 *s_ptr == ' ' || *s_ptr == '\t'))
12359 return ANIM_EVENT_NONE;
12364 static int get_anim_parameter_values(char *s)
12366 int list_pos = ANIM_EVENT_UNDEFINED;
12367 int event_value = ANIM_EVENT_DEFAULT;
12369 if (string_has_parameter(s, "any"))
12370 event_value |= ANIM_EVENT_ANY;
12372 if (string_has_parameter(s, "click:self") ||
12373 string_has_parameter(s, "click") ||
12374 string_has_parameter(s, "self"))
12375 event_value |= ANIM_EVENT_SELF;
12377 if (string_has_parameter(s, "unclick:any"))
12378 event_value |= ANIM_EVENT_UNCLICK_ANY;
12380 // if animation event found, add it to global animation event list
12381 if (event_value != ANIM_EVENT_NONE)
12382 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12386 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12387 event_value = get_anim_parameter_value(s);
12389 // if animation event found, add it to global animation event list
12390 if (event_value != ANIM_EVENT_NONE)
12391 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12393 // continue with next part of the string, starting with next comma
12394 s = strchr(s + 1, ',');
12400 static int get_anim_action_parameter_value(char *token)
12402 // check most common default case first to massively speed things up
12403 if (strEqual(token, ARG_UNDEFINED))
12404 return ANIM_EVENT_ACTION_NONE;
12406 int result = getImageIDFromToken(token);
12410 char *gfx_token = getStringCat2("gfx.", token);
12412 result = getImageIDFromToken(gfx_token);
12414 checked_free(gfx_token);
12419 Key key = getKeyFromX11KeyName(token);
12421 if (key != KSYM_UNDEFINED)
12422 result = -(int)key;
12429 result = get_hash_from_string(token); // unsigned int => int
12430 result = ABS(result); // may be negative now
12431 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12433 setHashEntry(anim_url_hash, int2str(result, 0), token);
12438 result = ANIM_EVENT_ACTION_NONE;
12443 int get_parameter_value(char *value_raw, char *suffix, int type)
12445 char *value = getStringToLower(value_raw);
12446 int result = 0; // probably a save default value
12448 if (strEqual(suffix, ".direction"))
12450 result = (strEqual(value, "left") ? MV_LEFT :
12451 strEqual(value, "right") ? MV_RIGHT :
12452 strEqual(value, "up") ? MV_UP :
12453 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12455 else if (strEqual(suffix, ".position"))
12457 result = (strEqual(value, "left") ? POS_LEFT :
12458 strEqual(value, "right") ? POS_RIGHT :
12459 strEqual(value, "top") ? POS_TOP :
12460 strEqual(value, "upper") ? POS_UPPER :
12461 strEqual(value, "middle") ? POS_MIDDLE :
12462 strEqual(value, "lower") ? POS_LOWER :
12463 strEqual(value, "bottom") ? POS_BOTTOM :
12464 strEqual(value, "any") ? POS_ANY :
12465 strEqual(value, "ce") ? POS_CE :
12466 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12467 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12469 else if (strEqual(suffix, ".align"))
12471 result = (strEqual(value, "left") ? ALIGN_LEFT :
12472 strEqual(value, "right") ? ALIGN_RIGHT :
12473 strEqual(value, "center") ? ALIGN_CENTER :
12474 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12476 else if (strEqual(suffix, ".valign"))
12478 result = (strEqual(value, "top") ? VALIGN_TOP :
12479 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12480 strEqual(value, "middle") ? VALIGN_MIDDLE :
12481 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12483 else if (strEqual(suffix, ".anim_mode"))
12485 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12486 string_has_parameter(value, "loop") ? ANIM_LOOP :
12487 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12488 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12489 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12490 string_has_parameter(value, "random") ? ANIM_RANDOM :
12491 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12492 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12493 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12494 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12495 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12496 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12497 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12498 string_has_parameter(value, "all") ? ANIM_ALL :
12499 string_has_parameter(value, "tiled") ? ANIM_TILED :
12500 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12503 if (string_has_parameter(value, "once"))
12504 result |= ANIM_ONCE;
12506 if (string_has_parameter(value, "reverse"))
12507 result |= ANIM_REVERSE;
12509 if (string_has_parameter(value, "opaque_player"))
12510 result |= ANIM_OPAQUE_PLAYER;
12512 if (string_has_parameter(value, "static_panel"))
12513 result |= ANIM_STATIC_PANEL;
12515 else if (strEqual(suffix, ".init_event") ||
12516 strEqual(suffix, ".anim_event"))
12518 result = get_anim_parameter_values(value);
12520 else if (strEqual(suffix, ".init_delay_action") ||
12521 strEqual(suffix, ".anim_delay_action") ||
12522 strEqual(suffix, ".post_delay_action") ||
12523 strEqual(suffix, ".init_event_action") ||
12524 strEqual(suffix, ".anim_event_action"))
12526 result = get_anim_action_parameter_value(value_raw);
12528 else if (strEqual(suffix, ".class"))
12530 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12531 get_hash_from_string(value));
12533 else if (strEqual(suffix, ".style"))
12535 result = STYLE_DEFAULT;
12537 if (string_has_parameter(value, "accurate_borders"))
12538 result |= STYLE_ACCURATE_BORDERS;
12540 if (string_has_parameter(value, "inner_corners"))
12541 result |= STYLE_INNER_CORNERS;
12543 if (string_has_parameter(value, "reverse"))
12544 result |= STYLE_REVERSE;
12546 if (string_has_parameter(value, "leftmost_position"))
12547 result |= STYLE_LEFTMOST_POSITION;
12549 if (string_has_parameter(value, "block_clicks"))
12550 result |= STYLE_BLOCK;
12552 if (string_has_parameter(value, "passthrough_clicks"))
12553 result |= STYLE_PASSTHROUGH;
12555 if (string_has_parameter(value, "multiple_actions"))
12556 result |= STYLE_MULTIPLE_ACTIONS;
12558 if (string_has_parameter(value, "consume_ce_event"))
12559 result |= STYLE_CONSUME_CE_EVENT;
12561 else if (strEqual(suffix, ".fade_mode"))
12563 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12564 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12565 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12566 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12567 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12568 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12569 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12570 FADE_MODE_DEFAULT);
12572 else if (strEqual(suffix, ".auto_delay_unit"))
12574 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12575 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12576 AUTO_DELAY_UNIT_DEFAULT);
12578 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12580 result = gfx.get_font_from_token_function(value);
12582 else // generic parameter of type integer or boolean
12584 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12585 type == TYPE_INTEGER ? get_integer_from_string(value) :
12586 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12587 ARG_UNDEFINED_VALUE);
12595 static int get_token_parameter_value(char *token, char *value_raw)
12599 if (token == NULL || value_raw == NULL)
12600 return ARG_UNDEFINED_VALUE;
12602 suffix = strrchr(token, '.');
12603 if (suffix == NULL)
12606 if (strEqual(suffix, ".element"))
12607 return getElementFromToken(value_raw);
12609 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12610 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12613 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12614 boolean ignore_defaults)
12618 for (i = 0; image_config_vars[i].token != NULL; i++)
12620 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12622 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12623 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12627 *image_config_vars[i].value =
12628 get_token_parameter_value(image_config_vars[i].token, value);
12632 void InitMenuDesignSettings_Static(void)
12634 // always start with reliable default values from static default config
12635 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12638 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12642 // the following initializes hierarchical values from static configuration
12644 // special case: initialize "ARG_DEFAULT" values in static default config
12645 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12646 titlescreen_initial_first_default.fade_mode =
12647 title_initial_first_default.fade_mode;
12648 titlescreen_initial_first_default.fade_delay =
12649 title_initial_first_default.fade_delay;
12650 titlescreen_initial_first_default.post_delay =
12651 title_initial_first_default.post_delay;
12652 titlescreen_initial_first_default.auto_delay =
12653 title_initial_first_default.auto_delay;
12654 titlescreen_initial_first_default.auto_delay_unit =
12655 title_initial_first_default.auto_delay_unit;
12656 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12657 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12658 titlescreen_first_default.post_delay = title_first_default.post_delay;
12659 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12660 titlescreen_first_default.auto_delay_unit =
12661 title_first_default.auto_delay_unit;
12662 titlemessage_initial_first_default.fade_mode =
12663 title_initial_first_default.fade_mode;
12664 titlemessage_initial_first_default.fade_delay =
12665 title_initial_first_default.fade_delay;
12666 titlemessage_initial_first_default.post_delay =
12667 title_initial_first_default.post_delay;
12668 titlemessage_initial_first_default.auto_delay =
12669 title_initial_first_default.auto_delay;
12670 titlemessage_initial_first_default.auto_delay_unit =
12671 title_initial_first_default.auto_delay_unit;
12672 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12673 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12674 titlemessage_first_default.post_delay = title_first_default.post_delay;
12675 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12676 titlemessage_first_default.auto_delay_unit =
12677 title_first_default.auto_delay_unit;
12679 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12680 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12681 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12682 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12683 titlescreen_initial_default.auto_delay_unit =
12684 title_initial_default.auto_delay_unit;
12685 titlescreen_default.fade_mode = title_default.fade_mode;
12686 titlescreen_default.fade_delay = title_default.fade_delay;
12687 titlescreen_default.post_delay = title_default.post_delay;
12688 titlescreen_default.auto_delay = title_default.auto_delay;
12689 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12690 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12691 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12692 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12693 titlemessage_initial_default.auto_delay_unit =
12694 title_initial_default.auto_delay_unit;
12695 titlemessage_default.fade_mode = title_default.fade_mode;
12696 titlemessage_default.fade_delay = title_default.fade_delay;
12697 titlemessage_default.post_delay = title_default.post_delay;
12698 titlemessage_default.auto_delay = title_default.auto_delay;
12699 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12701 // special case: initialize "ARG_DEFAULT" values in static default config
12702 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12703 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12705 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12706 titlescreen_first[i] = titlescreen_first_default;
12707 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12708 titlemessage_first[i] = titlemessage_first_default;
12710 titlescreen_initial[i] = titlescreen_initial_default;
12711 titlescreen[i] = titlescreen_default;
12712 titlemessage_initial[i] = titlemessage_initial_default;
12713 titlemessage[i] = titlemessage_default;
12716 // special case: initialize "ARG_DEFAULT" values in static default config
12717 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12718 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12720 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12723 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12724 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12725 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12728 // special case: initialize "ARG_DEFAULT" values in static default config
12729 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12730 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12732 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12733 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12734 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12736 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12739 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12743 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12747 struct XY *dst, *src;
12749 game_buttons_xy[] =
12751 { &game.button.save, &game.button.stop },
12752 { &game.button.pause2, &game.button.pause },
12753 { &game.button.load, &game.button.play },
12754 { &game.button.undo, &game.button.stop },
12755 { &game.button.redo, &game.button.play },
12761 // special case: initialize later added SETUP list size from LEVELS value
12762 if (menu.list_size[GAME_MODE_SETUP] == -1)
12763 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12765 // set default position for snapshot buttons to stop/pause/play buttons
12766 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12767 if ((*game_buttons_xy[i].dst).x == -1 &&
12768 (*game_buttons_xy[i].dst).y == -1)
12769 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12771 // --------------------------------------------------------------------------
12772 // dynamic viewports (including playfield margins, borders and alignments)
12773 // --------------------------------------------------------------------------
12775 // dynamic viewports currently only supported for landscape mode
12776 int display_width = MAX(video.display_width, video.display_height);
12777 int display_height = MIN(video.display_width, video.display_height);
12779 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12781 struct RectWithBorder *vp_window = &viewport.window[i];
12782 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12783 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12784 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12785 boolean dynamic_window_width = (vp_window->min_width != -1);
12786 boolean dynamic_window_height = (vp_window->min_height != -1);
12787 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12788 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12790 // adjust window size if min/max width/height is specified
12792 if (vp_window->min_width != -1)
12794 int window_width = display_width;
12796 // when using static window height, use aspect ratio of display
12797 if (vp_window->min_height == -1)
12798 window_width = vp_window->height * display_width / display_height;
12800 vp_window->width = MAX(vp_window->min_width, window_width);
12803 if (vp_window->min_height != -1)
12805 int window_height = display_height;
12807 // when using static window width, use aspect ratio of display
12808 if (vp_window->min_width == -1)
12809 window_height = vp_window->width * display_height / display_width;
12811 vp_window->height = MAX(vp_window->min_height, window_height);
12814 if (vp_window->max_width != -1)
12815 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12817 if (vp_window->max_height != -1)
12818 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12820 int playfield_width = vp_window->width;
12821 int playfield_height = vp_window->height;
12823 // adjust playfield size and position according to specified margins
12825 playfield_width -= vp_playfield->margin_left;
12826 playfield_width -= vp_playfield->margin_right;
12828 playfield_height -= vp_playfield->margin_top;
12829 playfield_height -= vp_playfield->margin_bottom;
12831 // adjust playfield size if min/max width/height is specified
12833 if (vp_playfield->min_width != -1)
12834 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12836 if (vp_playfield->min_height != -1)
12837 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12839 if (vp_playfield->max_width != -1)
12840 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12842 if (vp_playfield->max_height != -1)
12843 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12845 // adjust playfield position according to specified alignment
12847 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12848 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12849 else if (vp_playfield->align == ALIGN_CENTER)
12850 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12851 else if (vp_playfield->align == ALIGN_RIGHT)
12852 vp_playfield->x += playfield_width - vp_playfield->width;
12854 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12855 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12856 else if (vp_playfield->valign == VALIGN_MIDDLE)
12857 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12858 else if (vp_playfield->valign == VALIGN_BOTTOM)
12859 vp_playfield->y += playfield_height - vp_playfield->height;
12861 vp_playfield->x += vp_playfield->margin_left;
12862 vp_playfield->y += vp_playfield->margin_top;
12864 // adjust individual playfield borders if only default border is specified
12866 if (vp_playfield->border_left == -1)
12867 vp_playfield->border_left = vp_playfield->border_size;
12868 if (vp_playfield->border_right == -1)
12869 vp_playfield->border_right = vp_playfield->border_size;
12870 if (vp_playfield->border_top == -1)
12871 vp_playfield->border_top = vp_playfield->border_size;
12872 if (vp_playfield->border_bottom == -1)
12873 vp_playfield->border_bottom = vp_playfield->border_size;
12875 // set dynamic playfield borders if borders are specified as undefined
12876 // (but only if window size was dynamic and playfield size was static)
12878 if (dynamic_window_width && !dynamic_playfield_width)
12880 if (vp_playfield->border_left == -1)
12882 vp_playfield->border_left = (vp_playfield->x -
12883 vp_playfield->margin_left);
12884 vp_playfield->x -= vp_playfield->border_left;
12885 vp_playfield->width += vp_playfield->border_left;
12888 if (vp_playfield->border_right == -1)
12890 vp_playfield->border_right = (vp_window->width -
12892 vp_playfield->width -
12893 vp_playfield->margin_right);
12894 vp_playfield->width += vp_playfield->border_right;
12898 if (dynamic_window_height && !dynamic_playfield_height)
12900 if (vp_playfield->border_top == -1)
12902 vp_playfield->border_top = (vp_playfield->y -
12903 vp_playfield->margin_top);
12904 vp_playfield->y -= vp_playfield->border_top;
12905 vp_playfield->height += vp_playfield->border_top;
12908 if (vp_playfield->border_bottom == -1)
12910 vp_playfield->border_bottom = (vp_window->height -
12912 vp_playfield->height -
12913 vp_playfield->margin_bottom);
12914 vp_playfield->height += vp_playfield->border_bottom;
12918 // adjust playfield size to be a multiple of a defined alignment tile size
12920 int align_size = vp_playfield->align_size;
12921 int playfield_xtiles = vp_playfield->width / align_size;
12922 int playfield_ytiles = vp_playfield->height / align_size;
12923 int playfield_width_corrected = playfield_xtiles * align_size;
12924 int playfield_height_corrected = playfield_ytiles * align_size;
12925 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12926 i == GFX_SPECIAL_ARG_EDITOR);
12928 if (is_playfield_mode &&
12929 dynamic_playfield_width &&
12930 vp_playfield->width != playfield_width_corrected)
12932 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12934 vp_playfield->width = playfield_width_corrected;
12936 if (vp_playfield->align == ALIGN_LEFT)
12938 vp_playfield->border_left += playfield_xdiff;
12940 else if (vp_playfield->align == ALIGN_RIGHT)
12942 vp_playfield->border_right += playfield_xdiff;
12944 else if (vp_playfield->align == ALIGN_CENTER)
12946 int border_left_diff = playfield_xdiff / 2;
12947 int border_right_diff = playfield_xdiff - border_left_diff;
12949 vp_playfield->border_left += border_left_diff;
12950 vp_playfield->border_right += border_right_diff;
12954 if (is_playfield_mode &&
12955 dynamic_playfield_height &&
12956 vp_playfield->height != playfield_height_corrected)
12958 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12960 vp_playfield->height = playfield_height_corrected;
12962 if (vp_playfield->valign == VALIGN_TOP)
12964 vp_playfield->border_top += playfield_ydiff;
12966 else if (vp_playfield->align == VALIGN_BOTTOM)
12968 vp_playfield->border_right += playfield_ydiff;
12970 else if (vp_playfield->align == VALIGN_MIDDLE)
12972 int border_top_diff = playfield_ydiff / 2;
12973 int border_bottom_diff = playfield_ydiff - border_top_diff;
12975 vp_playfield->border_top += border_top_diff;
12976 vp_playfield->border_bottom += border_bottom_diff;
12980 // adjust door positions according to specified alignment
12982 for (j = 0; j < 2; j++)
12984 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12986 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12987 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12988 else if (vp_door->align == ALIGN_CENTER)
12989 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12990 else if (vp_door->align == ALIGN_RIGHT)
12991 vp_door->x += vp_window->width - vp_door->width;
12993 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12994 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12995 else if (vp_door->valign == VALIGN_MIDDLE)
12996 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12997 else if (vp_door->valign == VALIGN_BOTTOM)
12998 vp_door->y += vp_window->height - vp_door->height;
13003 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13007 struct XYTileSize *dst, *src;
13010 editor_buttons_xy[] =
13013 &editor.button.element_left, &editor.palette.element_left,
13014 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13017 &editor.button.element_middle, &editor.palette.element_middle,
13018 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13021 &editor.button.element_right, &editor.palette.element_right,
13022 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13029 // set default position for element buttons to element graphics
13030 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13032 if ((*editor_buttons_xy[i].dst).x == -1 &&
13033 (*editor_buttons_xy[i].dst).y == -1)
13035 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13037 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13039 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13043 // adjust editor palette rows and columns if specified to be dynamic
13045 if (editor.palette.cols == -1)
13047 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13048 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13049 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13051 editor.palette.cols = (vp_width - sc_width) / bt_width;
13053 if (editor.palette.x == -1)
13055 int palette_width = editor.palette.cols * bt_width + sc_width;
13057 editor.palette.x = (vp_width - palette_width) / 2;
13061 if (editor.palette.rows == -1)
13063 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13064 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13065 int tx_height = getFontHeight(FONT_TEXT_2);
13067 editor.palette.rows = (vp_height - tx_height) / bt_height;
13069 if (editor.palette.y == -1)
13071 int palette_height = editor.palette.rows * bt_height + tx_height;
13073 editor.palette.y = (vp_height - palette_height) / 2;
13078 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13079 boolean initialize)
13081 // special case: check if network and preview player positions are redefined,
13082 // to compare this later against the main menu level preview being redefined
13083 struct TokenIntPtrInfo menu_config_players[] =
13085 { "main.network_players.x", &menu.main.network_players.redefined },
13086 { "main.network_players.y", &menu.main.network_players.redefined },
13087 { "main.preview_players.x", &menu.main.preview_players.redefined },
13088 { "main.preview_players.y", &menu.main.preview_players.redefined },
13089 { "preview.x", &preview.redefined },
13090 { "preview.y", &preview.redefined }
13096 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13097 *menu_config_players[i].value = FALSE;
13101 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13102 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13103 *menu_config_players[i].value = TRUE;
13107 static void InitMenuDesignSettings_PreviewPlayers(void)
13109 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13112 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13114 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13117 static void LoadMenuDesignSettingsFromFilename(char *filename)
13119 static struct TitleFadingInfo tfi;
13120 static struct TitleMessageInfo tmi;
13121 static struct TokenInfo title_tokens[] =
13123 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13124 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13125 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13126 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13127 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13131 static struct TokenInfo titlemessage_tokens[] =
13133 { TYPE_INTEGER, &tmi.x, ".x" },
13134 { TYPE_INTEGER, &tmi.y, ".y" },
13135 { TYPE_INTEGER, &tmi.width, ".width" },
13136 { TYPE_INTEGER, &tmi.height, ".height" },
13137 { TYPE_INTEGER, &tmi.chars, ".chars" },
13138 { TYPE_INTEGER, &tmi.lines, ".lines" },
13139 { TYPE_INTEGER, &tmi.align, ".align" },
13140 { TYPE_INTEGER, &tmi.valign, ".valign" },
13141 { TYPE_INTEGER, &tmi.font, ".font" },
13142 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13143 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13144 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13145 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13146 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13147 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13148 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13149 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13150 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13156 struct TitleFadingInfo *info;
13161 // initialize first titles from "enter screen" definitions, if defined
13162 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13163 { &title_first_default, "menu.enter_screen.TITLE" },
13165 // initialize title screens from "next screen" definitions, if defined
13166 { &title_initial_default, "menu.next_screen.TITLE" },
13167 { &title_default, "menu.next_screen.TITLE" },
13173 struct TitleMessageInfo *array;
13176 titlemessage_arrays[] =
13178 // initialize first titles from "enter screen" definitions, if defined
13179 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13180 { titlescreen_first, "menu.enter_screen.TITLE" },
13181 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13182 { titlemessage_first, "menu.enter_screen.TITLE" },
13184 // initialize titles from "next screen" definitions, if defined
13185 { titlescreen_initial, "menu.next_screen.TITLE" },
13186 { titlescreen, "menu.next_screen.TITLE" },
13187 { titlemessage_initial, "menu.next_screen.TITLE" },
13188 { titlemessage, "menu.next_screen.TITLE" },
13190 // overwrite titles with title definitions, if defined
13191 { titlescreen_initial_first, "[title_initial]" },
13192 { titlescreen_first, "[title]" },
13193 { titlemessage_initial_first, "[title_initial]" },
13194 { titlemessage_first, "[title]" },
13196 { titlescreen_initial, "[title_initial]" },
13197 { titlescreen, "[title]" },
13198 { titlemessage_initial, "[title_initial]" },
13199 { titlemessage, "[title]" },
13201 // overwrite titles with title screen/message definitions, if defined
13202 { titlescreen_initial_first, "[titlescreen_initial]" },
13203 { titlescreen_first, "[titlescreen]" },
13204 { titlemessage_initial_first, "[titlemessage_initial]" },
13205 { titlemessage_first, "[titlemessage]" },
13207 { titlescreen_initial, "[titlescreen_initial]" },
13208 { titlescreen, "[titlescreen]" },
13209 { titlemessage_initial, "[titlemessage_initial]" },
13210 { titlemessage, "[titlemessage]" },
13214 SetupFileHash *setup_file_hash;
13217 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13220 // the following initializes hierarchical values from dynamic configuration
13222 // special case: initialize with default values that may be overwritten
13223 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13224 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13226 struct TokenIntPtrInfo menu_config[] =
13228 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13229 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13230 { "menu.list_size", &menu.list_size[i] }
13233 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13235 char *token = menu_config[j].token;
13236 char *value = getHashEntry(setup_file_hash, token);
13239 *menu_config[j].value = get_integer_from_string(value);
13243 // special case: initialize with default values that may be overwritten
13244 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13245 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13247 struct TokenIntPtrInfo menu_config[] =
13249 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13250 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13251 { "menu.list_size.INFO", &menu.list_size_info[i] },
13252 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13253 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13256 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13258 char *token = menu_config[j].token;
13259 char *value = getHashEntry(setup_file_hash, token);
13262 *menu_config[j].value = get_integer_from_string(value);
13266 // special case: initialize with default values that may be overwritten
13267 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13268 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13270 struct TokenIntPtrInfo menu_config[] =
13272 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13273 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13276 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13278 char *token = menu_config[j].token;
13279 char *value = getHashEntry(setup_file_hash, token);
13282 *menu_config[j].value = get_integer_from_string(value);
13286 // special case: initialize with default values that may be overwritten
13287 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13288 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13290 struct TokenIntPtrInfo menu_config[] =
13292 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13293 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13294 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13295 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13296 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13297 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13298 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13299 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13300 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13301 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13304 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13306 char *token = menu_config[j].token;
13307 char *value = getHashEntry(setup_file_hash, token);
13310 *menu_config[j].value = get_integer_from_string(value);
13314 // special case: initialize with default values that may be overwritten
13315 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13316 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13318 struct TokenIntPtrInfo menu_config[] =
13320 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13321 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13322 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13323 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13324 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13325 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13326 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13327 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13328 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13331 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13333 char *token = menu_config[j].token;
13334 char *value = getHashEntry(setup_file_hash, token);
13337 *menu_config[j].value = get_token_parameter_value(token, value);
13341 // special case: initialize with default values that may be overwritten
13342 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13343 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13347 char *token_prefix;
13348 struct RectWithBorder *struct_ptr;
13352 { "viewport.window", &viewport.window[i] },
13353 { "viewport.playfield", &viewport.playfield[i] },
13354 { "viewport.door_1", &viewport.door_1[i] },
13355 { "viewport.door_2", &viewport.door_2[i] }
13358 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13360 struct TokenIntPtrInfo vp_config[] =
13362 { ".x", &vp_struct[j].struct_ptr->x },
13363 { ".y", &vp_struct[j].struct_ptr->y },
13364 { ".width", &vp_struct[j].struct_ptr->width },
13365 { ".height", &vp_struct[j].struct_ptr->height },
13366 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13367 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13368 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13369 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13370 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13371 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13372 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13373 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13374 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13375 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13376 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13377 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13378 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13379 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13380 { ".align", &vp_struct[j].struct_ptr->align },
13381 { ".valign", &vp_struct[j].struct_ptr->valign }
13384 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13386 char *token = getStringCat2(vp_struct[j].token_prefix,
13387 vp_config[k].token);
13388 char *value = getHashEntry(setup_file_hash, token);
13391 *vp_config[k].value = get_token_parameter_value(token, value);
13398 // special case: initialize with default values that may be overwritten
13399 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13400 for (i = 0; title_info[i].info != NULL; i++)
13402 struct TitleFadingInfo *info = title_info[i].info;
13403 char *base_token = title_info[i].text;
13405 for (j = 0; title_tokens[j].type != -1; j++)
13407 char *token = getStringCat2(base_token, title_tokens[j].text);
13408 char *value = getHashEntry(setup_file_hash, token);
13412 int parameter_value = get_token_parameter_value(token, value);
13416 *(int *)title_tokens[j].value = (int)parameter_value;
13425 // special case: initialize with default values that may be overwritten
13426 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13427 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13429 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13430 char *base_token = titlemessage_arrays[i].text;
13432 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13434 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13435 char *value = getHashEntry(setup_file_hash, token);
13439 int parameter_value = get_token_parameter_value(token, value);
13441 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13445 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13446 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13448 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13458 // read (and overwrite with) values that may be specified in config file
13459 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13461 // special case: check if network and preview player positions are redefined
13462 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13464 freeSetupFileHash(setup_file_hash);
13467 void LoadMenuDesignSettings(void)
13469 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13471 InitMenuDesignSettings_Static();
13472 InitMenuDesignSettings_SpecialPreProcessing();
13473 InitMenuDesignSettings_PreviewPlayers();
13475 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13477 // first look for special settings configured in level series config
13478 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13480 if (fileExists(filename_base))
13481 LoadMenuDesignSettingsFromFilename(filename_base);
13484 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13486 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13487 LoadMenuDesignSettingsFromFilename(filename_local);
13489 InitMenuDesignSettings_SpecialPostProcessing();
13492 void LoadMenuDesignSettings_AfterGraphics(void)
13494 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13497 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13498 boolean ignore_defaults)
13502 for (i = 0; sound_config_vars[i].token != NULL; i++)
13504 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13506 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13507 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13511 *sound_config_vars[i].value =
13512 get_token_parameter_value(sound_config_vars[i].token, value);
13516 void InitSoundSettings_Static(void)
13518 // always start with reliable default values from static default config
13519 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13522 static void LoadSoundSettingsFromFilename(char *filename)
13524 SetupFileHash *setup_file_hash;
13526 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13529 // read (and overwrite with) values that may be specified in config file
13530 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13532 freeSetupFileHash(setup_file_hash);
13535 void LoadSoundSettings(void)
13537 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13539 InitSoundSettings_Static();
13541 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13543 // first look for special settings configured in level series config
13544 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13546 if (fileExists(filename_base))
13547 LoadSoundSettingsFromFilename(filename_base);
13550 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13552 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13553 LoadSoundSettingsFromFilename(filename_local);
13556 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13558 char *filename = getEditorSetupFilename();
13559 SetupFileList *setup_file_list, *list;
13560 SetupFileHash *element_hash;
13561 int num_unknown_tokens = 0;
13564 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13567 element_hash = newSetupFileHash();
13569 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13570 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13572 // determined size may be larger than needed (due to unknown elements)
13574 for (list = setup_file_list; list != NULL; list = list->next)
13577 // add space for up to 3 more elements for padding that may be needed
13578 *num_elements += 3;
13580 // free memory for old list of elements, if needed
13581 checked_free(*elements);
13583 // allocate memory for new list of elements
13584 *elements = checked_malloc(*num_elements * sizeof(int));
13587 for (list = setup_file_list; list != NULL; list = list->next)
13589 char *value = getHashEntry(element_hash, list->token);
13591 if (value == NULL) // try to find obsolete token mapping
13593 char *mapped_token = get_mapped_token(list->token);
13595 if (mapped_token != NULL)
13597 value = getHashEntry(element_hash, mapped_token);
13599 free(mapped_token);
13605 (*elements)[(*num_elements)++] = atoi(value);
13609 if (num_unknown_tokens == 0)
13612 Warn("unknown token(s) found in config file:");
13613 Warn("- config file: '%s'", filename);
13615 num_unknown_tokens++;
13618 Warn("- token: '%s'", list->token);
13622 if (num_unknown_tokens > 0)
13625 while (*num_elements % 4) // pad with empty elements, if needed
13626 (*elements)[(*num_elements)++] = EL_EMPTY;
13628 freeSetupFileList(setup_file_list);
13629 freeSetupFileHash(element_hash);
13632 for (i = 0; i < *num_elements; i++)
13633 Debug("editor", "element '%s' [%d]\n",
13634 element_info[(*elements)[i]].token_name, (*elements)[i]);
13638 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13641 SetupFileHash *setup_file_hash = NULL;
13642 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13643 char *filename_music, *filename_prefix, *filename_info;
13649 token_to_value_ptr[] =
13651 { "title_header", &tmp_music_file_info.title_header },
13652 { "artist_header", &tmp_music_file_info.artist_header },
13653 { "album_header", &tmp_music_file_info.album_header },
13654 { "year_header", &tmp_music_file_info.year_header },
13655 { "played_header", &tmp_music_file_info.played_header },
13657 { "title", &tmp_music_file_info.title },
13658 { "artist", &tmp_music_file_info.artist },
13659 { "album", &tmp_music_file_info.album },
13660 { "year", &tmp_music_file_info.year },
13661 { "played", &tmp_music_file_info.played },
13667 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13668 getCustomMusicFilename(basename));
13670 if (filename_music == NULL)
13673 // ---------- try to replace file extension ----------
13675 filename_prefix = getStringCopy(filename_music);
13676 if (strrchr(filename_prefix, '.') != NULL)
13677 *strrchr(filename_prefix, '.') = '\0';
13678 filename_info = getStringCat2(filename_prefix, ".txt");
13680 if (fileExists(filename_info))
13681 setup_file_hash = loadSetupFileHash(filename_info);
13683 free(filename_prefix);
13684 free(filename_info);
13686 if (setup_file_hash == NULL)
13688 // ---------- try to add file extension ----------
13690 filename_prefix = getStringCopy(filename_music);
13691 filename_info = getStringCat2(filename_prefix, ".txt");
13693 if (fileExists(filename_info))
13694 setup_file_hash = loadSetupFileHash(filename_info);
13696 free(filename_prefix);
13697 free(filename_info);
13700 if (setup_file_hash == NULL)
13703 // ---------- music file info found ----------
13705 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13707 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13709 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13711 *token_to_value_ptr[i].value_ptr =
13712 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13715 tmp_music_file_info.basename = getStringCopy(basename);
13716 tmp_music_file_info.music = music;
13717 tmp_music_file_info.is_sound = is_sound;
13719 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13720 *new_music_file_info = tmp_music_file_info;
13722 return new_music_file_info;
13725 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13727 return get_music_file_info_ext(basename, music, FALSE);
13730 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13732 return get_music_file_info_ext(basename, sound, TRUE);
13735 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13736 char *basename, boolean is_sound)
13738 for (; list != NULL; list = list->next)
13739 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13745 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13747 return music_info_listed_ext(list, basename, FALSE);
13750 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13752 return music_info_listed_ext(list, basename, TRUE);
13755 void LoadMusicInfo(void)
13757 int num_music_noconf = getMusicListSize_NoConf();
13758 int num_music = getMusicListSize();
13759 int num_sounds = getSoundListSize();
13760 struct FileInfo *music, *sound;
13761 struct MusicFileInfo *next, **new;
13765 while (music_file_info != NULL)
13767 next = music_file_info->next;
13769 checked_free(music_file_info->basename);
13771 checked_free(music_file_info->title_header);
13772 checked_free(music_file_info->artist_header);
13773 checked_free(music_file_info->album_header);
13774 checked_free(music_file_info->year_header);
13775 checked_free(music_file_info->played_header);
13777 checked_free(music_file_info->title);
13778 checked_free(music_file_info->artist);
13779 checked_free(music_file_info->album);
13780 checked_free(music_file_info->year);
13781 checked_free(music_file_info->played);
13783 free(music_file_info);
13785 music_file_info = next;
13788 new = &music_file_info;
13790 // get (configured or unconfigured) music file info for all levels
13791 for (i = leveldir_current->first_level;
13792 i <= leveldir_current->last_level; i++)
13796 if (levelset.music[i] != MUS_UNDEFINED)
13798 // get music file info for configured level music
13799 music_nr = levelset.music[i];
13801 else if (num_music_noconf > 0)
13803 // get music file info for unconfigured level music
13804 int level_pos = i - leveldir_current->first_level;
13806 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13813 char *basename = getMusicInfoEntryFilename(music_nr);
13815 if (basename == NULL)
13818 if (!music_info_listed(music_file_info, basename))
13820 *new = get_music_file_info(basename, music_nr);
13823 new = &(*new)->next;
13827 // get music file info for all remaining configured music files
13828 for (i = 0; i < num_music; i++)
13830 music = getMusicListEntry(i);
13832 if (music->filename == NULL)
13835 if (strEqual(music->filename, UNDEFINED_FILENAME))
13838 // a configured file may be not recognized as music
13839 if (!FileIsMusic(music->filename))
13842 if (!music_info_listed(music_file_info, music->filename))
13844 *new = get_music_file_info(music->filename, i);
13847 new = &(*new)->next;
13851 // get sound file info for all configured sound files
13852 for (i = 0; i < num_sounds; i++)
13854 sound = getSoundListEntry(i);
13856 if (sound->filename == NULL)
13859 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13862 // a configured file may be not recognized as sound
13863 if (!FileIsSound(sound->filename))
13866 if (!sound_info_listed(music_file_info, sound->filename))
13868 *new = get_sound_file_info(sound->filename, i);
13870 new = &(*new)->next;
13874 // add pointers to previous list nodes
13876 struct MusicFileInfo *node = music_file_info;
13878 while (node != NULL)
13881 node->next->prev = node;
13887 static void add_helpanim_entry(int element, int action, int direction,
13888 int delay, int *num_list_entries)
13890 struct HelpAnimInfo *new_list_entry;
13891 (*num_list_entries)++;
13894 checked_realloc(helpanim_info,
13895 *num_list_entries * sizeof(struct HelpAnimInfo));
13896 new_list_entry = &helpanim_info[*num_list_entries - 1];
13898 new_list_entry->element = element;
13899 new_list_entry->action = action;
13900 new_list_entry->direction = direction;
13901 new_list_entry->delay = delay;
13904 static void print_unknown_token(char *filename, char *token, int token_nr)
13909 Warn("unknown token(s) found in config file:");
13910 Warn("- config file: '%s'", filename);
13913 Warn("- token: '%s'", token);
13916 static void print_unknown_token_end(int token_nr)
13922 void LoadHelpAnimInfo(void)
13924 char *filename = getHelpAnimFilename();
13925 SetupFileList *setup_file_list = NULL, *list;
13926 SetupFileHash *element_hash, *action_hash, *direction_hash;
13927 int num_list_entries = 0;
13928 int num_unknown_tokens = 0;
13931 if (fileExists(filename))
13932 setup_file_list = loadSetupFileList(filename);
13934 if (setup_file_list == NULL)
13936 // use reliable default values from static configuration
13937 SetupFileList *insert_ptr;
13939 insert_ptr = setup_file_list =
13940 newSetupFileList(helpanim_config[0].token,
13941 helpanim_config[0].value);
13943 for (i = 1; helpanim_config[i].token; i++)
13944 insert_ptr = addListEntry(insert_ptr,
13945 helpanim_config[i].token,
13946 helpanim_config[i].value);
13949 element_hash = newSetupFileHash();
13950 action_hash = newSetupFileHash();
13951 direction_hash = newSetupFileHash();
13953 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13954 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13956 for (i = 0; i < NUM_ACTIONS; i++)
13957 setHashEntry(action_hash, element_action_info[i].suffix,
13958 i_to_a(element_action_info[i].value));
13960 // do not store direction index (bit) here, but direction value!
13961 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13962 setHashEntry(direction_hash, element_direction_info[i].suffix,
13963 i_to_a(1 << element_direction_info[i].value));
13965 for (list = setup_file_list; list != NULL; list = list->next)
13967 char *element_token, *action_token, *direction_token;
13968 char *element_value, *action_value, *direction_value;
13969 int delay = atoi(list->value);
13971 if (strEqual(list->token, "end"))
13973 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13978 /* first try to break element into element/action/direction parts;
13979 if this does not work, also accept combined "element[.act][.dir]"
13980 elements (like "dynamite.active"), which are unique elements */
13982 if (strchr(list->token, '.') == NULL) // token contains no '.'
13984 element_value = getHashEntry(element_hash, list->token);
13985 if (element_value != NULL) // element found
13986 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13987 &num_list_entries);
13990 // no further suffixes found -- this is not an element
13991 print_unknown_token(filename, list->token, num_unknown_tokens++);
13997 // token has format "<prefix>.<something>"
13999 action_token = strchr(list->token, '.'); // suffix may be action ...
14000 direction_token = action_token; // ... or direction
14002 element_token = getStringCopy(list->token);
14003 *strchr(element_token, '.') = '\0';
14005 element_value = getHashEntry(element_hash, element_token);
14007 if (element_value == NULL) // this is no element
14009 element_value = getHashEntry(element_hash, list->token);
14010 if (element_value != NULL) // combined element found
14011 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14012 &num_list_entries);
14014 print_unknown_token(filename, list->token, num_unknown_tokens++);
14016 free(element_token);
14021 action_value = getHashEntry(action_hash, action_token);
14023 if (action_value != NULL) // action found
14025 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14026 &num_list_entries);
14028 free(element_token);
14033 direction_value = getHashEntry(direction_hash, direction_token);
14035 if (direction_value != NULL) // direction found
14037 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14038 &num_list_entries);
14040 free(element_token);
14045 if (strchr(action_token + 1, '.') == NULL)
14047 // no further suffixes found -- this is not an action nor direction
14049 element_value = getHashEntry(element_hash, list->token);
14050 if (element_value != NULL) // combined element found
14051 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14052 &num_list_entries);
14054 print_unknown_token(filename, list->token, num_unknown_tokens++);
14056 free(element_token);
14061 // token has format "<prefix>.<suffix>.<something>"
14063 direction_token = strchr(action_token + 1, '.');
14065 action_token = getStringCopy(action_token);
14066 *strchr(action_token + 1, '.') = '\0';
14068 action_value = getHashEntry(action_hash, action_token);
14070 if (action_value == NULL) // this is no action
14072 element_value = getHashEntry(element_hash, list->token);
14073 if (element_value != NULL) // combined element found
14074 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14075 &num_list_entries);
14077 print_unknown_token(filename, list->token, num_unknown_tokens++);
14079 free(element_token);
14080 free(action_token);
14085 direction_value = getHashEntry(direction_hash, direction_token);
14087 if (direction_value != NULL) // direction found
14089 add_helpanim_entry(atoi(element_value), atoi(action_value),
14090 atoi(direction_value), delay, &num_list_entries);
14092 free(element_token);
14093 free(action_token);
14098 // this is no direction
14100 element_value = getHashEntry(element_hash, list->token);
14101 if (element_value != NULL) // combined element found
14102 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14103 &num_list_entries);
14105 print_unknown_token(filename, list->token, num_unknown_tokens++);
14107 free(element_token);
14108 free(action_token);
14111 print_unknown_token_end(num_unknown_tokens);
14113 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14114 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14116 freeSetupFileList(setup_file_list);
14117 freeSetupFileHash(element_hash);
14118 freeSetupFileHash(action_hash);
14119 freeSetupFileHash(direction_hash);
14122 for (i = 0; i < num_list_entries; i++)
14123 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14124 EL_NAME(helpanim_info[i].element),
14125 helpanim_info[i].element,
14126 helpanim_info[i].action,
14127 helpanim_info[i].direction,
14128 helpanim_info[i].delay);
14132 void LoadHelpTextInfo(void)
14134 char *filename = getHelpTextFilename();
14137 if (helptext_info != NULL)
14139 freeSetupFileHash(helptext_info);
14140 helptext_info = NULL;
14143 if (fileExists(filename))
14144 helptext_info = loadSetupFileHash(filename);
14146 if (helptext_info == NULL)
14148 // use reliable default values from static configuration
14149 helptext_info = newSetupFileHash();
14151 for (i = 0; helptext_config[i].token; i++)
14152 setHashEntry(helptext_info,
14153 helptext_config[i].token,
14154 helptext_config[i].value);
14158 BEGIN_HASH_ITERATION(helptext_info, itr)
14160 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14161 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14163 END_HASH_ITERATION(hash, itr)
14168 // ----------------------------------------------------------------------------
14170 // ----------------------------------------------------------------------------
14172 #define MAX_NUM_CONVERT_LEVELS 1000
14174 void ConvertLevels(void)
14176 static LevelDirTree *convert_leveldir = NULL;
14177 static int convert_level_nr = -1;
14178 static int num_levels_handled = 0;
14179 static int num_levels_converted = 0;
14180 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14183 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14184 global.convert_leveldir);
14186 if (convert_leveldir == NULL)
14187 Fail("no such level identifier: '%s'", global.convert_leveldir);
14189 leveldir_current = convert_leveldir;
14191 if (global.convert_level_nr != -1)
14193 convert_leveldir->first_level = global.convert_level_nr;
14194 convert_leveldir->last_level = global.convert_level_nr;
14197 convert_level_nr = convert_leveldir->first_level;
14199 PrintLine("=", 79);
14200 Print("Converting levels\n");
14201 PrintLine("-", 79);
14202 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14203 Print("Level series name: '%s'\n", convert_leveldir->name);
14204 Print("Level series author: '%s'\n", convert_leveldir->author);
14205 Print("Number of levels: %d\n", convert_leveldir->levels);
14206 PrintLine("=", 79);
14209 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14210 levels_failed[i] = FALSE;
14212 while (convert_level_nr <= convert_leveldir->last_level)
14214 char *level_filename;
14217 level_nr = convert_level_nr++;
14219 Print("Level %03d: ", level_nr);
14221 LoadLevel(level_nr);
14222 if (level.no_level_file || level.no_valid_file)
14224 Print("(no level)\n");
14228 Print("converting level ... ");
14231 // special case: conversion of some EMC levels as requested by ACME
14232 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14235 level_filename = getDefaultLevelFilename(level_nr);
14236 new_level = !fileExists(level_filename);
14240 SaveLevel(level_nr);
14242 num_levels_converted++;
14244 Print("converted.\n");
14248 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14249 levels_failed[level_nr] = TRUE;
14251 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14254 num_levels_handled++;
14258 PrintLine("=", 79);
14259 Print("Number of levels handled: %d\n", num_levels_handled);
14260 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14261 (num_levels_handled ?
14262 num_levels_converted * 100 / num_levels_handled : 0));
14263 PrintLine("-", 79);
14264 Print("Summary (for automatic parsing by scripts):\n");
14265 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14266 convert_leveldir->identifier, num_levels_converted,
14267 num_levels_handled,
14268 (num_levels_handled ?
14269 num_levels_converted * 100 / num_levels_handled : 0));
14271 if (num_levels_handled != num_levels_converted)
14273 Print(", FAILED:");
14274 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14275 if (levels_failed[i])
14280 PrintLine("=", 79);
14282 CloseAllAndExit(0);
14286 // ----------------------------------------------------------------------------
14287 // create and save images for use in level sketches (raw BMP format)
14288 // ----------------------------------------------------------------------------
14290 void CreateLevelSketchImages(void)
14296 InitElementPropertiesGfxElement();
14298 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14299 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14301 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14303 int element = getMappedElement(i);
14304 char basename1[16];
14305 char basename2[16];
14309 sprintf(basename1, "%04d.bmp", i);
14310 sprintf(basename2, "%04ds.bmp", i);
14312 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14313 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14315 DrawSizedElement(0, 0, element, TILESIZE);
14316 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14318 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14319 Fail("cannot save level sketch image file '%s'", filename1);
14321 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14322 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14324 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14325 Fail("cannot save level sketch image file '%s'", filename2);
14330 // create corresponding SQL statements (for normal and small images)
14333 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14334 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14337 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14338 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14340 // optional: create content for forum level sketch demonstration post
14342 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14345 FreeBitmap(bitmap1);
14346 FreeBitmap(bitmap2);
14349 fprintf(stderr, "\n");
14351 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14353 CloseAllAndExit(0);
14357 // ----------------------------------------------------------------------------
14358 // create and save images for element collecting animations (raw BMP format)
14359 // ----------------------------------------------------------------------------
14361 static boolean createCollectImage(int element)
14363 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14366 void CreateCollectElementImages(void)
14370 int anim_frames = num_steps - 1;
14371 int tile_size = TILESIZE;
14372 int anim_width = tile_size * anim_frames;
14373 int anim_height = tile_size;
14374 int num_collect_images = 0;
14375 int pos_collect_images = 0;
14377 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14378 if (createCollectImage(i))
14379 num_collect_images++;
14381 Info("Creating %d element collecting animation images ...",
14382 num_collect_images);
14384 int dst_width = anim_width * 2;
14385 int dst_height = anim_height * num_collect_images / 2;
14386 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14387 char *basename_bmp = "RocksCollect.bmp";
14388 char *basename_png = "RocksCollect.png";
14389 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14390 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14391 int len_filename_bmp = strlen(filename_bmp);
14392 int len_filename_png = strlen(filename_png);
14393 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14394 char cmd_convert[max_command_len];
14396 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14400 // force using RGBA surface for destination bitmap
14401 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14402 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14404 dst_bitmap->surface =
14405 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14407 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14409 if (!createCollectImage(i))
14412 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14413 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14414 int graphic = el2img(i);
14415 char *token_name = element_info[i].token_name;
14416 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14417 Bitmap *src_bitmap;
14420 Info("- creating collecting image for '%s' ...", token_name);
14422 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14424 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14425 tile_size, tile_size, 0, 0);
14427 // force using RGBA surface for temporary bitmap (using transparent black)
14428 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14429 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14431 tmp_bitmap->surface =
14432 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14434 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14436 for (j = 0; j < anim_frames; j++)
14438 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14439 int frame_size = frame_size_final * num_steps;
14440 int offset = (tile_size - frame_size_final) / 2;
14441 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14443 while (frame_size > frame_size_final)
14447 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14449 FreeBitmap(frame_bitmap);
14451 frame_bitmap = half_bitmap;
14454 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14455 frame_size_final, frame_size_final,
14456 dst_x + j * tile_size + offset, dst_y + offset);
14458 FreeBitmap(frame_bitmap);
14461 tmp_bitmap->surface_masked = NULL;
14463 FreeBitmap(tmp_bitmap);
14465 pos_collect_images++;
14468 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14469 Fail("cannot save element collecting image file '%s'", filename_bmp);
14471 FreeBitmap(dst_bitmap);
14473 Info("Converting image file from BMP to PNG ...");
14475 if (system(cmd_convert) != 0)
14476 Fail("converting image file failed");
14478 unlink(filename_bmp);
14482 CloseAllAndExit(0);
14486 // ----------------------------------------------------------------------------
14487 // create and save images for custom and group elements (raw BMP format)
14488 // ----------------------------------------------------------------------------
14490 void CreateCustomElementImages(char *directory)
14492 char *src_basename = "RocksCE-template.ilbm";
14493 char *dst_basename = "RocksCE.bmp";
14494 char *src_filename = getPath2(directory, src_basename);
14495 char *dst_filename = getPath2(directory, dst_basename);
14496 Bitmap *src_bitmap;
14498 int yoffset_ce = 0;
14499 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14502 InitVideoDefaults();
14504 ReCreateBitmap(&backbuffer, video.width, video.height);
14506 src_bitmap = LoadImage(src_filename);
14508 bitmap = CreateBitmap(TILEX * 16 * 2,
14509 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14512 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14519 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14520 TILEX * x, TILEY * y + yoffset_ce);
14522 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14524 TILEX * x + TILEX * 16,
14525 TILEY * y + yoffset_ce);
14527 for (j = 2; j >= 0; j--)
14531 BlitBitmap(src_bitmap, bitmap,
14532 TILEX + c * 7, 0, 6, 10,
14533 TILEX * x + 6 + j * 7,
14534 TILEY * y + 11 + yoffset_ce);
14536 BlitBitmap(src_bitmap, bitmap,
14537 TILEX + c * 8, TILEY, 6, 10,
14538 TILEX * 16 + TILEX * x + 6 + j * 8,
14539 TILEY * y + 10 + yoffset_ce);
14545 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14552 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14553 TILEX * x, TILEY * y + yoffset_ge);
14555 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14557 TILEX * x + TILEX * 16,
14558 TILEY * y + yoffset_ge);
14560 for (j = 1; j >= 0; j--)
14564 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14565 TILEX * x + 6 + j * 10,
14566 TILEY * y + 11 + yoffset_ge);
14568 BlitBitmap(src_bitmap, bitmap,
14569 TILEX + c * 8, TILEY + 12, 6, 10,
14570 TILEX * 16 + TILEX * x + 10 + j * 8,
14571 TILEY * y + 10 + yoffset_ge);
14577 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14578 Fail("cannot save CE graphics file '%s'", dst_filename);
14580 FreeBitmap(bitmap);
14582 CloseAllAndExit(0);