1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
315 TYPE_INTEGER, CONF_VALUE_8_BIT(24),
316 &li.bd_cave_random_seed_c64, 0
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
328 // (these values are the same for each player)
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
332 &li.block_last_field, FALSE // default case for EM levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
337 &li.sp_block_last_field, TRUE // default case for SP levels
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
342 &li.instant_relocation, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
347 &li.can_pass_to_walkable, FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
352 &li.block_snap_field, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
357 &li.continuous_snapping, TRUE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
362 &li.shifted_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
367 &li.lazy_relocation, FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
372 &li.finish_dig_collect, TRUE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
377 &li.keep_walkable_ce, FALSE
380 // (these values are different for each player)
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[0], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[0], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[0], EL_PLAYER_1
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[0], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[0], EL_PLAYER_1
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[0], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[0], EL_PLAYER_1
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[0], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[0], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[1], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[1], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[1], EL_PLAYER_2
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[1], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[1], EL_PLAYER_2
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[1], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[1], EL_PLAYER_2
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[1], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[1], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[2], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[2], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[2], EL_PLAYER_3
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[2], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[2], EL_PLAYER_3
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[2], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[2], EL_PLAYER_3
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[2], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[2], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
555 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
560 &li.initial_player_gravity[3], FALSE
564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
565 &li.use_start_element[3], FALSE
569 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
570 &li.start_element[3], EL_PLAYER_4
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
575 &li.use_artwork_element[3], FALSE
579 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
580 &li.artwork_element[3], EL_PLAYER_4
584 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
585 &li.use_explosion_element[3], FALSE
589 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
590 &li.explosion_element[3], EL_PLAYER_4
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
595 &li.use_initial_inventory[3], FALSE
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
600 &li.initial_inventory_size[3], 1
604 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
605 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
609 // (these values are only valid for BD style levels)
610 // (some values for BD style amoeba following below)
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
614 &li.bd_diagonal_movements, FALSE
618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
619 &li.bd_topmost_player_active, TRUE
623 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
624 &li.bd_pushing_prob, 25
628 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
629 &li.bd_pushing_prob_with_sweet, 100
633 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
634 &li.bd_push_mega_rock_with_sweet, FALSE
638 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
639 &li.bd_snap_element, EL_EMPTY
644 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
645 &li.score[SC_DIAMOND_EXTRA], 20
649 EL_BD_MAGIC_WALL, -1,
650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
651 &li.bd_magic_wall_wait_hatching, FALSE
654 EL_BD_MAGIC_WALL, -1,
655 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
656 &li.bd_magic_wall_stops_amoeba, TRUE
661 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
662 &li.bd_clock_extra_time, 30
666 EL_BD_VOODOO_DOLL, -1,
667 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
668 &li.bd_voodoo_collects_diamonds, FALSE
671 EL_BD_VOODOO_DOLL, -1,
672 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
673 &li.bd_voodoo_hurt_kills_player, FALSE
676 EL_BD_VOODOO_DOLL, -1,
677 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
678 &li.bd_voodoo_dies_by_rock, FALSE
681 EL_BD_VOODOO_DOLL, -1,
682 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
683 &li.bd_voodoo_vanish_by_explosion, TRUE
686 EL_BD_VOODOO_DOLL, -1,
687 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
688 &li.bd_voodoo_penalty_time, 30
693 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
694 &li.bd_slime_is_predictable, TRUE
698 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
699 &li.bd_slime_permeability_rate, 100
703 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
704 &li.bd_slime_permeability_bits_c64, 0
708 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
709 &li.bd_slime_random_seed_c64, -1
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.bd_acid_eats_element, EL_BD_SAND
719 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
720 &li.bd_acid_spread_rate, 3
724 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
725 &li.bd_acid_turns_to_element, EL_EMPTY
730 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
731 &li.bd_biter_move_delay, 0
735 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
736 &li.bd_biter_eats_element, EL_BD_DIAMOND
741 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
742 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
745 // (the following values are related to various game elements)
749 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
750 &li.score[SC_EMERALD], 10
755 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
756 &li.score[SC_DIAMOND], 10
761 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
762 &li.score[SC_BUG], 10
767 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
768 &li.score[SC_SPACESHIP], 10
773 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
774 &li.score[SC_PACMAN], 10
779 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
780 &li.score[SC_NUT], 10
785 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
786 &li.score[SC_DYNAMITE], 10
791 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
792 &li.score[SC_KEY], 10
797 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
798 &li.score[SC_PEARL], 10
803 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
804 &li.score[SC_CRYSTAL], 10
807 // (amoeba values used by R'n'D game engine only)
810 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
811 &li.amoeba_content, EL_DIAMOND
815 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
820 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
821 &li.grow_into_diggable, TRUE
823 // (amoeba values used by BD game engine only)
826 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
827 &li.bd_amoeba_wait_for_hatching, FALSE
831 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
832 &li.bd_amoeba_start_immediately, TRUE
836 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
837 &li.bd_amoeba_2_explode_by_amoeba, TRUE
841 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
842 &li.bd_amoeba_threshold_too_big, 200
846 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
847 &li.bd_amoeba_slow_growth_time, 200
851 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
852 &li.bd_amoeba_slow_growth_rate, 3
856 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
857 &li.bd_amoeba_fast_growth_rate, 25
861 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
862 &li.bd_amoeba_content_too_big, EL_BD_ROCK
866 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
867 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
872 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
873 &li.bd_amoeba_2_threshold_too_big, 200
877 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
878 &li.bd_amoeba_2_slow_growth_time, 200
882 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
883 &li.bd_amoeba_2_slow_growth_rate, 3
887 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
888 &li.bd_amoeba_2_fast_growth_rate, 25
892 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
893 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
897 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
898 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
902 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
903 &li.bd_amoeba_2_content_exploding, EL_EMPTY
907 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
908 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
913 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
914 &li.yamyam_content, EL_ROCK, NULL,
915 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
919 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
920 &li.score[SC_YAMYAM], 10
925 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
926 &li.score[SC_ROBOT], 10
930 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
936 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
942 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
943 &li.time_magic_wall, 10
948 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
949 &li.game_of_life[0], 2
953 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
954 &li.game_of_life[1], 3
958 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
959 &li.game_of_life[2], 3
963 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
964 &li.game_of_life[3], 3
968 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
969 &li.use_life_bugs, FALSE
974 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
979 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
984 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
989 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
994 EL_TIMEGATE_SWITCH, -1,
995 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
996 &li.time_timegate, 10
1000 EL_LIGHT_SWITCH_ACTIVE, -1,
1001 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1006 EL_SHIELD_NORMAL, -1,
1007 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1008 &li.shield_normal_time, 10
1011 EL_SHIELD_NORMAL, -1,
1012 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1013 &li.score[SC_SHIELD], 10
1017 EL_SHIELD_DEADLY, -1,
1018 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1019 &li.shield_deadly_time, 10
1022 EL_SHIELD_DEADLY, -1,
1023 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1024 &li.score[SC_SHIELD], 10
1029 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1034 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1035 &li.extra_time_score, 10
1039 EL_TIME_ORB_FULL, -1,
1040 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1041 &li.time_orb_time, 10
1044 EL_TIME_ORB_FULL, -1,
1045 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1046 &li.use_time_orb_bug, FALSE
1051 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1052 &li.use_spring_bug, FALSE
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1058 &li.android_move_time, 10
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1063 &li.android_clone_time, 10
1066 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1067 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1068 &li.android_clone_element[0], EL_EMPTY, NULL,
1069 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1073 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1074 &li.android_clone_element[0], EL_EMPTY, NULL,
1075 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1080 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1081 &li.lenses_score, 10
1085 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1090 EL_EMC_MAGNIFIER, -1,
1091 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1092 &li.magnify_score, 10
1095 EL_EMC_MAGNIFIER, -1,
1096 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1097 &li.magnify_time, 10
1101 EL_EMC_MAGIC_BALL, -1,
1102 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1106 EL_EMC_MAGIC_BALL, -1,
1107 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1108 &li.ball_random, FALSE
1111 EL_EMC_MAGIC_BALL, -1,
1112 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1113 &li.ball_active_initial, FALSE
1116 EL_EMC_MAGIC_BALL, -1,
1117 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1118 &li.ball_content, EL_EMPTY, NULL,
1119 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1123 EL_SOKOBAN_FIELD_EMPTY, -1,
1124 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1125 &li.sb_fields_needed, TRUE
1129 EL_SOKOBAN_OBJECT, -1,
1130 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1131 &li.sb_objects_needed, TRUE
1136 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1137 &li.mm_laser_red, FALSE
1141 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1142 &li.mm_laser_green, FALSE
1146 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1147 &li.mm_laser_blue, TRUE
1152 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1153 &li.df_laser_red, TRUE
1157 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1158 &li.df_laser_green, TRUE
1162 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1163 &li.df_laser_blue, FALSE
1167 EL_MM_FUSE_ACTIVE, -1,
1168 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1169 &li.mm_time_fuse, 25
1173 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1174 &li.mm_time_bomb, 75
1178 EL_MM_GRAY_BALL, -1,
1179 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1180 &li.mm_time_ball, 75
1183 EL_MM_GRAY_BALL, -1,
1184 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1185 &li.mm_ball_choice_mode, ANIM_RANDOM
1188 EL_MM_GRAY_BALL, -1,
1189 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1190 &li.mm_ball_content, EL_EMPTY, NULL,
1191 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1194 EL_MM_GRAY_BALL, -1,
1195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1196 &li.rotate_mm_ball_content, TRUE
1199 EL_MM_GRAY_BALL, -1,
1200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1201 &li.explode_mm_ball, FALSE
1205 EL_MM_STEEL_BLOCK, -1,
1206 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1207 &li.mm_time_block, 75
1210 EL_MM_LIGHTBALL, -1,
1211 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1212 &li.score[SC_ELEM_BONUS], 10
1222 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1226 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1227 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1231 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1232 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1237 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1238 &xx_envelope.autowrap, FALSE
1242 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1243 &xx_envelope.centered, FALSE
1248 TYPE_STRING, CONF_VALUE_BYTES(1),
1249 &xx_envelope.text, -1, NULL,
1250 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1251 &xx_default_string_empty[0]
1261 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1265 TYPE_STRING, CONF_VALUE_BYTES(1),
1266 &xx_ei.description[0], -1,
1267 &yy_ei.description[0],
1268 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1269 &xx_default_description[0]
1274 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1275 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1276 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1278 #if ENABLE_RESERVED_CODE
1279 // (reserved for later use)
1282 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1283 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1284 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1290 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1291 &xx_ei.use_gfx_element, FALSE,
1292 &yy_ei.use_gfx_element
1296 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1297 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1298 &yy_ei.gfx_element_initial
1303 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1304 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1305 &yy_ei.access_direction
1310 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1311 &xx_ei.collect_score_initial, 10,
1312 &yy_ei.collect_score_initial
1316 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1317 &xx_ei.collect_count_initial, 1,
1318 &yy_ei.collect_count_initial
1323 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1324 &xx_ei.ce_value_fixed_initial, 0,
1325 &yy_ei.ce_value_fixed_initial
1329 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1330 &xx_ei.ce_value_random_initial, 0,
1331 &yy_ei.ce_value_random_initial
1335 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1336 &xx_ei.use_last_ce_value, FALSE,
1337 &yy_ei.use_last_ce_value
1342 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1343 &xx_ei.push_delay_fixed, 8,
1344 &yy_ei.push_delay_fixed
1348 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1349 &xx_ei.push_delay_random, 8,
1350 &yy_ei.push_delay_random
1354 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1355 &xx_ei.drop_delay_fixed, 0,
1356 &yy_ei.drop_delay_fixed
1360 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1361 &xx_ei.drop_delay_random, 0,
1362 &yy_ei.drop_delay_random
1366 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1367 &xx_ei.move_delay_fixed, 0,
1368 &yy_ei.move_delay_fixed
1372 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1373 &xx_ei.move_delay_random, 0,
1374 &yy_ei.move_delay_random
1378 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1379 &xx_ei.step_delay_fixed, 0,
1380 &yy_ei.step_delay_fixed
1384 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1385 &xx_ei.step_delay_random, 0,
1386 &yy_ei.step_delay_random
1391 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1392 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1397 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1398 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1399 &yy_ei.move_direction_initial
1403 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1404 &xx_ei.move_stepsize, TILEX / 8,
1405 &yy_ei.move_stepsize
1410 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1411 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1412 &yy_ei.move_enter_element
1416 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1417 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1418 &yy_ei.move_leave_element
1422 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1423 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1424 &yy_ei.move_leave_type
1429 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1430 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1431 &yy_ei.slippery_type
1436 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1437 &xx_ei.explosion_type, EXPLODES_3X3,
1438 &yy_ei.explosion_type
1442 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1443 &xx_ei.explosion_delay, 16,
1444 &yy_ei.explosion_delay
1448 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1449 &xx_ei.ignition_delay, 8,
1450 &yy_ei.ignition_delay
1455 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1456 &xx_ei.content, EL_EMPTY_SPACE,
1458 &xx_num_contents, 1, 1
1461 // ---------- "num_change_pages" must be the last entry ---------------------
1464 -1, SAVE_CONF_ALWAYS,
1465 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1466 &xx_ei.num_change_pages, 1,
1467 &yy_ei.num_change_pages
1478 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1480 // ---------- "current_change_page" must be the first entry -----------------
1483 -1, SAVE_CONF_ALWAYS,
1484 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1485 &xx_current_change_page, -1
1488 // ---------- (the remaining entries can be in any order) -------------------
1492 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1493 &xx_change.can_change, FALSE
1498 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1499 &xx_event_bits[0], 0
1503 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1504 &xx_event_bits[1], 0
1509 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1510 &xx_change.trigger_player, CH_PLAYER_ANY
1514 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1515 &xx_change.trigger_side, CH_SIDE_ANY
1519 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1520 &xx_change.trigger_page, CH_PAGE_ANY
1525 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1526 &xx_change.target_element, EL_EMPTY_SPACE
1531 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1532 &xx_change.delay_fixed, 0
1536 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1537 &xx_change.delay_random, 0
1541 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1542 &xx_change.delay_frames, FRAMES_PER_SECOND
1547 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1548 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1553 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1554 &xx_change.explode, FALSE
1558 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1559 &xx_change.use_target_content, FALSE
1563 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1564 &xx_change.only_if_complete, FALSE
1568 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1569 &xx_change.use_random_replace, FALSE
1573 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1574 &xx_change.random_percentage, 100
1578 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1579 &xx_change.replace_when, CP_WHEN_EMPTY
1584 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1585 &xx_change.has_action, FALSE
1589 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1590 &xx_change.action_type, CA_NO_ACTION
1594 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1595 &xx_change.action_mode, CA_MODE_UNDEFINED
1599 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1600 &xx_change.action_arg, CA_ARG_UNDEFINED
1605 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1606 &xx_change.action_element, EL_EMPTY_SPACE
1611 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1612 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1613 &xx_num_contents, 1, 1
1623 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1627 TYPE_STRING, CONF_VALUE_BYTES(1),
1628 &xx_ei.description[0], -1, NULL,
1629 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1630 &xx_default_description[0]
1635 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1636 &xx_ei.use_gfx_element, FALSE
1640 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1641 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1646 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1647 &xx_group.choice_mode, ANIM_RANDOM
1652 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1653 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1654 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1664 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1668 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1669 &xx_ei.use_gfx_element, FALSE
1673 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1674 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1684 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1688 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1689 &li.block_snap_field, TRUE
1693 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1694 &li.continuous_snapping, TRUE
1698 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1699 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1703 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1704 &li.use_start_element[0], FALSE
1708 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1709 &li.start_element[0], EL_PLAYER_1
1713 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1714 &li.use_artwork_element[0], FALSE
1718 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1719 &li.artwork_element[0], EL_PLAYER_1
1723 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1724 &li.use_explosion_element[0], FALSE
1728 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1729 &li.explosion_element[0], EL_PLAYER_1
1744 filetype_id_list[] =
1746 { LEVEL_FILE_TYPE_RND, "RND" },
1747 { LEVEL_FILE_TYPE_BD, "BD" },
1748 { LEVEL_FILE_TYPE_EM, "EM" },
1749 { LEVEL_FILE_TYPE_SP, "SP" },
1750 { LEVEL_FILE_TYPE_DX, "DX" },
1751 { LEVEL_FILE_TYPE_SB, "SB" },
1752 { LEVEL_FILE_TYPE_DC, "DC" },
1753 { LEVEL_FILE_TYPE_MM, "MM" },
1754 { LEVEL_FILE_TYPE_MM, "DF" },
1759 // ============================================================================
1760 // level file functions
1761 // ============================================================================
1763 static boolean check_special_flags(char *flag)
1765 if (strEqual(options.special_flags, flag) ||
1766 strEqual(leveldir_current->special_flags, flag))
1772 static struct DateInfo getCurrentDate(void)
1774 time_t epoch_seconds = time(NULL);
1775 struct tm *now = localtime(&epoch_seconds);
1776 struct DateInfo date;
1778 date.year = now->tm_year + 1900;
1779 date.month = now->tm_mon + 1;
1780 date.day = now->tm_mday;
1782 date.src = DATE_SRC_CLOCK;
1787 static void resetEventFlags(struct ElementChangeInfo *change)
1791 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1792 change->has_event[i] = FALSE;
1795 static void resetEventBits(void)
1799 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1800 xx_event_bits[i] = 0;
1803 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1807 /* important: only change event flag if corresponding event bit is set
1808 (this is because all xx_event_bits[] values are loaded separately,
1809 and all xx_event_bits[] values are set back to zero before loading
1810 another value xx_event_bits[x] (each value representing 32 flags)) */
1812 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1813 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1814 change->has_event[i] = TRUE;
1817 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1821 /* in contrast to the above function setEventFlagsFromEventBits(), it
1822 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1823 depending on the corresponding change->has_event[i] values here, as
1824 all xx_event_bits[] values are reset in resetEventBits() before */
1826 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1827 if (change->has_event[i])
1828 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1831 static char *getDefaultElementDescription(struct ElementInfo *ei)
1833 static char description[MAX_ELEMENT_NAME_LEN + 1];
1834 char *default_description = (ei->custom_description != NULL ?
1835 ei->custom_description :
1836 ei->editor_description);
1839 // always start with reliable default values
1840 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1841 description[i] = '\0';
1843 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1844 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1846 return &description[0];
1849 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1851 char *default_description = getDefaultElementDescription(ei);
1854 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1855 ei->description[i] = default_description[i];
1858 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1862 for (i = 0; conf[i].data_type != -1; i++)
1864 int default_value = conf[i].default_value;
1865 int data_type = conf[i].data_type;
1866 int conf_type = conf[i].conf_type;
1867 int byte_mask = conf_type & CONF_MASK_BYTES;
1869 if (byte_mask == CONF_MASK_MULTI_BYTES)
1871 int default_num_entities = conf[i].default_num_entities;
1872 int max_num_entities = conf[i].max_num_entities;
1874 *(int *)(conf[i].num_entities) = default_num_entities;
1876 if (data_type == TYPE_STRING)
1878 char *default_string = conf[i].default_string;
1879 char *string = (char *)(conf[i].value);
1881 strncpy(string, default_string, max_num_entities);
1883 else if (data_type == TYPE_ELEMENT_LIST)
1885 int *element_array = (int *)(conf[i].value);
1888 for (j = 0; j < max_num_entities; j++)
1889 element_array[j] = default_value;
1891 else if (data_type == TYPE_CONTENT_LIST)
1893 struct Content *content = (struct Content *)(conf[i].value);
1896 for (c = 0; c < max_num_entities; c++)
1897 for (y = 0; y < 3; y++)
1898 for (x = 0; x < 3; x++)
1899 content[c].e[x][y] = default_value;
1902 else // constant size configuration data (1, 2 or 4 bytes)
1904 if (data_type == TYPE_BOOLEAN)
1905 *(boolean *)(conf[i].value) = default_value;
1907 *(int *) (conf[i].value) = default_value;
1912 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1916 for (i = 0; conf[i].data_type != -1; i++)
1918 int data_type = conf[i].data_type;
1919 int conf_type = conf[i].conf_type;
1920 int byte_mask = conf_type & CONF_MASK_BYTES;
1922 if (byte_mask == CONF_MASK_MULTI_BYTES)
1924 int max_num_entities = conf[i].max_num_entities;
1926 if (data_type == TYPE_STRING)
1928 char *string = (char *)(conf[i].value);
1929 char *string_copy = (char *)(conf[i].value_copy);
1931 strncpy(string_copy, string, max_num_entities);
1933 else if (data_type == TYPE_ELEMENT_LIST)
1935 int *element_array = (int *)(conf[i].value);
1936 int *element_array_copy = (int *)(conf[i].value_copy);
1939 for (j = 0; j < max_num_entities; j++)
1940 element_array_copy[j] = element_array[j];
1942 else if (data_type == TYPE_CONTENT_LIST)
1944 struct Content *content = (struct Content *)(conf[i].value);
1945 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1948 for (c = 0; c < max_num_entities; c++)
1949 for (y = 0; y < 3; y++)
1950 for (x = 0; x < 3; x++)
1951 content_copy[c].e[x][y] = content[c].e[x][y];
1954 else // constant size configuration data (1, 2 or 4 bytes)
1956 if (data_type == TYPE_BOOLEAN)
1957 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1959 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1964 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1968 xx_ei = *ei_from; // copy element data into temporary buffer
1969 yy_ei = *ei_to; // copy element data into temporary buffer
1971 copyConfigFromConfigList(chunk_config_CUSX_base);
1976 // ---------- reinitialize and copy change pages ----------
1978 ei_to->num_change_pages = ei_from->num_change_pages;
1979 ei_to->current_change_page = ei_from->current_change_page;
1981 setElementChangePages(ei_to, ei_to->num_change_pages);
1983 for (i = 0; i < ei_to->num_change_pages; i++)
1984 ei_to->change_page[i] = ei_from->change_page[i];
1986 // ---------- copy group element info ----------
1987 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1988 *ei_to->group = *ei_from->group;
1990 // mark this custom element as modified
1991 ei_to->modified_settings = TRUE;
1994 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1996 int change_page_size = sizeof(struct ElementChangeInfo);
1998 ei->num_change_pages = MAX(1, change_pages);
2001 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2003 if (ei->current_change_page >= ei->num_change_pages)
2004 ei->current_change_page = ei->num_change_pages - 1;
2006 ei->change = &ei->change_page[ei->current_change_page];
2009 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2011 xx_change = *change; // copy change data into temporary buffer
2013 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2015 *change = xx_change;
2017 resetEventFlags(change);
2019 change->direct_action = 0;
2020 change->other_action = 0;
2022 change->pre_change_function = NULL;
2023 change->change_function = NULL;
2024 change->post_change_function = NULL;
2027 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2031 li = *level; // copy level data into temporary buffer
2032 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2033 *level = li; // copy temporary buffer back to level data
2035 setLevelInfoToDefaults_BD();
2036 setLevelInfoToDefaults_EM();
2037 setLevelInfoToDefaults_SP();
2038 setLevelInfoToDefaults_MM();
2040 level->native_bd_level = &native_bd_level;
2041 level->native_em_level = &native_em_level;
2042 level->native_sp_level = &native_sp_level;
2043 level->native_mm_level = &native_mm_level;
2045 level->file_version = FILE_VERSION_ACTUAL;
2046 level->game_version = GAME_VERSION_ACTUAL;
2048 level->creation_date = getCurrentDate();
2050 level->encoding_16bit_field = TRUE;
2051 level->encoding_16bit_yamyam = TRUE;
2052 level->encoding_16bit_amoeba = TRUE;
2054 // clear level name and level author string buffers
2055 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2056 level->name[i] = '\0';
2057 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2058 level->author[i] = '\0';
2060 // set level name and level author to default values
2061 strcpy(level->name, NAMELESS_LEVEL_NAME);
2062 strcpy(level->author, ANONYMOUS_NAME);
2064 // set level playfield to playable default level with player and exit
2065 for (x = 0; x < MAX_LEV_FIELDX; x++)
2066 for (y = 0; y < MAX_LEV_FIELDY; y++)
2067 level->field[x][y] = EL_SAND;
2069 level->field[0][0] = EL_PLAYER_1;
2070 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2072 BorderElement = EL_STEELWALL;
2074 // detect custom elements when loading them
2075 level->file_has_custom_elements = FALSE;
2077 // set all bug compatibility flags to "false" => do not emulate this bug
2078 level->use_action_after_change_bug = FALSE;
2080 if (leveldir_current)
2082 // try to determine better author name than 'anonymous'
2083 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2085 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2086 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2090 switch (LEVELCLASS(leveldir_current))
2092 case LEVELCLASS_TUTORIAL:
2093 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2096 case LEVELCLASS_CONTRIB:
2097 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2098 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2101 case LEVELCLASS_PRIVATE:
2102 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2103 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2107 // keep default value
2114 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2116 static boolean clipboard_elements_initialized = FALSE;
2119 InitElementPropertiesStatic();
2121 li = *level; // copy level data into temporary buffer
2122 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2123 *level = li; // copy temporary buffer back to level data
2125 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2128 struct ElementInfo *ei = &element_info[element];
2130 if (element == EL_MM_GRAY_BALL)
2132 struct LevelInfo_MM *level_mm = level->native_mm_level;
2135 for (j = 0; j < level->num_mm_ball_contents; j++)
2136 level->mm_ball_content[j] =
2137 map_element_MM_to_RND(level_mm->ball_content[j]);
2140 // never initialize clipboard elements after the very first time
2141 // (to be able to use clipboard elements between several levels)
2142 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2145 if (IS_ENVELOPE(element))
2147 int envelope_nr = element - EL_ENVELOPE_1;
2149 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2151 level->envelope[envelope_nr] = xx_envelope;
2154 if (IS_CUSTOM_ELEMENT(element) ||
2155 IS_GROUP_ELEMENT(element) ||
2156 IS_INTERNAL_ELEMENT(element))
2158 xx_ei = *ei; // copy element data into temporary buffer
2160 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2165 setElementChangePages(ei, 1);
2166 setElementChangeInfoToDefaults(ei->change);
2168 if (IS_CUSTOM_ELEMENT(element) ||
2169 IS_GROUP_ELEMENT(element))
2171 setElementDescriptionToDefault(ei);
2173 ei->modified_settings = FALSE;
2176 if (IS_CUSTOM_ELEMENT(element) ||
2177 IS_INTERNAL_ELEMENT(element))
2179 // internal values used in level editor
2181 ei->access_type = 0;
2182 ei->access_layer = 0;
2183 ei->access_protected = 0;
2184 ei->walk_to_action = 0;
2185 ei->smash_targets = 0;
2188 ei->can_explode_by_fire = FALSE;
2189 ei->can_explode_smashed = FALSE;
2190 ei->can_explode_impact = FALSE;
2192 ei->current_change_page = 0;
2195 if (IS_GROUP_ELEMENT(element) ||
2196 IS_INTERNAL_ELEMENT(element))
2198 struct ElementGroupInfo *group;
2200 // initialize memory for list of elements in group
2201 if (ei->group == NULL)
2202 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2206 xx_group = *group; // copy group data into temporary buffer
2208 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2213 if (IS_EMPTY_ELEMENT(element) ||
2214 IS_INTERNAL_ELEMENT(element))
2216 xx_ei = *ei; // copy element data into temporary buffer
2218 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2224 clipboard_elements_initialized = TRUE;
2227 static void setLevelInfoToDefaults(struct LevelInfo *level,
2228 boolean level_info_only,
2229 boolean reset_file_status)
2231 setLevelInfoToDefaults_Level(level);
2233 if (!level_info_only)
2234 setLevelInfoToDefaults_Elements(level);
2236 if (reset_file_status)
2238 level->no_valid_file = FALSE;
2239 level->no_level_file = FALSE;
2242 level->changed = FALSE;
2245 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2247 level_file_info->nr = 0;
2248 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2249 level_file_info->packed = FALSE;
2251 setString(&level_file_info->basename, NULL);
2252 setString(&level_file_info->filename, NULL);
2255 int getMappedElement_SB(int, boolean);
2257 static void ActivateLevelTemplate(void)
2261 if (check_special_flags("load_xsb_to_ces"))
2263 // fill smaller playfields with padding "beyond border wall" elements
2264 if (level.fieldx < level_template.fieldx ||
2265 level.fieldy < level_template.fieldy)
2267 short field[level.fieldx][level.fieldy];
2268 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2269 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2270 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2271 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2273 // copy old playfield (which is smaller than the visible area)
2274 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2275 field[x][y] = level.field[x][y];
2277 // fill new, larger playfield with "beyond border wall" elements
2278 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2279 level.field[x][y] = getMappedElement_SB('_', TRUE);
2281 // copy the old playfield to the middle of the new playfield
2282 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2283 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2285 level.fieldx = new_fieldx;
2286 level.fieldy = new_fieldy;
2290 // Currently there is no special action needed to activate the template
2291 // data, because 'element_info' property settings overwrite the original
2292 // level data, while all other variables do not change.
2294 // Exception: 'from_level_template' elements in the original level playfield
2295 // are overwritten with the corresponding elements at the same position in
2296 // playfield from the level template.
2298 for (x = 0; x < level.fieldx; x++)
2299 for (y = 0; y < level.fieldy; y++)
2300 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2301 level.field[x][y] = level_template.field[x][y];
2303 if (check_special_flags("load_xsb_to_ces"))
2305 struct LevelInfo level_backup = level;
2307 // overwrite all individual level settings from template level settings
2308 level = level_template;
2310 // restore level file info
2311 level.file_info = level_backup.file_info;
2313 // restore playfield size
2314 level.fieldx = level_backup.fieldx;
2315 level.fieldy = level_backup.fieldy;
2317 // restore playfield content
2318 for (x = 0; x < level.fieldx; x++)
2319 for (y = 0; y < level.fieldy; y++)
2320 level.field[x][y] = level_backup.field[x][y];
2322 // restore name and author from individual level
2323 strcpy(level.name, level_backup.name);
2324 strcpy(level.author, level_backup.author);
2326 // restore flag "use_custom_template"
2327 level.use_custom_template = level_backup.use_custom_template;
2331 static boolean checkForPackageFromBasename_BD(char *basename)
2333 // check for native BD level file extensions
2334 if (!strSuffixLower(basename, ".bd") &&
2335 !strSuffixLower(basename, ".bdr") &&
2336 !strSuffixLower(basename, ".brc") &&
2337 !strSuffixLower(basename, ".gds"))
2340 // check for standard single-level BD files (like "001.bd")
2341 if (strSuffixLower(basename, ".bd") &&
2342 strlen(basename) == 6 &&
2343 basename[0] >= '0' && basename[0] <= '9' &&
2344 basename[1] >= '0' && basename[1] <= '9' &&
2345 basename[2] >= '0' && basename[2] <= '9')
2348 // this is a level package in native BD file format
2352 static char *getLevelFilenameFromBasename(char *basename)
2354 static char *filename = NULL;
2356 checked_free(filename);
2358 filename = getPath2(getCurrentLevelDir(), basename);
2363 static int getFileTypeFromBasename(char *basename)
2365 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2367 static char *filename = NULL;
2368 struct stat file_status;
2370 // ---------- try to determine file type from filename ----------
2372 // check for typical filename of a Supaplex level package file
2373 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2374 return LEVEL_FILE_TYPE_SP;
2376 // check for typical filename of a Diamond Caves II level package file
2377 if (strSuffixLower(basename, ".dc") ||
2378 strSuffixLower(basename, ".dc2"))
2379 return LEVEL_FILE_TYPE_DC;
2381 // check for typical filename of a Sokoban level package file
2382 if (strSuffixLower(basename, ".xsb") &&
2383 strchr(basename, '%') == NULL)
2384 return LEVEL_FILE_TYPE_SB;
2386 // check for typical filename of a Boulder Dash (GDash) level package file
2387 if (checkForPackageFromBasename_BD(basename))
2388 return LEVEL_FILE_TYPE_BD;
2390 // ---------- try to determine file type from filesize ----------
2392 checked_free(filename);
2393 filename = getPath2(getCurrentLevelDir(), basename);
2395 if (stat(filename, &file_status) == 0)
2397 // check for typical filesize of a Supaplex level package file
2398 if (file_status.st_size == 170496)
2399 return LEVEL_FILE_TYPE_SP;
2402 return LEVEL_FILE_TYPE_UNKNOWN;
2405 static int getFileTypeFromMagicBytes(char *filename, int type)
2409 if ((file = openFile(filename, MODE_READ)))
2411 char chunk_name[CHUNK_ID_LEN + 1];
2413 getFileChunkBE(file, chunk_name, NULL);
2415 if (strEqual(chunk_name, "MMII") ||
2416 strEqual(chunk_name, "MIRR"))
2417 type = LEVEL_FILE_TYPE_MM;
2425 static boolean checkForPackageFromBasename(char *basename)
2427 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2428 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2430 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2433 static char *getSingleLevelBasenameExt(int nr, char *extension)
2435 static char basename[MAX_FILENAME_LEN];
2438 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2440 sprintf(basename, "%03d.%s", nr, extension);
2445 static char *getSingleLevelBasename(int nr)
2447 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2450 static char *getPackedLevelBasename(int type)
2452 static char basename[MAX_FILENAME_LEN];
2453 char *directory = getCurrentLevelDir();
2455 DirectoryEntry *dir_entry;
2457 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2459 if ((dir = openDirectory(directory)) == NULL)
2461 Warn("cannot read current level directory '%s'", directory);
2466 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2468 char *entry_basename = dir_entry->basename;
2469 int entry_type = getFileTypeFromBasename(entry_basename);
2471 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2473 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2476 strcpy(basename, entry_basename);
2483 closeDirectory(dir);
2488 static char *getSingleLevelFilename(int nr)
2490 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2493 #if ENABLE_UNUSED_CODE
2494 static char *getPackedLevelFilename(int type)
2496 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2500 char *getDefaultLevelFilename(int nr)
2502 return getSingleLevelFilename(nr);
2505 #if ENABLE_UNUSED_CODE
2506 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2510 lfi->packed = FALSE;
2512 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2513 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2517 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2518 int type, char *format, ...)
2520 static char basename[MAX_FILENAME_LEN];
2523 va_start(ap, format);
2524 vsprintf(basename, format, ap);
2528 lfi->packed = FALSE;
2530 setString(&lfi->basename, basename);
2531 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2534 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2540 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2541 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2544 static int getFiletypeFromID(char *filetype_id)
2546 char *filetype_id_lower;
2547 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2550 if (filetype_id == NULL)
2551 return LEVEL_FILE_TYPE_UNKNOWN;
2553 filetype_id_lower = getStringToLower(filetype_id);
2555 for (i = 0; filetype_id_list[i].id != NULL; i++)
2557 char *id_lower = getStringToLower(filetype_id_list[i].id);
2559 if (strEqual(filetype_id_lower, id_lower))
2560 filetype = filetype_id_list[i].filetype;
2564 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2568 free(filetype_id_lower);
2573 char *getLocalLevelTemplateFilename(void)
2575 return getDefaultLevelFilename(-1);
2578 char *getGlobalLevelTemplateFilename(void)
2580 // global variable "leveldir_current" must be modified in the loop below
2581 LevelDirTree *leveldir_current_last = leveldir_current;
2582 char *filename = NULL;
2584 // check for template level in path from current to topmost tree node
2586 while (leveldir_current != NULL)
2588 filename = getDefaultLevelFilename(-1);
2590 if (fileExists(filename))
2593 leveldir_current = leveldir_current->node_parent;
2596 // restore global variable "leveldir_current" modified in above loop
2597 leveldir_current = leveldir_current_last;
2602 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2606 // special case: level number is negative => check for level template file
2609 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2610 getSingleLevelBasename(-1));
2612 // replace local level template filename with global template filename
2613 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2615 // no fallback if template file not existing
2619 // special case: check for file name/pattern specified in "levelinfo.conf"
2620 if (leveldir_current->level_filename != NULL)
2622 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2624 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2625 leveldir_current->level_filename, nr);
2627 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2629 if (fileExists(lfi->filename))
2632 else if (leveldir_current->level_filetype != NULL)
2634 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2636 // check for specified native level file with standard file name
2637 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2638 "%03d.%s", nr, LEVELFILE_EXTENSION);
2639 if (fileExists(lfi->filename))
2643 // check for native Rocks'n'Diamonds level file
2644 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2645 "%03d.%s", nr, LEVELFILE_EXTENSION);
2646 if (fileExists(lfi->filename))
2649 // check for native Boulder Dash level file
2650 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2651 if (fileExists(lfi->filename))
2654 // check for Emerald Mine level file (V1)
2655 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2656 'a' + (nr / 10) % 26, '0' + nr % 10);
2657 if (fileExists(lfi->filename))
2659 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2660 'A' + (nr / 10) % 26, '0' + nr % 10);
2661 if (fileExists(lfi->filename))
2664 // check for Emerald Mine level file (V2 to V5)
2665 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2666 if (fileExists(lfi->filename))
2669 // check for Emerald Mine level file (V6 / single mode)
2670 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2671 if (fileExists(lfi->filename))
2673 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2674 if (fileExists(lfi->filename))
2677 // check for Emerald Mine level file (V6 / teamwork mode)
2678 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2679 if (fileExists(lfi->filename))
2681 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2682 if (fileExists(lfi->filename))
2685 // check for various packed level file formats
2686 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2687 if (fileExists(lfi->filename))
2690 // no known level file found -- use default values (and fail later)
2691 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2692 "%03d.%s", nr, LEVELFILE_EXTENSION);
2695 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2697 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2698 lfi->type = getFileTypeFromBasename(lfi->basename);
2700 if (lfi->type == LEVEL_FILE_TYPE_RND)
2701 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2704 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2706 // always start with reliable default values
2707 setFileInfoToDefaults(level_file_info);
2709 level_file_info->nr = nr; // set requested level number
2711 determineLevelFileInfo_Filename(level_file_info);
2712 determineLevelFileInfo_Filetype(level_file_info);
2715 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2716 struct LevelFileInfo *lfi_to)
2718 lfi_to->nr = lfi_from->nr;
2719 lfi_to->type = lfi_from->type;
2720 lfi_to->packed = lfi_from->packed;
2722 setString(&lfi_to->basename, lfi_from->basename);
2723 setString(&lfi_to->filename, lfi_from->filename);
2726 // ----------------------------------------------------------------------------
2727 // functions for loading R'n'D level
2728 // ----------------------------------------------------------------------------
2730 int getMappedElement(int element)
2732 // remap some (historic, now obsolete) elements
2736 case EL_PLAYER_OBSOLETE:
2737 element = EL_PLAYER_1;
2740 case EL_KEY_OBSOLETE:
2744 case EL_EM_KEY_1_FILE_OBSOLETE:
2745 element = EL_EM_KEY_1;
2748 case EL_EM_KEY_2_FILE_OBSOLETE:
2749 element = EL_EM_KEY_2;
2752 case EL_EM_KEY_3_FILE_OBSOLETE:
2753 element = EL_EM_KEY_3;
2756 case EL_EM_KEY_4_FILE_OBSOLETE:
2757 element = EL_EM_KEY_4;
2760 case EL_ENVELOPE_OBSOLETE:
2761 element = EL_ENVELOPE_1;
2769 if (element >= NUM_FILE_ELEMENTS)
2771 Warn("invalid level element %d", element);
2773 element = EL_UNKNOWN;
2781 static int getMappedElementByVersion(int element, int game_version)
2783 // remap some elements due to certain game version
2785 if (game_version <= VERSION_IDENT(2,2,0,0))
2787 // map game font elements
2788 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2789 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2790 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2791 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2794 if (game_version < VERSION_IDENT(3,0,0,0))
2796 // map Supaplex gravity tube elements
2797 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2798 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2799 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2800 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2807 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2809 level->file_version = getFileVersion(file);
2810 level->game_version = getFileVersion(file);
2815 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2817 level->creation_date.year = getFile16BitBE(file);
2818 level->creation_date.month = getFile8Bit(file);
2819 level->creation_date.day = getFile8Bit(file);
2821 level->creation_date.src = DATE_SRC_LEVELFILE;
2826 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2828 int initial_player_stepsize;
2829 int initial_player_gravity;
2832 level->fieldx = getFile8Bit(file);
2833 level->fieldy = getFile8Bit(file);
2835 level->time = getFile16BitBE(file);
2836 level->gems_needed = getFile16BitBE(file);
2838 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2839 level->name[i] = getFile8Bit(file);
2840 level->name[MAX_LEVEL_NAME_LEN] = 0;
2842 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2843 level->score[i] = getFile8Bit(file);
2845 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2846 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2847 for (y = 0; y < 3; y++)
2848 for (x = 0; x < 3; x++)
2849 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2851 level->amoeba_speed = getFile8Bit(file);
2852 level->time_magic_wall = getFile8Bit(file);
2853 level->time_wheel = getFile8Bit(file);
2854 level->amoeba_content = getMappedElement(getFile8Bit(file));
2856 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2859 for (i = 0; i < MAX_PLAYERS; i++)
2860 level->initial_player_stepsize[i] = initial_player_stepsize;
2862 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2864 for (i = 0; i < MAX_PLAYERS; i++)
2865 level->initial_player_gravity[i] = initial_player_gravity;
2867 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2868 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2870 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2872 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2873 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2874 level->can_move_into_acid_bits = getFile32BitBE(file);
2875 level->dont_collide_with_bits = getFile8Bit(file);
2877 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2878 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2880 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2881 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2882 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2884 level->game_engine_type = getFile8Bit(file);
2886 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2891 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2895 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2896 level->name[i] = getFile8Bit(file);
2897 level->name[MAX_LEVEL_NAME_LEN] = 0;
2902 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2906 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2907 level->author[i] = getFile8Bit(file);
2908 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2913 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2916 int chunk_size_expected = level->fieldx * level->fieldy;
2918 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2919 stored with 16-bit encoding (and should be twice as big then).
2920 Even worse, playfield data was stored 16-bit when only yamyam content
2921 contained 16-bit elements and vice versa. */
2923 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2924 chunk_size_expected *= 2;
2926 if (chunk_size_expected != chunk_size)
2928 ReadUnusedBytesFromFile(file, chunk_size);
2929 return chunk_size_expected;
2932 for (y = 0; y < level->fieldy; y++)
2933 for (x = 0; x < level->fieldx; x++)
2934 level->field[x][y] =
2935 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2940 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2943 int header_size = 4;
2944 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2945 int chunk_size_expected = header_size + content_size;
2947 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2948 stored with 16-bit encoding (and should be twice as big then).
2949 Even worse, playfield data was stored 16-bit when only yamyam content
2950 contained 16-bit elements and vice versa. */
2952 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2953 chunk_size_expected += content_size;
2955 if (chunk_size_expected != chunk_size)
2957 ReadUnusedBytesFromFile(file, chunk_size);
2958 return chunk_size_expected;
2962 level->num_yamyam_contents = getFile8Bit(file);
2966 // correct invalid number of content fields -- should never happen
2967 if (level->num_yamyam_contents < 1 ||
2968 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2969 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2971 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2972 for (y = 0; y < 3; y++)
2973 for (x = 0; x < 3; x++)
2974 level->yamyam_content[i].e[x][y] =
2975 getMappedElement(level->encoding_16bit_field ?
2976 getFile16BitBE(file) : getFile8Bit(file));
2980 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2985 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2987 element = getMappedElement(getFile16BitBE(file));
2988 num_contents = getFile8Bit(file);
2990 getFile8Bit(file); // content x size (unused)
2991 getFile8Bit(file); // content y size (unused)
2993 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2995 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2996 for (y = 0; y < 3; y++)
2997 for (x = 0; x < 3; x++)
2998 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3000 // correct invalid number of content fields -- should never happen
3001 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3002 num_contents = STD_ELEMENT_CONTENTS;
3004 if (element == EL_YAMYAM)
3006 level->num_yamyam_contents = num_contents;
3008 for (i = 0; i < num_contents; i++)
3009 for (y = 0; y < 3; y++)
3010 for (x = 0; x < 3; x++)
3011 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3013 else if (element == EL_BD_AMOEBA)
3015 level->amoeba_content = content_array[0][0][0];
3019 Warn("cannot load content for element '%d'", element);
3025 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3031 int chunk_size_expected;
3033 element = getMappedElement(getFile16BitBE(file));
3034 if (!IS_ENVELOPE(element))
3035 element = EL_ENVELOPE_1;
3037 envelope_nr = element - EL_ENVELOPE_1;
3039 envelope_len = getFile16BitBE(file);
3041 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3042 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3044 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3046 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3047 if (chunk_size_expected != chunk_size)
3049 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3050 return chunk_size_expected;
3053 for (i = 0; i < envelope_len; i++)
3054 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3059 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3061 int num_changed_custom_elements = getFile16BitBE(file);
3062 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3065 if (chunk_size_expected != chunk_size)
3067 ReadUnusedBytesFromFile(file, chunk_size - 2);
3068 return chunk_size_expected;
3071 for (i = 0; i < num_changed_custom_elements; i++)
3073 int element = getMappedElement(getFile16BitBE(file));
3074 int properties = getFile32BitBE(file);
3076 if (IS_CUSTOM_ELEMENT(element))
3077 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3079 Warn("invalid custom element number %d", element);
3081 // older game versions that wrote level files with CUS1 chunks used
3082 // different default push delay values (not yet stored in level file)
3083 element_info[element].push_delay_fixed = 2;
3084 element_info[element].push_delay_random = 8;
3087 level->file_has_custom_elements = TRUE;
3092 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3094 int num_changed_custom_elements = getFile16BitBE(file);
3095 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3098 if (chunk_size_expected != chunk_size)
3100 ReadUnusedBytesFromFile(file, chunk_size - 2);
3101 return chunk_size_expected;
3104 for (i = 0; i < num_changed_custom_elements; i++)
3106 int element = getMappedElement(getFile16BitBE(file));
3107 int custom_target_element = getMappedElement(getFile16BitBE(file));
3109 if (IS_CUSTOM_ELEMENT(element))
3110 element_info[element].change->target_element = custom_target_element;
3112 Warn("invalid custom element number %d", element);
3115 level->file_has_custom_elements = TRUE;
3120 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3122 int num_changed_custom_elements = getFile16BitBE(file);
3123 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3126 if (chunk_size_expected != chunk_size)
3128 ReadUnusedBytesFromFile(file, chunk_size - 2);
3129 return chunk_size_expected;
3132 for (i = 0; i < num_changed_custom_elements; i++)
3134 int element = getMappedElement(getFile16BitBE(file));
3135 struct ElementInfo *ei = &element_info[element];
3136 unsigned int event_bits;
3138 if (!IS_CUSTOM_ELEMENT(element))
3140 Warn("invalid custom element number %d", element);
3142 element = EL_INTERNAL_DUMMY;
3145 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3146 ei->description[j] = getFile8Bit(file);
3147 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3149 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3151 // some free bytes for future properties and padding
3152 ReadUnusedBytesFromFile(file, 7);
3154 ei->use_gfx_element = getFile8Bit(file);
3155 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3157 ei->collect_score_initial = getFile8Bit(file);
3158 ei->collect_count_initial = getFile8Bit(file);
3160 ei->push_delay_fixed = getFile16BitBE(file);
3161 ei->push_delay_random = getFile16BitBE(file);
3162 ei->move_delay_fixed = getFile16BitBE(file);
3163 ei->move_delay_random = getFile16BitBE(file);
3165 ei->move_pattern = getFile16BitBE(file);
3166 ei->move_direction_initial = getFile8Bit(file);
3167 ei->move_stepsize = getFile8Bit(file);
3169 for (y = 0; y < 3; y++)
3170 for (x = 0; x < 3; x++)
3171 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3173 // bits 0 - 31 of "has_event[]"
3174 event_bits = getFile32BitBE(file);
3175 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3176 if (event_bits & (1u << j))
3177 ei->change->has_event[j] = TRUE;
3179 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3181 ei->change->delay_fixed = getFile16BitBE(file);
3182 ei->change->delay_random = getFile16BitBE(file);
3183 ei->change->delay_frames = getFile16BitBE(file);
3185 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3187 ei->change->explode = getFile8Bit(file);
3188 ei->change->use_target_content = getFile8Bit(file);
3189 ei->change->only_if_complete = getFile8Bit(file);
3190 ei->change->use_random_replace = getFile8Bit(file);
3192 ei->change->random_percentage = getFile8Bit(file);
3193 ei->change->replace_when = getFile8Bit(file);
3195 for (y = 0; y < 3; y++)
3196 for (x = 0; x < 3; x++)
3197 ei->change->target_content.e[x][y] =
3198 getMappedElement(getFile16BitBE(file));
3200 ei->slippery_type = getFile8Bit(file);
3202 // some free bytes for future properties and padding
3203 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3205 // mark that this custom element has been modified
3206 ei->modified_settings = TRUE;
3209 level->file_has_custom_elements = TRUE;
3214 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3216 struct ElementInfo *ei;
3217 int chunk_size_expected;
3221 // ---------- custom element base property values (96 bytes) ----------------
3223 element = getMappedElement(getFile16BitBE(file));
3225 if (!IS_CUSTOM_ELEMENT(element))
3227 Warn("invalid custom element number %d", element);
3229 ReadUnusedBytesFromFile(file, chunk_size - 2);
3234 ei = &element_info[element];
3236 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3237 ei->description[i] = getFile8Bit(file);
3238 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3240 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3242 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3244 ei->num_change_pages = getFile8Bit(file);
3246 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3247 if (chunk_size_expected != chunk_size)
3249 ReadUnusedBytesFromFile(file, chunk_size - 43);
3250 return chunk_size_expected;
3253 ei->ce_value_fixed_initial = getFile16BitBE(file);
3254 ei->ce_value_random_initial = getFile16BitBE(file);
3255 ei->use_last_ce_value = getFile8Bit(file);
3257 ei->use_gfx_element = getFile8Bit(file);
3258 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3260 ei->collect_score_initial = getFile8Bit(file);
3261 ei->collect_count_initial = getFile8Bit(file);
3263 ei->drop_delay_fixed = getFile8Bit(file);
3264 ei->push_delay_fixed = getFile8Bit(file);
3265 ei->drop_delay_random = getFile8Bit(file);
3266 ei->push_delay_random = getFile8Bit(file);
3267 ei->move_delay_fixed = getFile16BitBE(file);
3268 ei->move_delay_random = getFile16BitBE(file);
3270 // bits 0 - 15 of "move_pattern" ...
3271 ei->move_pattern = getFile16BitBE(file);
3272 ei->move_direction_initial = getFile8Bit(file);
3273 ei->move_stepsize = getFile8Bit(file);
3275 ei->slippery_type = getFile8Bit(file);
3277 for (y = 0; y < 3; y++)
3278 for (x = 0; x < 3; x++)
3279 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3281 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3282 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3283 ei->move_leave_type = getFile8Bit(file);
3285 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3286 ei->move_pattern |= (getFile16BitBE(file) << 16);
3288 ei->access_direction = getFile8Bit(file);
3290 ei->explosion_delay = getFile8Bit(file);
3291 ei->ignition_delay = getFile8Bit(file);
3292 ei->explosion_type = getFile8Bit(file);
3294 // some free bytes for future custom property values and padding
3295 ReadUnusedBytesFromFile(file, 1);
3297 // ---------- change page property values (48 bytes) ------------------------
3299 setElementChangePages(ei, ei->num_change_pages);
3301 for (i = 0; i < ei->num_change_pages; i++)
3303 struct ElementChangeInfo *change = &ei->change_page[i];
3304 unsigned int event_bits;
3306 // always start with reliable default values
3307 setElementChangeInfoToDefaults(change);
3309 // bits 0 - 31 of "has_event[]" ...
3310 event_bits = getFile32BitBE(file);
3311 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3312 if (event_bits & (1u << j))
3313 change->has_event[j] = TRUE;
3315 change->target_element = getMappedElement(getFile16BitBE(file));
3317 change->delay_fixed = getFile16BitBE(file);
3318 change->delay_random = getFile16BitBE(file);
3319 change->delay_frames = getFile16BitBE(file);
3321 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3323 change->explode = getFile8Bit(file);
3324 change->use_target_content = getFile8Bit(file);
3325 change->only_if_complete = getFile8Bit(file);
3326 change->use_random_replace = getFile8Bit(file);
3328 change->random_percentage = getFile8Bit(file);
3329 change->replace_when = getFile8Bit(file);
3331 for (y = 0; y < 3; y++)
3332 for (x = 0; x < 3; x++)
3333 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3335 change->can_change = getFile8Bit(file);
3337 change->trigger_side = getFile8Bit(file);
3339 change->trigger_player = getFile8Bit(file);
3340 change->trigger_page = getFile8Bit(file);
3342 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3343 CH_PAGE_ANY : (1 << change->trigger_page));
3345 change->has_action = getFile8Bit(file);
3346 change->action_type = getFile8Bit(file);
3347 change->action_mode = getFile8Bit(file);
3348 change->action_arg = getFile16BitBE(file);
3350 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3351 event_bits = getFile8Bit(file);
3352 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3353 if (event_bits & (1u << (j - 32)))
3354 change->has_event[j] = TRUE;
3357 // mark this custom element as modified
3358 ei->modified_settings = TRUE;
3360 level->file_has_custom_elements = TRUE;
3365 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3367 struct ElementInfo *ei;
3368 struct ElementGroupInfo *group;
3372 element = getMappedElement(getFile16BitBE(file));
3374 if (!IS_GROUP_ELEMENT(element))
3376 Warn("invalid group element number %d", element);
3378 ReadUnusedBytesFromFile(file, chunk_size - 2);
3383 ei = &element_info[element];
3385 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3386 ei->description[i] = getFile8Bit(file);
3387 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3389 group = element_info[element].group;
3391 group->num_elements = getFile8Bit(file);
3393 ei->use_gfx_element = getFile8Bit(file);
3394 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3396 group->choice_mode = getFile8Bit(file);
3398 // some free bytes for future values and padding
3399 ReadUnusedBytesFromFile(file, 3);
3401 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3402 group->element[i] = getMappedElement(getFile16BitBE(file));
3404 // mark this group element as modified
3405 element_info[element].modified_settings = TRUE;
3407 level->file_has_custom_elements = TRUE;
3412 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3413 int element, int real_element)
3415 int micro_chunk_size = 0;
3416 int conf_type = getFile8Bit(file);
3417 int byte_mask = conf_type & CONF_MASK_BYTES;
3418 boolean element_found = FALSE;
3421 micro_chunk_size += 1;
3423 if (byte_mask == CONF_MASK_MULTI_BYTES)
3425 int num_bytes = getFile16BitBE(file);
3426 byte *buffer = checked_malloc(num_bytes);
3428 ReadBytesFromFile(file, buffer, num_bytes);
3430 for (i = 0; conf[i].data_type != -1; i++)
3432 if (conf[i].element == element &&
3433 conf[i].conf_type == conf_type)
3435 int data_type = conf[i].data_type;
3436 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3437 int max_num_entities = conf[i].max_num_entities;
3439 if (num_entities > max_num_entities)
3441 Warn("truncating number of entities for element %d from %d to %d",
3442 element, num_entities, max_num_entities);
3444 num_entities = max_num_entities;
3447 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3448 data_type == TYPE_CONTENT_LIST))
3450 // for element and content lists, zero entities are not allowed
3451 Warn("found empty list of entities for element %d", element);
3453 // do not set "num_entities" here to prevent reading behind buffer
3455 *(int *)(conf[i].num_entities) = 1; // at least one is required
3459 *(int *)(conf[i].num_entities) = num_entities;
3462 element_found = TRUE;
3464 if (data_type == TYPE_STRING)
3466 char *string = (char *)(conf[i].value);
3469 for (j = 0; j < max_num_entities; j++)
3470 string[j] = (j < num_entities ? buffer[j] : '\0');
3472 else if (data_type == TYPE_ELEMENT_LIST)
3474 int *element_array = (int *)(conf[i].value);
3477 for (j = 0; j < num_entities; j++)
3479 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3481 else if (data_type == TYPE_CONTENT_LIST)
3483 struct Content *content= (struct Content *)(conf[i].value);
3486 for (c = 0; c < num_entities; c++)
3487 for (y = 0; y < 3; y++)
3488 for (x = 0; x < 3; x++)
3489 content[c].e[x][y] =
3490 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3493 element_found = FALSE;
3499 checked_free(buffer);
3501 micro_chunk_size += 2 + num_bytes;
3503 else // constant size configuration data (1, 2 or 4 bytes)
3505 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3506 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3507 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3509 for (i = 0; conf[i].data_type != -1; i++)
3511 if (conf[i].element == element &&
3512 conf[i].conf_type == conf_type)
3514 int data_type = conf[i].data_type;
3516 if (data_type == TYPE_ELEMENT)
3517 value = getMappedElement(value);
3519 if (data_type == TYPE_BOOLEAN)
3520 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3522 *(int *) (conf[i].value) = value;
3524 element_found = TRUE;
3530 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3535 char *error_conf_chunk_bytes =
3536 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3537 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3538 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3539 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3540 int error_element = real_element;
3542 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3543 error_conf_chunk_bytes, error_conf_chunk_token,
3544 error_element, EL_NAME(error_element));
3547 return micro_chunk_size;
3550 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3552 int real_chunk_size = 0;
3554 li = *level; // copy level data into temporary buffer
3556 while (!checkEndOfFile(file))
3558 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3560 if (real_chunk_size >= chunk_size)
3564 *level = li; // copy temporary buffer back to level data
3566 return real_chunk_size;
3569 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3571 int real_chunk_size = 0;
3573 li = *level; // copy level data into temporary buffer
3575 while (!checkEndOfFile(file))
3577 int element = getMappedElement(getFile16BitBE(file));
3579 real_chunk_size += 2;
3580 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3582 if (real_chunk_size >= chunk_size)
3586 *level = li; // copy temporary buffer back to level data
3588 return real_chunk_size;
3591 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3593 int real_chunk_size = 0;
3595 li = *level; // copy level data into temporary buffer
3597 while (!checkEndOfFile(file))
3599 int element = getMappedElement(getFile16BitBE(file));
3601 real_chunk_size += 2;
3602 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3604 if (real_chunk_size >= chunk_size)
3608 *level = li; // copy temporary buffer back to level data
3610 return real_chunk_size;
3613 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3615 int element = getMappedElement(getFile16BitBE(file));
3616 int envelope_nr = element - EL_ENVELOPE_1;
3617 int real_chunk_size = 2;
3619 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3621 while (!checkEndOfFile(file))
3623 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3626 if (real_chunk_size >= chunk_size)
3630 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3632 return real_chunk_size;
3635 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3637 int element = getMappedElement(getFile16BitBE(file));
3638 int real_chunk_size = 2;
3639 struct ElementInfo *ei = &element_info[element];
3642 xx_ei = *ei; // copy element data into temporary buffer
3644 xx_ei.num_change_pages = -1;
3646 while (!checkEndOfFile(file))
3648 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3650 if (xx_ei.num_change_pages != -1)
3653 if (real_chunk_size >= chunk_size)
3659 if (ei->num_change_pages == -1)
3661 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3664 ei->num_change_pages = 1;
3666 setElementChangePages(ei, 1);
3667 setElementChangeInfoToDefaults(ei->change);
3669 return real_chunk_size;
3672 // initialize number of change pages stored for this custom element
3673 setElementChangePages(ei, ei->num_change_pages);
3674 for (i = 0; i < ei->num_change_pages; i++)
3675 setElementChangeInfoToDefaults(&ei->change_page[i]);
3677 // start with reading properties for the first change page
3678 xx_current_change_page = 0;
3680 while (!checkEndOfFile(file))
3682 // level file might contain invalid change page number
3683 if (xx_current_change_page >= ei->num_change_pages)
3686 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3688 xx_change = *change; // copy change data into temporary buffer
3690 resetEventBits(); // reset bits; change page might have changed
3692 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3695 *change = xx_change;
3697 setEventFlagsFromEventBits(change);
3699 if (real_chunk_size >= chunk_size)
3703 level->file_has_custom_elements = TRUE;
3705 return real_chunk_size;
3708 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3710 int element = getMappedElement(getFile16BitBE(file));
3711 int real_chunk_size = 2;
3712 struct ElementInfo *ei = &element_info[element];
3713 struct ElementGroupInfo *group = ei->group;
3718 xx_ei = *ei; // copy element data into temporary buffer
3719 xx_group = *group; // copy group data into temporary buffer
3721 while (!checkEndOfFile(file))
3723 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3726 if (real_chunk_size >= chunk_size)
3733 level->file_has_custom_elements = TRUE;
3735 return real_chunk_size;
3738 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3740 int element = getMappedElement(getFile16BitBE(file));
3741 int real_chunk_size = 2;
3742 struct ElementInfo *ei = &element_info[element];
3744 xx_ei = *ei; // copy element data into temporary buffer
3746 while (!checkEndOfFile(file))
3748 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3751 if (real_chunk_size >= chunk_size)
3757 level->file_has_custom_elements = TRUE;
3759 return real_chunk_size;
3762 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3763 struct LevelFileInfo *level_file_info,
3764 boolean level_info_only)
3766 char *filename = level_file_info->filename;
3767 char cookie[MAX_LINE_LEN];
3768 char chunk_name[CHUNK_ID_LEN + 1];
3772 if (!(file = openFile(filename, MODE_READ)))
3774 level->no_valid_file = TRUE;
3775 level->no_level_file = TRUE;
3777 if (level_info_only)
3780 Warn("cannot read level '%s' -- using empty level", filename);
3782 if (!setup.editor.use_template_for_new_levels)
3785 // if level file not found, try to initialize level data from template
3786 filename = getGlobalLevelTemplateFilename();
3788 if (!(file = openFile(filename, MODE_READ)))
3791 // default: for empty levels, use level template for custom elements
3792 level->use_custom_template = TRUE;
3794 level->no_valid_file = FALSE;
3797 getFileChunkBE(file, chunk_name, NULL);
3798 if (strEqual(chunk_name, "RND1"))
3800 getFile32BitBE(file); // not used
3802 getFileChunkBE(file, chunk_name, NULL);
3803 if (!strEqual(chunk_name, "CAVE"))
3805 level->no_valid_file = TRUE;
3807 Warn("unknown format of level file '%s'", filename);
3814 else // check for pre-2.0 file format with cookie string
3816 strcpy(cookie, chunk_name);
3817 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3819 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3820 cookie[strlen(cookie) - 1] = '\0';
3822 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3824 level->no_valid_file = TRUE;
3826 Warn("unknown format of level file '%s'", filename);
3833 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3835 level->no_valid_file = TRUE;
3837 Warn("unsupported version of level file '%s'", filename);
3844 // pre-2.0 level files have no game version, so use file version here
3845 level->game_version = level->file_version;
3848 if (level->file_version < FILE_VERSION_1_2)
3850 // level files from versions before 1.2.0 without chunk structure
3851 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3852 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3860 int (*loader)(File *, int, struct LevelInfo *);
3864 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3865 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3866 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3867 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3868 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3869 { "INFO", -1, LoadLevel_INFO },
3870 { "BODY", -1, LoadLevel_BODY },
3871 { "CONT", -1, LoadLevel_CONT },
3872 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3873 { "CNT3", -1, LoadLevel_CNT3 },
3874 { "CUS1", -1, LoadLevel_CUS1 },
3875 { "CUS2", -1, LoadLevel_CUS2 },
3876 { "CUS3", -1, LoadLevel_CUS3 },
3877 { "CUS4", -1, LoadLevel_CUS4 },
3878 { "GRP1", -1, LoadLevel_GRP1 },
3879 { "CONF", -1, LoadLevel_CONF },
3880 { "ELEM", -1, LoadLevel_ELEM },
3881 { "NOTE", -1, LoadLevel_NOTE },
3882 { "CUSX", -1, LoadLevel_CUSX },
3883 { "GRPX", -1, LoadLevel_GRPX },
3884 { "EMPX", -1, LoadLevel_EMPX },
3889 while (getFileChunkBE(file, chunk_name, &chunk_size))
3893 while (chunk_info[i].name != NULL &&
3894 !strEqual(chunk_name, chunk_info[i].name))
3897 if (chunk_info[i].name == NULL)
3899 Warn("unknown chunk '%s' in level file '%s'",
3900 chunk_name, filename);
3902 ReadUnusedBytesFromFile(file, chunk_size);
3904 else if (chunk_info[i].size != -1 &&
3905 chunk_info[i].size != chunk_size)
3907 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3908 chunk_size, chunk_name, filename);
3910 ReadUnusedBytesFromFile(file, chunk_size);
3914 // call function to load this level chunk
3915 int chunk_size_expected =
3916 (chunk_info[i].loader)(file, chunk_size, level);
3918 if (chunk_size_expected < 0)
3920 Warn("error reading chunk '%s' in level file '%s'",
3921 chunk_name, filename);
3926 // the size of some chunks cannot be checked before reading other
3927 // chunks first (like "HEAD" and "BODY") that contain some header
3928 // information, so check them here
3929 if (chunk_size_expected != chunk_size)
3931 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3932 chunk_size, chunk_name, filename);
3944 // ----------------------------------------------------------------------------
3945 // functions for loading BD level
3946 // ----------------------------------------------------------------------------
3948 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3950 struct LevelInfo_BD *level_bd = level->native_bd_level;
3951 GdCave *cave = NULL; // will be changed below
3952 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3953 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3956 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3958 // cave and map newly allocated when set to defaults above
3959 cave = level_bd->cave;
3962 cave->intermission = level->bd_intermission;
3965 cave->level_time[0] = level->time;
3966 cave->level_diamonds[0] = level->gems_needed;
3969 cave->scheduling = level->bd_scheduling_type;
3970 cave->pal_timing = level->bd_pal_timing;
3971 cave->level_speed[0] = level->bd_cycle_delay_ms;
3972 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3973 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3974 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3977 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3978 cave->diamond_value = level->score[SC_EMERALD];
3979 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3981 // compatibility settings
3982 cave->lineshift = level->bd_line_shifting_borders;
3983 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3984 cave->short_explosions = level->bd_short_explosions;
3985 cave->gravity_affects_all = level->bd_gravity_affects_all;
3987 // player properties
3988 cave->diagonal_movements = level->bd_diagonal_movements;
3989 cave->active_is_first_found = level->bd_topmost_player_active;
3990 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3991 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3992 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3993 cave->snap_element = map_element_RND_to_BD(level->bd_snap_element);
3995 // element properties
3996 cave->level_bonus_time[0] = level->bd_clock_extra_time;
3997 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
3998 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
3999 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4000 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4001 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4002 cave->level_magic_wall_time[0] = level->time_magic_wall;
4003 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4004 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4005 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4006 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4007 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4008 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4009 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4010 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4011 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4012 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4013 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4014 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4015 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4017 cave->amoeba_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
4018 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
4019 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
4020 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
4021 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
4022 cave->amoeba_2_looks_like = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
4024 cave->slime_predictable = level->bd_slime_is_predictable;
4025 cave->slime_correct_random = level->bd_slime_correct_random;
4026 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4027 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4028 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4029 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4031 cave->acid_eats_this = map_element_RND_to_BD(level->bd_acid_eats_element);
4032 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4033 cave->acid_turns_to = map_element_RND_to_BD(level->bd_acid_turns_to_element);
4035 cave->biter_delay_frame = level->bd_biter_move_delay;
4036 cave->biter_eat = map_element_RND_to_BD(level->bd_biter_eats_element);
4038 cave->bladder_converts_by = map_element_RND_to_BD(level->bd_bladder_converts_by_element);
4041 strncpy(cave->name, level->name, sizeof(GdString));
4042 cave->name[sizeof(GdString) - 1] = '\0';
4044 // playfield elements
4045 for (x = 0; x < cave->w; x++)
4046 for (y = 0; y < cave->h; y++)
4047 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
4050 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4052 struct LevelInfo_BD *level_bd = level->native_bd_level;
4053 GdCave *cave = level_bd->cave;
4054 int bd_level_nr = level_bd->level_nr;
4057 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4058 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4061 level->bd_intermission = cave->intermission;
4064 level->time = cave->level_time[bd_level_nr];
4065 level->gems_needed = cave->level_diamonds[bd_level_nr];
4068 level->bd_scheduling_type = cave->scheduling;
4069 level->bd_pal_timing = cave->pal_timing;
4070 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4071 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4072 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4073 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4076 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4077 level->score[SC_EMERALD] = cave->diamond_value;
4078 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4080 // compatibility settings
4081 level->bd_line_shifting_borders = cave->lineshift;
4082 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4083 level->bd_short_explosions = cave->short_explosions;
4084 level->bd_gravity_affects_all = cave->gravity_affects_all;
4086 // player properties
4087 level->bd_diagonal_movements = cave->diagonal_movements;
4088 level->bd_topmost_player_active = cave->active_is_first_found;
4089 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4090 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4091 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4092 level->bd_snap_element = map_element_BD_to_RND(cave->snap_element);
4094 // element properties
4095 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4096 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4097 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4098 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4099 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4100 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4101 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4102 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4103 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4104 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4105 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4106 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4107 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4108 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4109 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4110 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4111 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4112 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4113 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4114 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4116 level->bd_amoeba_content_too_big = map_element_BD_to_RND(cave->amoeba_too_big_effect);
4117 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4118 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4119 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4120 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4121 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4123 level->bd_slime_is_predictable = cave->slime_predictable;
4124 level->bd_slime_correct_random = cave->slime_correct_random;
4125 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4126 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4127 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4128 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4130 level->bd_acid_eats_element = map_element_BD_to_RND(cave->acid_eats_this);
4131 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4132 level->bd_acid_turns_to_element = map_element_BD_to_RND(cave->acid_turns_to);
4134 level->bd_biter_move_delay = cave->biter_delay_frame;
4135 level->bd_biter_eats_element = map_element_BD_to_RND(cave->biter_eat);
4137 level->bd_bladder_converts_by_element = map_element_BD_to_RND(cave->bladder_converts_by);
4140 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4142 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4143 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4145 // playfield elements
4146 for (x = 0; x < level->fieldx; x++)
4147 for (y = 0; y < level->fieldy; y++)
4148 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4150 checked_free(cave_name);
4153 static void setTapeInfoToDefaults(void);
4155 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4157 struct LevelInfo_BD *level_bd = level->native_bd_level;
4158 GdCave *cave = level_bd->cave;
4159 GdReplay *replay = level_bd->replay;
4165 // always start with reliable default values
4166 setTapeInfoToDefaults();
4168 tape.level_nr = level_nr; // (currently not used)
4169 tape.random_seed = replay->seed;
4171 TapeSetDateFromIsoDateString(replay->date);
4174 tape.pos[tape.counter].delay = 0;
4176 tape.bd_replay = TRUE;
4178 // all time calculations only used to display approximate tape time
4179 int cave_speed = cave->speed;
4180 int milliseconds_game = 0;
4181 int milliseconds_elapsed = 20;
4183 for (i = 0; i < replay->movements->len; i++)
4185 int replay_action = replay->movements->data[i];
4186 int tape_action = map_action_BD_to_RND(replay_action);
4187 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4188 boolean success = 0;
4192 success = TapeAddAction(action);
4194 milliseconds_game += milliseconds_elapsed;
4196 if (milliseconds_game >= cave_speed)
4198 milliseconds_game -= cave_speed;
4205 tape.pos[tape.counter].delay = 0;
4206 tape.pos[tape.counter].action[0] = 0;
4210 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4216 TapeHaltRecording();
4220 // ----------------------------------------------------------------------------
4221 // functions for loading EM level
4222 // ----------------------------------------------------------------------------
4224 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4226 static int ball_xy[8][2] =
4237 struct LevelInfo_EM *level_em = level->native_em_level;
4238 struct CAVE *cav = level_em->cav;
4241 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4242 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4244 cav->time_seconds = level->time;
4245 cav->gems_needed = level->gems_needed;
4247 cav->emerald_score = level->score[SC_EMERALD];
4248 cav->diamond_score = level->score[SC_DIAMOND];
4249 cav->alien_score = level->score[SC_ROBOT];
4250 cav->tank_score = level->score[SC_SPACESHIP];
4251 cav->bug_score = level->score[SC_BUG];
4252 cav->eater_score = level->score[SC_YAMYAM];
4253 cav->nut_score = level->score[SC_NUT];
4254 cav->dynamite_score = level->score[SC_DYNAMITE];
4255 cav->key_score = level->score[SC_KEY];
4256 cav->exit_score = level->score[SC_TIME_BONUS];
4258 cav->num_eater_arrays = level->num_yamyam_contents;
4260 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4261 for (y = 0; y < 3; y++)
4262 for (x = 0; x < 3; x++)
4263 cav->eater_array[i][y * 3 + x] =
4264 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4266 cav->amoeba_time = level->amoeba_speed;
4267 cav->wonderwall_time = level->time_magic_wall;
4268 cav->wheel_time = level->time_wheel;
4270 cav->android_move_time = level->android_move_time;
4271 cav->android_clone_time = level->android_clone_time;
4272 cav->ball_random = level->ball_random;
4273 cav->ball_active = level->ball_active_initial;
4274 cav->ball_time = level->ball_time;
4275 cav->num_ball_arrays = level->num_ball_contents;
4277 cav->lenses_score = level->lenses_score;
4278 cav->magnify_score = level->magnify_score;
4279 cav->slurp_score = level->slurp_score;
4281 cav->lenses_time = level->lenses_time;
4282 cav->magnify_time = level->magnify_time;
4284 cav->wind_time = 9999;
4285 cav->wind_direction =
4286 map_direction_RND_to_EM(level->wind_direction_initial);
4288 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4289 for (j = 0; j < 8; j++)
4290 cav->ball_array[i][j] =
4291 map_element_RND_to_EM_cave(level->ball_content[i].
4292 e[ball_xy[j][0]][ball_xy[j][1]]);
4294 map_android_clone_elements_RND_to_EM(level);
4296 // first fill the complete playfield with the empty space element
4297 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4298 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4299 cav->cave[x][y] = Cblank;
4301 // then copy the real level contents from level file into the playfield
4302 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4304 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4306 if (level->field[x][y] == EL_AMOEBA_DEAD)
4307 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4309 cav->cave[x][y] = new_element;
4312 for (i = 0; i < MAX_PLAYERS; i++)
4314 cav->player_x[i] = -1;
4315 cav->player_y[i] = -1;
4318 // initialize player positions and delete players from the playfield
4319 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4321 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4323 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4325 cav->player_x[player_nr] = x;
4326 cav->player_y[player_nr] = y;
4328 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4333 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4335 static int ball_xy[8][2] =
4346 struct LevelInfo_EM *level_em = level->native_em_level;
4347 struct CAVE *cav = level_em->cav;
4350 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4351 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4353 level->time = cav->time_seconds;
4354 level->gems_needed = cav->gems_needed;
4356 sprintf(level->name, "Level %d", level->file_info.nr);
4358 level->score[SC_EMERALD] = cav->emerald_score;
4359 level->score[SC_DIAMOND] = cav->diamond_score;
4360 level->score[SC_ROBOT] = cav->alien_score;
4361 level->score[SC_SPACESHIP] = cav->tank_score;
4362 level->score[SC_BUG] = cav->bug_score;
4363 level->score[SC_YAMYAM] = cav->eater_score;
4364 level->score[SC_NUT] = cav->nut_score;
4365 level->score[SC_DYNAMITE] = cav->dynamite_score;
4366 level->score[SC_KEY] = cav->key_score;
4367 level->score[SC_TIME_BONUS] = cav->exit_score;
4369 level->num_yamyam_contents = cav->num_eater_arrays;
4371 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4372 for (y = 0; y < 3; y++)
4373 for (x = 0; x < 3; x++)
4374 level->yamyam_content[i].e[x][y] =
4375 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4377 level->amoeba_speed = cav->amoeba_time;
4378 level->time_magic_wall = cav->wonderwall_time;
4379 level->time_wheel = cav->wheel_time;
4381 level->android_move_time = cav->android_move_time;
4382 level->android_clone_time = cav->android_clone_time;
4383 level->ball_random = cav->ball_random;
4384 level->ball_active_initial = cav->ball_active;
4385 level->ball_time = cav->ball_time;
4386 level->num_ball_contents = cav->num_ball_arrays;
4388 level->lenses_score = cav->lenses_score;
4389 level->magnify_score = cav->magnify_score;
4390 level->slurp_score = cav->slurp_score;
4392 level->lenses_time = cav->lenses_time;
4393 level->magnify_time = cav->magnify_time;
4395 level->wind_direction_initial =
4396 map_direction_EM_to_RND(cav->wind_direction);
4398 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4399 for (j = 0; j < 8; j++)
4400 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4401 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4403 map_android_clone_elements_EM_to_RND(level);
4405 // convert the playfield (some elements need special treatment)
4406 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4408 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4410 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4411 new_element = EL_AMOEBA_DEAD;
4413 level->field[x][y] = new_element;
4416 for (i = 0; i < MAX_PLAYERS; i++)
4418 // in case of all players set to the same field, use the first player
4419 int nr = MAX_PLAYERS - i - 1;
4420 int jx = cav->player_x[nr];
4421 int jy = cav->player_y[nr];
4423 if (jx != -1 && jy != -1)
4424 level->field[jx][jy] = EL_PLAYER_1 + nr;
4427 // time score is counted for each 10 seconds left in Emerald Mine levels
4428 level->time_score_base = 10;
4432 // ----------------------------------------------------------------------------
4433 // functions for loading SP level
4434 // ----------------------------------------------------------------------------
4436 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4438 struct LevelInfo_SP *level_sp = level->native_sp_level;
4439 LevelInfoType *header = &level_sp->header;
4442 level_sp->width = level->fieldx;
4443 level_sp->height = level->fieldy;
4445 for (x = 0; x < level->fieldx; x++)
4446 for (y = 0; y < level->fieldy; y++)
4447 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4449 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4451 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4452 header->LevelTitle[i] = level->name[i];
4453 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4455 header->InfotronsNeeded = level->gems_needed;
4457 header->SpecialPortCount = 0;
4459 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4461 boolean gravity_port_found = FALSE;
4462 boolean gravity_port_valid = FALSE;
4463 int gravity_port_flag;
4464 int gravity_port_base_element;
4465 int element = level->field[x][y];
4467 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4468 element <= EL_SP_GRAVITY_ON_PORT_UP)
4470 gravity_port_found = TRUE;
4471 gravity_port_valid = TRUE;
4472 gravity_port_flag = 1;
4473 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4475 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4476 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4478 gravity_port_found = TRUE;
4479 gravity_port_valid = TRUE;
4480 gravity_port_flag = 0;
4481 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4483 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4484 element <= EL_SP_GRAVITY_PORT_UP)
4486 // change R'n'D style gravity inverting special port to normal port
4487 // (there are no gravity inverting ports in native Supaplex engine)
4489 gravity_port_found = TRUE;
4490 gravity_port_valid = FALSE;
4491 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4494 if (gravity_port_found)
4496 if (gravity_port_valid &&
4497 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4499 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4501 port->PortLocation = (y * level->fieldx + x) * 2;
4502 port->Gravity = gravity_port_flag;
4504 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4506 header->SpecialPortCount++;
4510 // change special gravity port to normal port
4512 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4515 level_sp->playfield[x][y] = element - EL_SP_START;
4520 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4522 struct LevelInfo_SP *level_sp = level->native_sp_level;
4523 LevelInfoType *header = &level_sp->header;
4524 boolean num_invalid_elements = 0;
4527 level->fieldx = level_sp->width;
4528 level->fieldy = level_sp->height;
4530 for (x = 0; x < level->fieldx; x++)
4532 for (y = 0; y < level->fieldy; y++)
4534 int element_old = level_sp->playfield[x][y];
4535 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4537 if (element_new == EL_UNKNOWN)
4539 num_invalid_elements++;
4541 Debug("level:native:SP", "invalid element %d at position %d, %d",
4545 level->field[x][y] = element_new;
4549 if (num_invalid_elements > 0)
4550 Warn("found %d invalid elements%s", num_invalid_elements,
4551 (!options.debug ? " (use '--debug' for more details)" : ""));
4553 for (i = 0; i < MAX_PLAYERS; i++)
4554 level->initial_player_gravity[i] =
4555 (header->InitialGravity == 1 ? TRUE : FALSE);
4557 // skip leading spaces
4558 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4559 if (header->LevelTitle[i] != ' ')
4563 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4564 level->name[j] = header->LevelTitle[i];
4565 level->name[j] = '\0';
4567 // cut trailing spaces
4569 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4570 level->name[j - 1] = '\0';
4572 level->gems_needed = header->InfotronsNeeded;
4574 for (i = 0; i < header->SpecialPortCount; i++)
4576 SpecialPortType *port = &header->SpecialPort[i];
4577 int port_location = port->PortLocation;
4578 int gravity = port->Gravity;
4579 int port_x, port_y, port_element;
4581 port_x = (port_location / 2) % level->fieldx;
4582 port_y = (port_location / 2) / level->fieldx;
4584 if (port_x < 0 || port_x >= level->fieldx ||
4585 port_y < 0 || port_y >= level->fieldy)
4587 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4592 port_element = level->field[port_x][port_y];
4594 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4595 port_element > EL_SP_GRAVITY_PORT_UP)
4597 Warn("no special port at position (%d, %d)", port_x, port_y);
4602 // change previous (wrong) gravity inverting special port to either
4603 // gravity enabling special port or gravity disabling special port
4604 level->field[port_x][port_y] +=
4605 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4606 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4609 // change special gravity ports without database entries to normal ports
4610 for (x = 0; x < level->fieldx; x++)
4611 for (y = 0; y < level->fieldy; y++)
4612 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4613 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4614 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4616 level->time = 0; // no time limit
4617 level->amoeba_speed = 0;
4618 level->time_magic_wall = 0;
4619 level->time_wheel = 0;
4620 level->amoeba_content = EL_EMPTY;
4622 // original Supaplex does not use score values -- rate by playing time
4623 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4624 level->score[i] = 0;
4626 level->rate_time_over_score = TRUE;
4628 // there are no yamyams in supaplex levels
4629 for (i = 0; i < level->num_yamyam_contents; i++)
4630 for (x = 0; x < 3; x++)
4631 for (y = 0; y < 3; y++)
4632 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4635 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4637 struct LevelInfo_SP *level_sp = level->native_sp_level;
4638 struct DemoInfo_SP *demo = &level_sp->demo;
4641 // always start with reliable default values
4642 demo->is_available = FALSE;
4645 if (TAPE_IS_EMPTY(tape))
4648 demo->level_nr = tape.level_nr; // (currently not used)
4650 level_sp->header.DemoRandomSeed = tape.random_seed;
4654 for (i = 0; i < tape.length; i++)
4656 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4657 int demo_repeat = tape.pos[i].delay;
4658 int demo_entries = (demo_repeat + 15) / 16;
4660 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4662 Warn("tape truncated: size exceeds maximum SP demo size %d",
4668 for (j = 0; j < demo_repeat / 16; j++)
4669 demo->data[demo->length++] = 0xf0 | demo_action;
4671 if (demo_repeat % 16)
4672 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4675 demo->is_available = TRUE;
4678 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4680 struct LevelInfo_SP *level_sp = level->native_sp_level;
4681 struct DemoInfo_SP *demo = &level_sp->demo;
4682 char *filename = level->file_info.filename;
4685 // always start with reliable default values
4686 setTapeInfoToDefaults();
4688 if (!demo->is_available)
4691 tape.level_nr = demo->level_nr; // (currently not used)
4692 tape.random_seed = level_sp->header.DemoRandomSeed;
4694 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4697 tape.pos[tape.counter].delay = 0;
4699 for (i = 0; i < demo->length; i++)
4701 int demo_action = demo->data[i] & 0x0f;
4702 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4703 int tape_action = map_key_SP_to_RND(demo_action);
4704 int tape_repeat = demo_repeat + 1;
4705 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4706 boolean success = 0;
4709 for (j = 0; j < tape_repeat; j++)
4710 success = TapeAddAction(action);
4714 Warn("SP demo truncated: size exceeds maximum tape size %d",
4721 TapeHaltRecording();
4725 // ----------------------------------------------------------------------------
4726 // functions for loading MM level
4727 // ----------------------------------------------------------------------------
4729 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4731 struct LevelInfo_MM *level_mm = level->native_mm_level;
4734 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4735 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4737 level_mm->time = level->time;
4738 level_mm->kettles_needed = level->gems_needed;
4739 level_mm->auto_count_kettles = level->auto_count_gems;
4741 level_mm->mm_laser_red = level->mm_laser_red;
4742 level_mm->mm_laser_green = level->mm_laser_green;
4743 level_mm->mm_laser_blue = level->mm_laser_blue;
4745 level_mm->df_laser_red = level->df_laser_red;
4746 level_mm->df_laser_green = level->df_laser_green;
4747 level_mm->df_laser_blue = level->df_laser_blue;
4749 strcpy(level_mm->name, level->name);
4750 strcpy(level_mm->author, level->author);
4752 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4753 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4754 level_mm->score[SC_KEY] = level->score[SC_KEY];
4755 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4756 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4758 level_mm->amoeba_speed = level->amoeba_speed;
4759 level_mm->time_fuse = level->mm_time_fuse;
4760 level_mm->time_bomb = level->mm_time_bomb;
4761 level_mm->time_ball = level->mm_time_ball;
4762 level_mm->time_block = level->mm_time_block;
4764 level_mm->num_ball_contents = level->num_mm_ball_contents;
4765 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4766 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4767 level_mm->explode_ball = level->explode_mm_ball;
4769 for (i = 0; i < level->num_mm_ball_contents; i++)
4770 level_mm->ball_content[i] =
4771 map_element_RND_to_MM(level->mm_ball_content[i]);
4773 for (x = 0; x < level->fieldx; x++)
4774 for (y = 0; y < level->fieldy; y++)
4776 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4779 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4781 struct LevelInfo_MM *level_mm = level->native_mm_level;
4784 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4785 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4787 level->time = level_mm->time;
4788 level->gems_needed = level_mm->kettles_needed;
4789 level->auto_count_gems = level_mm->auto_count_kettles;
4791 level->mm_laser_red = level_mm->mm_laser_red;
4792 level->mm_laser_green = level_mm->mm_laser_green;
4793 level->mm_laser_blue = level_mm->mm_laser_blue;
4795 level->df_laser_red = level_mm->df_laser_red;
4796 level->df_laser_green = level_mm->df_laser_green;
4797 level->df_laser_blue = level_mm->df_laser_blue;
4799 strcpy(level->name, level_mm->name);
4801 // only overwrite author from 'levelinfo.conf' if author defined in level
4802 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4803 strcpy(level->author, level_mm->author);
4805 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4806 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4807 level->score[SC_KEY] = level_mm->score[SC_KEY];
4808 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4809 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4811 level->amoeba_speed = level_mm->amoeba_speed;
4812 level->mm_time_fuse = level_mm->time_fuse;
4813 level->mm_time_bomb = level_mm->time_bomb;
4814 level->mm_time_ball = level_mm->time_ball;
4815 level->mm_time_block = level_mm->time_block;
4817 level->num_mm_ball_contents = level_mm->num_ball_contents;
4818 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4819 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4820 level->explode_mm_ball = level_mm->explode_ball;
4822 for (i = 0; i < level->num_mm_ball_contents; i++)
4823 level->mm_ball_content[i] =
4824 map_element_MM_to_RND(level_mm->ball_content[i]);
4826 for (x = 0; x < level->fieldx; x++)
4827 for (y = 0; y < level->fieldy; y++)
4828 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4832 // ----------------------------------------------------------------------------
4833 // functions for loading DC level
4834 // ----------------------------------------------------------------------------
4836 #define DC_LEVEL_HEADER_SIZE 344
4838 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4841 static int last_data_encoded;
4845 int diff_hi, diff_lo;
4846 int data_hi, data_lo;
4847 unsigned short data_decoded;
4851 last_data_encoded = 0;
4858 diff = data_encoded - last_data_encoded;
4859 diff_hi = diff & ~0xff;
4860 diff_lo = diff & 0xff;
4864 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4865 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4866 data_hi = data_hi & 0xff00;
4868 data_decoded = data_hi | data_lo;
4870 last_data_encoded = data_encoded;
4872 offset1 = (offset1 + 1) % 31;
4873 offset2 = offset2 & 0xff;
4875 return data_decoded;
4878 static int getMappedElement_DC(int element)
4886 // 0x0117 - 0x036e: (?)
4889 // 0x042d - 0x0684: (?)
4905 element = EL_CRYSTAL;
4908 case 0x0e77: // quicksand (boulder)
4909 element = EL_QUICKSAND_FAST_FULL;
4912 case 0x0e99: // slow quicksand (boulder)
4913 element = EL_QUICKSAND_FULL;
4917 element = EL_EM_EXIT_OPEN;
4921 element = EL_EM_EXIT_CLOSED;
4925 element = EL_EM_STEEL_EXIT_OPEN;
4929 element = EL_EM_STEEL_EXIT_CLOSED;
4932 case 0x0f4f: // dynamite (lit 1)
4933 element = EL_EM_DYNAMITE_ACTIVE;
4936 case 0x0f57: // dynamite (lit 2)
4937 element = EL_EM_DYNAMITE_ACTIVE;
4940 case 0x0f5f: // dynamite (lit 3)
4941 element = EL_EM_DYNAMITE_ACTIVE;
4944 case 0x0f67: // dynamite (lit 4)
4945 element = EL_EM_DYNAMITE_ACTIVE;
4952 element = EL_AMOEBA_WET;
4956 element = EL_AMOEBA_DROP;
4960 element = EL_DC_MAGIC_WALL;
4964 element = EL_SPACESHIP_UP;
4968 element = EL_SPACESHIP_DOWN;
4972 element = EL_SPACESHIP_LEFT;
4976 element = EL_SPACESHIP_RIGHT;
4980 element = EL_BUG_UP;
4984 element = EL_BUG_DOWN;
4988 element = EL_BUG_LEFT;
4992 element = EL_BUG_RIGHT;
4996 element = EL_MOLE_UP;
5000 element = EL_MOLE_DOWN;
5004 element = EL_MOLE_LEFT;
5008 element = EL_MOLE_RIGHT;
5016 element = EL_YAMYAM_UP;
5020 element = EL_SWITCHGATE_OPEN;
5024 element = EL_SWITCHGATE_CLOSED;
5028 element = EL_DC_SWITCHGATE_SWITCH_UP;
5032 element = EL_TIMEGATE_CLOSED;
5035 case 0x144c: // conveyor belt switch (green)
5036 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5039 case 0x144f: // conveyor belt switch (red)
5040 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5043 case 0x1452: // conveyor belt switch (blue)
5044 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5048 element = EL_CONVEYOR_BELT_3_MIDDLE;
5052 element = EL_CONVEYOR_BELT_3_LEFT;
5056 element = EL_CONVEYOR_BELT_3_RIGHT;
5060 element = EL_CONVEYOR_BELT_1_MIDDLE;
5064 element = EL_CONVEYOR_BELT_1_LEFT;
5068 element = EL_CONVEYOR_BELT_1_RIGHT;
5072 element = EL_CONVEYOR_BELT_4_MIDDLE;
5076 element = EL_CONVEYOR_BELT_4_LEFT;
5080 element = EL_CONVEYOR_BELT_4_RIGHT;
5084 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5088 element = EL_EXPANDABLE_WALL_VERTICAL;
5092 element = EL_EXPANDABLE_WALL_ANY;
5095 case 0x14ce: // growing steel wall (left/right)
5096 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5099 case 0x14df: // growing steel wall (up/down)
5100 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5103 case 0x14e8: // growing steel wall (up/down/left/right)
5104 element = EL_EXPANDABLE_STEELWALL_ANY;
5108 element = EL_SHIELD_DEADLY;
5112 element = EL_EXTRA_TIME;
5120 element = EL_EMPTY_SPACE;
5123 case 0x1578: // quicksand (empty)
5124 element = EL_QUICKSAND_FAST_EMPTY;
5127 case 0x1579: // slow quicksand (empty)
5128 element = EL_QUICKSAND_EMPTY;
5138 element = EL_EM_DYNAMITE;
5141 case 0x15a1: // key (red)
5142 element = EL_EM_KEY_1;
5145 case 0x15a2: // key (yellow)
5146 element = EL_EM_KEY_2;
5149 case 0x15a3: // key (blue)
5150 element = EL_EM_KEY_4;
5153 case 0x15a4: // key (green)
5154 element = EL_EM_KEY_3;
5157 case 0x15a5: // key (white)
5158 element = EL_DC_KEY_WHITE;
5162 element = EL_WALL_SLIPPERY;
5169 case 0x15a8: // wall (not round)
5173 case 0x15a9: // (blue)
5174 element = EL_CHAR_A;
5177 case 0x15aa: // (blue)
5178 element = EL_CHAR_B;
5181 case 0x15ab: // (blue)
5182 element = EL_CHAR_C;
5185 case 0x15ac: // (blue)
5186 element = EL_CHAR_D;
5189 case 0x15ad: // (blue)
5190 element = EL_CHAR_E;
5193 case 0x15ae: // (blue)
5194 element = EL_CHAR_F;
5197 case 0x15af: // (blue)
5198 element = EL_CHAR_G;
5201 case 0x15b0: // (blue)
5202 element = EL_CHAR_H;
5205 case 0x15b1: // (blue)
5206 element = EL_CHAR_I;
5209 case 0x15b2: // (blue)
5210 element = EL_CHAR_J;
5213 case 0x15b3: // (blue)
5214 element = EL_CHAR_K;
5217 case 0x15b4: // (blue)
5218 element = EL_CHAR_L;
5221 case 0x15b5: // (blue)
5222 element = EL_CHAR_M;
5225 case 0x15b6: // (blue)
5226 element = EL_CHAR_N;
5229 case 0x15b7: // (blue)
5230 element = EL_CHAR_O;
5233 case 0x15b8: // (blue)
5234 element = EL_CHAR_P;
5237 case 0x15b9: // (blue)
5238 element = EL_CHAR_Q;
5241 case 0x15ba: // (blue)
5242 element = EL_CHAR_R;
5245 case 0x15bb: // (blue)
5246 element = EL_CHAR_S;
5249 case 0x15bc: // (blue)
5250 element = EL_CHAR_T;
5253 case 0x15bd: // (blue)
5254 element = EL_CHAR_U;
5257 case 0x15be: // (blue)
5258 element = EL_CHAR_V;
5261 case 0x15bf: // (blue)
5262 element = EL_CHAR_W;
5265 case 0x15c0: // (blue)
5266 element = EL_CHAR_X;
5269 case 0x15c1: // (blue)
5270 element = EL_CHAR_Y;
5273 case 0x15c2: // (blue)
5274 element = EL_CHAR_Z;
5277 case 0x15c3: // (blue)
5278 element = EL_CHAR_AUMLAUT;
5281 case 0x15c4: // (blue)
5282 element = EL_CHAR_OUMLAUT;
5285 case 0x15c5: // (blue)
5286 element = EL_CHAR_UUMLAUT;
5289 case 0x15c6: // (blue)
5290 element = EL_CHAR_0;
5293 case 0x15c7: // (blue)
5294 element = EL_CHAR_1;
5297 case 0x15c8: // (blue)
5298 element = EL_CHAR_2;
5301 case 0x15c9: // (blue)
5302 element = EL_CHAR_3;
5305 case 0x15ca: // (blue)
5306 element = EL_CHAR_4;
5309 case 0x15cb: // (blue)
5310 element = EL_CHAR_5;
5313 case 0x15cc: // (blue)
5314 element = EL_CHAR_6;
5317 case 0x15cd: // (blue)
5318 element = EL_CHAR_7;
5321 case 0x15ce: // (blue)
5322 element = EL_CHAR_8;
5325 case 0x15cf: // (blue)
5326 element = EL_CHAR_9;
5329 case 0x15d0: // (blue)
5330 element = EL_CHAR_PERIOD;
5333 case 0x15d1: // (blue)
5334 element = EL_CHAR_EXCLAM;
5337 case 0x15d2: // (blue)
5338 element = EL_CHAR_COLON;
5341 case 0x15d3: // (blue)
5342 element = EL_CHAR_LESS;
5345 case 0x15d4: // (blue)
5346 element = EL_CHAR_GREATER;
5349 case 0x15d5: // (blue)
5350 element = EL_CHAR_QUESTION;
5353 case 0x15d6: // (blue)
5354 element = EL_CHAR_COPYRIGHT;
5357 case 0x15d7: // (blue)
5358 element = EL_CHAR_UP;
5361 case 0x15d8: // (blue)
5362 element = EL_CHAR_DOWN;
5365 case 0x15d9: // (blue)
5366 element = EL_CHAR_BUTTON;
5369 case 0x15da: // (blue)
5370 element = EL_CHAR_PLUS;
5373 case 0x15db: // (blue)
5374 element = EL_CHAR_MINUS;
5377 case 0x15dc: // (blue)
5378 element = EL_CHAR_APOSTROPHE;
5381 case 0x15dd: // (blue)
5382 element = EL_CHAR_PARENLEFT;
5385 case 0x15de: // (blue)
5386 element = EL_CHAR_PARENRIGHT;
5389 case 0x15df: // (green)
5390 element = EL_CHAR_A;
5393 case 0x15e0: // (green)
5394 element = EL_CHAR_B;
5397 case 0x15e1: // (green)
5398 element = EL_CHAR_C;
5401 case 0x15e2: // (green)
5402 element = EL_CHAR_D;
5405 case 0x15e3: // (green)
5406 element = EL_CHAR_E;
5409 case 0x15e4: // (green)
5410 element = EL_CHAR_F;
5413 case 0x15e5: // (green)
5414 element = EL_CHAR_G;
5417 case 0x15e6: // (green)
5418 element = EL_CHAR_H;
5421 case 0x15e7: // (green)
5422 element = EL_CHAR_I;
5425 case 0x15e8: // (green)
5426 element = EL_CHAR_J;
5429 case 0x15e9: // (green)
5430 element = EL_CHAR_K;
5433 case 0x15ea: // (green)
5434 element = EL_CHAR_L;
5437 case 0x15eb: // (green)
5438 element = EL_CHAR_M;
5441 case 0x15ec: // (green)
5442 element = EL_CHAR_N;
5445 case 0x15ed: // (green)
5446 element = EL_CHAR_O;
5449 case 0x15ee: // (green)
5450 element = EL_CHAR_P;
5453 case 0x15ef: // (green)
5454 element = EL_CHAR_Q;
5457 case 0x15f0: // (green)
5458 element = EL_CHAR_R;
5461 case 0x15f1: // (green)
5462 element = EL_CHAR_S;
5465 case 0x15f2: // (green)
5466 element = EL_CHAR_T;
5469 case 0x15f3: // (green)
5470 element = EL_CHAR_U;
5473 case 0x15f4: // (green)
5474 element = EL_CHAR_V;
5477 case 0x15f5: // (green)
5478 element = EL_CHAR_W;
5481 case 0x15f6: // (green)
5482 element = EL_CHAR_X;
5485 case 0x15f7: // (green)
5486 element = EL_CHAR_Y;
5489 case 0x15f8: // (green)
5490 element = EL_CHAR_Z;
5493 case 0x15f9: // (green)
5494 element = EL_CHAR_AUMLAUT;
5497 case 0x15fa: // (green)
5498 element = EL_CHAR_OUMLAUT;
5501 case 0x15fb: // (green)
5502 element = EL_CHAR_UUMLAUT;
5505 case 0x15fc: // (green)
5506 element = EL_CHAR_0;
5509 case 0x15fd: // (green)
5510 element = EL_CHAR_1;
5513 case 0x15fe: // (green)
5514 element = EL_CHAR_2;
5517 case 0x15ff: // (green)
5518 element = EL_CHAR_3;
5521 case 0x1600: // (green)
5522 element = EL_CHAR_4;
5525 case 0x1601: // (green)
5526 element = EL_CHAR_5;
5529 case 0x1602: // (green)
5530 element = EL_CHAR_6;
5533 case 0x1603: // (green)
5534 element = EL_CHAR_7;
5537 case 0x1604: // (green)
5538 element = EL_CHAR_8;
5541 case 0x1605: // (green)
5542 element = EL_CHAR_9;
5545 case 0x1606: // (green)
5546 element = EL_CHAR_PERIOD;
5549 case 0x1607: // (green)
5550 element = EL_CHAR_EXCLAM;
5553 case 0x1608: // (green)
5554 element = EL_CHAR_COLON;
5557 case 0x1609: // (green)
5558 element = EL_CHAR_LESS;
5561 case 0x160a: // (green)
5562 element = EL_CHAR_GREATER;
5565 case 0x160b: // (green)
5566 element = EL_CHAR_QUESTION;
5569 case 0x160c: // (green)
5570 element = EL_CHAR_COPYRIGHT;
5573 case 0x160d: // (green)
5574 element = EL_CHAR_UP;
5577 case 0x160e: // (green)
5578 element = EL_CHAR_DOWN;
5581 case 0x160f: // (green)
5582 element = EL_CHAR_BUTTON;
5585 case 0x1610: // (green)
5586 element = EL_CHAR_PLUS;
5589 case 0x1611: // (green)
5590 element = EL_CHAR_MINUS;
5593 case 0x1612: // (green)
5594 element = EL_CHAR_APOSTROPHE;
5597 case 0x1613: // (green)
5598 element = EL_CHAR_PARENLEFT;
5601 case 0x1614: // (green)
5602 element = EL_CHAR_PARENRIGHT;
5605 case 0x1615: // (blue steel)
5606 element = EL_STEEL_CHAR_A;
5609 case 0x1616: // (blue steel)
5610 element = EL_STEEL_CHAR_B;
5613 case 0x1617: // (blue steel)
5614 element = EL_STEEL_CHAR_C;
5617 case 0x1618: // (blue steel)
5618 element = EL_STEEL_CHAR_D;
5621 case 0x1619: // (blue steel)
5622 element = EL_STEEL_CHAR_E;
5625 case 0x161a: // (blue steel)
5626 element = EL_STEEL_CHAR_F;
5629 case 0x161b: // (blue steel)
5630 element = EL_STEEL_CHAR_G;
5633 case 0x161c: // (blue steel)
5634 element = EL_STEEL_CHAR_H;
5637 case 0x161d: // (blue steel)
5638 element = EL_STEEL_CHAR_I;
5641 case 0x161e: // (blue steel)
5642 element = EL_STEEL_CHAR_J;
5645 case 0x161f: // (blue steel)
5646 element = EL_STEEL_CHAR_K;
5649 case 0x1620: // (blue steel)
5650 element = EL_STEEL_CHAR_L;
5653 case 0x1621: // (blue steel)
5654 element = EL_STEEL_CHAR_M;
5657 case 0x1622: // (blue steel)
5658 element = EL_STEEL_CHAR_N;
5661 case 0x1623: // (blue steel)
5662 element = EL_STEEL_CHAR_O;
5665 case 0x1624: // (blue steel)
5666 element = EL_STEEL_CHAR_P;
5669 case 0x1625: // (blue steel)
5670 element = EL_STEEL_CHAR_Q;
5673 case 0x1626: // (blue steel)
5674 element = EL_STEEL_CHAR_R;
5677 case 0x1627: // (blue steel)
5678 element = EL_STEEL_CHAR_S;
5681 case 0x1628: // (blue steel)
5682 element = EL_STEEL_CHAR_T;
5685 case 0x1629: // (blue steel)
5686 element = EL_STEEL_CHAR_U;
5689 case 0x162a: // (blue steel)
5690 element = EL_STEEL_CHAR_V;
5693 case 0x162b: // (blue steel)
5694 element = EL_STEEL_CHAR_W;
5697 case 0x162c: // (blue steel)
5698 element = EL_STEEL_CHAR_X;
5701 case 0x162d: // (blue steel)
5702 element = EL_STEEL_CHAR_Y;
5705 case 0x162e: // (blue steel)
5706 element = EL_STEEL_CHAR_Z;
5709 case 0x162f: // (blue steel)
5710 element = EL_STEEL_CHAR_AUMLAUT;
5713 case 0x1630: // (blue steel)
5714 element = EL_STEEL_CHAR_OUMLAUT;
5717 case 0x1631: // (blue steel)
5718 element = EL_STEEL_CHAR_UUMLAUT;
5721 case 0x1632: // (blue steel)
5722 element = EL_STEEL_CHAR_0;
5725 case 0x1633: // (blue steel)
5726 element = EL_STEEL_CHAR_1;
5729 case 0x1634: // (blue steel)
5730 element = EL_STEEL_CHAR_2;
5733 case 0x1635: // (blue steel)
5734 element = EL_STEEL_CHAR_3;
5737 case 0x1636: // (blue steel)
5738 element = EL_STEEL_CHAR_4;
5741 case 0x1637: // (blue steel)
5742 element = EL_STEEL_CHAR_5;
5745 case 0x1638: // (blue steel)
5746 element = EL_STEEL_CHAR_6;
5749 case 0x1639: // (blue steel)
5750 element = EL_STEEL_CHAR_7;
5753 case 0x163a: // (blue steel)
5754 element = EL_STEEL_CHAR_8;
5757 case 0x163b: // (blue steel)
5758 element = EL_STEEL_CHAR_9;
5761 case 0x163c: // (blue steel)
5762 element = EL_STEEL_CHAR_PERIOD;
5765 case 0x163d: // (blue steel)
5766 element = EL_STEEL_CHAR_EXCLAM;
5769 case 0x163e: // (blue steel)
5770 element = EL_STEEL_CHAR_COLON;
5773 case 0x163f: // (blue steel)
5774 element = EL_STEEL_CHAR_LESS;
5777 case 0x1640: // (blue steel)
5778 element = EL_STEEL_CHAR_GREATER;
5781 case 0x1641: // (blue steel)
5782 element = EL_STEEL_CHAR_QUESTION;
5785 case 0x1642: // (blue steel)
5786 element = EL_STEEL_CHAR_COPYRIGHT;
5789 case 0x1643: // (blue steel)
5790 element = EL_STEEL_CHAR_UP;
5793 case 0x1644: // (blue steel)
5794 element = EL_STEEL_CHAR_DOWN;
5797 case 0x1645: // (blue steel)
5798 element = EL_STEEL_CHAR_BUTTON;
5801 case 0x1646: // (blue steel)
5802 element = EL_STEEL_CHAR_PLUS;
5805 case 0x1647: // (blue steel)
5806 element = EL_STEEL_CHAR_MINUS;
5809 case 0x1648: // (blue steel)
5810 element = EL_STEEL_CHAR_APOSTROPHE;
5813 case 0x1649: // (blue steel)
5814 element = EL_STEEL_CHAR_PARENLEFT;
5817 case 0x164a: // (blue steel)
5818 element = EL_STEEL_CHAR_PARENRIGHT;
5821 case 0x164b: // (green steel)
5822 element = EL_STEEL_CHAR_A;
5825 case 0x164c: // (green steel)
5826 element = EL_STEEL_CHAR_B;
5829 case 0x164d: // (green steel)
5830 element = EL_STEEL_CHAR_C;
5833 case 0x164e: // (green steel)
5834 element = EL_STEEL_CHAR_D;
5837 case 0x164f: // (green steel)
5838 element = EL_STEEL_CHAR_E;
5841 case 0x1650: // (green steel)
5842 element = EL_STEEL_CHAR_F;
5845 case 0x1651: // (green steel)
5846 element = EL_STEEL_CHAR_G;
5849 case 0x1652: // (green steel)
5850 element = EL_STEEL_CHAR_H;
5853 case 0x1653: // (green steel)
5854 element = EL_STEEL_CHAR_I;
5857 case 0x1654: // (green steel)
5858 element = EL_STEEL_CHAR_J;
5861 case 0x1655: // (green steel)
5862 element = EL_STEEL_CHAR_K;
5865 case 0x1656: // (green steel)
5866 element = EL_STEEL_CHAR_L;
5869 case 0x1657: // (green steel)
5870 element = EL_STEEL_CHAR_M;
5873 case 0x1658: // (green steel)
5874 element = EL_STEEL_CHAR_N;
5877 case 0x1659: // (green steel)
5878 element = EL_STEEL_CHAR_O;
5881 case 0x165a: // (green steel)
5882 element = EL_STEEL_CHAR_P;
5885 case 0x165b: // (green steel)
5886 element = EL_STEEL_CHAR_Q;
5889 case 0x165c: // (green steel)
5890 element = EL_STEEL_CHAR_R;
5893 case 0x165d: // (green steel)
5894 element = EL_STEEL_CHAR_S;
5897 case 0x165e: // (green steel)
5898 element = EL_STEEL_CHAR_T;
5901 case 0x165f: // (green steel)
5902 element = EL_STEEL_CHAR_U;
5905 case 0x1660: // (green steel)
5906 element = EL_STEEL_CHAR_V;
5909 case 0x1661: // (green steel)
5910 element = EL_STEEL_CHAR_W;
5913 case 0x1662: // (green steel)
5914 element = EL_STEEL_CHAR_X;
5917 case 0x1663: // (green steel)
5918 element = EL_STEEL_CHAR_Y;
5921 case 0x1664: // (green steel)
5922 element = EL_STEEL_CHAR_Z;
5925 case 0x1665: // (green steel)
5926 element = EL_STEEL_CHAR_AUMLAUT;
5929 case 0x1666: // (green steel)
5930 element = EL_STEEL_CHAR_OUMLAUT;
5933 case 0x1667: // (green steel)
5934 element = EL_STEEL_CHAR_UUMLAUT;
5937 case 0x1668: // (green steel)
5938 element = EL_STEEL_CHAR_0;
5941 case 0x1669: // (green steel)
5942 element = EL_STEEL_CHAR_1;
5945 case 0x166a: // (green steel)
5946 element = EL_STEEL_CHAR_2;
5949 case 0x166b: // (green steel)
5950 element = EL_STEEL_CHAR_3;
5953 case 0x166c: // (green steel)
5954 element = EL_STEEL_CHAR_4;
5957 case 0x166d: // (green steel)
5958 element = EL_STEEL_CHAR_5;
5961 case 0x166e: // (green steel)
5962 element = EL_STEEL_CHAR_6;
5965 case 0x166f: // (green steel)
5966 element = EL_STEEL_CHAR_7;
5969 case 0x1670: // (green steel)
5970 element = EL_STEEL_CHAR_8;
5973 case 0x1671: // (green steel)
5974 element = EL_STEEL_CHAR_9;
5977 case 0x1672: // (green steel)
5978 element = EL_STEEL_CHAR_PERIOD;
5981 case 0x1673: // (green steel)
5982 element = EL_STEEL_CHAR_EXCLAM;
5985 case 0x1674: // (green steel)
5986 element = EL_STEEL_CHAR_COLON;
5989 case 0x1675: // (green steel)
5990 element = EL_STEEL_CHAR_LESS;
5993 case 0x1676: // (green steel)
5994 element = EL_STEEL_CHAR_GREATER;
5997 case 0x1677: // (green steel)
5998 element = EL_STEEL_CHAR_QUESTION;
6001 case 0x1678: // (green steel)
6002 element = EL_STEEL_CHAR_COPYRIGHT;
6005 case 0x1679: // (green steel)
6006 element = EL_STEEL_CHAR_UP;
6009 case 0x167a: // (green steel)
6010 element = EL_STEEL_CHAR_DOWN;
6013 case 0x167b: // (green steel)
6014 element = EL_STEEL_CHAR_BUTTON;
6017 case 0x167c: // (green steel)
6018 element = EL_STEEL_CHAR_PLUS;
6021 case 0x167d: // (green steel)
6022 element = EL_STEEL_CHAR_MINUS;
6025 case 0x167e: // (green steel)
6026 element = EL_STEEL_CHAR_APOSTROPHE;
6029 case 0x167f: // (green steel)
6030 element = EL_STEEL_CHAR_PARENLEFT;
6033 case 0x1680: // (green steel)
6034 element = EL_STEEL_CHAR_PARENRIGHT;
6037 case 0x1681: // gate (red)
6038 element = EL_EM_GATE_1;
6041 case 0x1682: // secret gate (red)
6042 element = EL_EM_GATE_1_GRAY;
6045 case 0x1683: // gate (yellow)
6046 element = EL_EM_GATE_2;
6049 case 0x1684: // secret gate (yellow)
6050 element = EL_EM_GATE_2_GRAY;
6053 case 0x1685: // gate (blue)
6054 element = EL_EM_GATE_4;
6057 case 0x1686: // secret gate (blue)
6058 element = EL_EM_GATE_4_GRAY;
6061 case 0x1687: // gate (green)
6062 element = EL_EM_GATE_3;
6065 case 0x1688: // secret gate (green)
6066 element = EL_EM_GATE_3_GRAY;
6069 case 0x1689: // gate (white)
6070 element = EL_DC_GATE_WHITE;
6073 case 0x168a: // secret gate (white)
6074 element = EL_DC_GATE_WHITE_GRAY;
6077 case 0x168b: // secret gate (no key)
6078 element = EL_DC_GATE_FAKE_GRAY;
6082 element = EL_ROBOT_WHEEL;
6086 element = EL_DC_TIMEGATE_SWITCH;
6090 element = EL_ACID_POOL_BOTTOM;
6094 element = EL_ACID_POOL_TOPLEFT;
6098 element = EL_ACID_POOL_TOPRIGHT;
6102 element = EL_ACID_POOL_BOTTOMLEFT;
6106 element = EL_ACID_POOL_BOTTOMRIGHT;
6110 element = EL_STEELWALL;
6114 element = EL_STEELWALL_SLIPPERY;
6117 case 0x1695: // steel wall (not round)
6118 element = EL_STEELWALL;
6121 case 0x1696: // steel wall (left)
6122 element = EL_DC_STEELWALL_1_LEFT;
6125 case 0x1697: // steel wall (bottom)
6126 element = EL_DC_STEELWALL_1_BOTTOM;
6129 case 0x1698: // steel wall (right)
6130 element = EL_DC_STEELWALL_1_RIGHT;
6133 case 0x1699: // steel wall (top)
6134 element = EL_DC_STEELWALL_1_TOP;
6137 case 0x169a: // steel wall (left/bottom)
6138 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6141 case 0x169b: // steel wall (right/bottom)
6142 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6145 case 0x169c: // steel wall (right/top)
6146 element = EL_DC_STEELWALL_1_TOPRIGHT;
6149 case 0x169d: // steel wall (left/top)
6150 element = EL_DC_STEELWALL_1_TOPLEFT;
6153 case 0x169e: // steel wall (right/bottom small)
6154 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6157 case 0x169f: // steel wall (left/bottom small)
6158 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6161 case 0x16a0: // steel wall (right/top small)
6162 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6165 case 0x16a1: // steel wall (left/top small)
6166 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6169 case 0x16a2: // steel wall (left/right)
6170 element = EL_DC_STEELWALL_1_VERTICAL;
6173 case 0x16a3: // steel wall (top/bottom)
6174 element = EL_DC_STEELWALL_1_HORIZONTAL;
6177 case 0x16a4: // steel wall 2 (left end)
6178 element = EL_DC_STEELWALL_2_LEFT;
6181 case 0x16a5: // steel wall 2 (right end)
6182 element = EL_DC_STEELWALL_2_RIGHT;
6185 case 0x16a6: // steel wall 2 (top end)
6186 element = EL_DC_STEELWALL_2_TOP;
6189 case 0x16a7: // steel wall 2 (bottom end)
6190 element = EL_DC_STEELWALL_2_BOTTOM;
6193 case 0x16a8: // steel wall 2 (left/right)
6194 element = EL_DC_STEELWALL_2_HORIZONTAL;
6197 case 0x16a9: // steel wall 2 (up/down)
6198 element = EL_DC_STEELWALL_2_VERTICAL;
6201 case 0x16aa: // steel wall 2 (mid)
6202 element = EL_DC_STEELWALL_2_MIDDLE;
6206 element = EL_SIGN_EXCLAMATION;
6210 element = EL_SIGN_RADIOACTIVITY;
6214 element = EL_SIGN_STOP;
6218 element = EL_SIGN_WHEELCHAIR;
6222 element = EL_SIGN_PARKING;
6226 element = EL_SIGN_NO_ENTRY;
6230 element = EL_SIGN_HEART;
6234 element = EL_SIGN_GIVE_WAY;
6238 element = EL_SIGN_ENTRY_FORBIDDEN;
6242 element = EL_SIGN_EMERGENCY_EXIT;
6246 element = EL_SIGN_YIN_YANG;
6250 element = EL_WALL_EMERALD;
6254 element = EL_WALL_DIAMOND;
6258 element = EL_WALL_PEARL;
6262 element = EL_WALL_CRYSTAL;
6266 element = EL_INVISIBLE_WALL;
6270 element = EL_INVISIBLE_STEELWALL;
6274 // EL_INVISIBLE_SAND
6277 element = EL_LIGHT_SWITCH;
6281 element = EL_ENVELOPE_1;
6285 if (element >= 0x0117 && element <= 0x036e) // (?)
6286 element = EL_DIAMOND;
6287 else if (element >= 0x042d && element <= 0x0684) // (?)
6288 element = EL_EMERALD;
6289 else if (element >= 0x157c && element <= 0x158b)
6291 else if (element >= 0x1590 && element <= 0x159f)
6292 element = EL_DC_LANDMINE;
6293 else if (element >= 0x16bc && element <= 0x16cb)
6294 element = EL_INVISIBLE_SAND;
6297 Warn("unknown Diamond Caves element 0x%04x", element);
6299 element = EL_UNKNOWN;
6304 return getMappedElement(element);
6307 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6309 byte header[DC_LEVEL_HEADER_SIZE];
6311 int envelope_header_pos = 62;
6312 int envelope_content_pos = 94;
6313 int level_name_pos = 251;
6314 int level_author_pos = 292;
6315 int envelope_header_len;
6316 int envelope_content_len;
6318 int level_author_len;
6320 int num_yamyam_contents;
6323 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6325 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6327 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6329 header[i * 2 + 0] = header_word >> 8;
6330 header[i * 2 + 1] = header_word & 0xff;
6333 // read some values from level header to check level decoding integrity
6334 fieldx = header[6] | (header[7] << 8);
6335 fieldy = header[8] | (header[9] << 8);
6336 num_yamyam_contents = header[60] | (header[61] << 8);
6338 // do some simple sanity checks to ensure that level was correctly decoded
6339 if (fieldx < 1 || fieldx > 256 ||
6340 fieldy < 1 || fieldy > 256 ||
6341 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6343 level->no_valid_file = TRUE;
6345 Warn("cannot decode level from stream -- using empty level");
6350 // maximum envelope header size is 31 bytes
6351 envelope_header_len = header[envelope_header_pos];
6352 // maximum envelope content size is 110 (156?) bytes
6353 envelope_content_len = header[envelope_content_pos];
6355 // maximum level title size is 40 bytes
6356 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6357 // maximum level author size is 30 (51?) bytes
6358 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6362 for (i = 0; i < envelope_header_len; i++)
6363 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6364 level->envelope[0].text[envelope_size++] =
6365 header[envelope_header_pos + 1 + i];
6367 if (envelope_header_len > 0 && envelope_content_len > 0)
6369 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6370 level->envelope[0].text[envelope_size++] = '\n';
6371 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6372 level->envelope[0].text[envelope_size++] = '\n';
6375 for (i = 0; i < envelope_content_len; i++)
6376 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6377 level->envelope[0].text[envelope_size++] =
6378 header[envelope_content_pos + 1 + i];
6380 level->envelope[0].text[envelope_size] = '\0';
6382 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6383 level->envelope[0].ysize = 10;
6384 level->envelope[0].autowrap = TRUE;
6385 level->envelope[0].centered = TRUE;
6387 for (i = 0; i < level_name_len; i++)
6388 level->name[i] = header[level_name_pos + 1 + i];
6389 level->name[level_name_len] = '\0';
6391 for (i = 0; i < level_author_len; i++)
6392 level->author[i] = header[level_author_pos + 1 + i];
6393 level->author[level_author_len] = '\0';
6395 num_yamyam_contents = header[60] | (header[61] << 8);
6396 level->num_yamyam_contents =
6397 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6399 for (i = 0; i < num_yamyam_contents; i++)
6401 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6403 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6404 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6406 if (i < MAX_ELEMENT_CONTENTS)
6407 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6411 fieldx = header[6] | (header[7] << 8);
6412 fieldy = header[8] | (header[9] << 8);
6413 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6414 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6416 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6418 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6419 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6421 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6422 level->field[x][y] = getMappedElement_DC(element_dc);
6425 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6426 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6427 level->field[x][y] = EL_PLAYER_1;
6429 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6430 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6431 level->field[x][y] = EL_PLAYER_2;
6433 level->gems_needed = header[18] | (header[19] << 8);
6435 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6436 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6437 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6438 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6439 level->score[SC_NUT] = header[28] | (header[29] << 8);
6440 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6441 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6442 level->score[SC_BUG] = header[34] | (header[35] << 8);
6443 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6444 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6445 level->score[SC_KEY] = header[40] | (header[41] << 8);
6446 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6448 level->time = header[44] | (header[45] << 8);
6450 level->amoeba_speed = header[46] | (header[47] << 8);
6451 level->time_light = header[48] | (header[49] << 8);
6452 level->time_timegate = header[50] | (header[51] << 8);
6453 level->time_wheel = header[52] | (header[53] << 8);
6454 level->time_magic_wall = header[54] | (header[55] << 8);
6455 level->extra_time = header[56] | (header[57] << 8);
6456 level->shield_normal_time = header[58] | (header[59] << 8);
6458 // shield and extra time elements do not have a score
6459 level->score[SC_SHIELD] = 0;
6460 level->extra_time_score = 0;
6462 // set time for normal and deadly shields to the same value
6463 level->shield_deadly_time = level->shield_normal_time;
6465 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6466 // can slip down from flat walls, like normal walls and steel walls
6467 level->em_slippery_gems = TRUE;
6469 // time score is counted for each 10 seconds left in Diamond Caves levels
6470 level->time_score_base = 10;
6473 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6474 struct LevelFileInfo *level_file_info,
6475 boolean level_info_only)
6477 char *filename = level_file_info->filename;
6479 int num_magic_bytes = 8;
6480 char magic_bytes[num_magic_bytes + 1];
6481 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6483 if (!(file = openFile(filename, MODE_READ)))
6485 level->no_valid_file = TRUE;
6487 if (!level_info_only)
6488 Warn("cannot read level '%s' -- using empty level", filename);
6493 // fseek(file, 0x0000, SEEK_SET);
6495 if (level_file_info->packed)
6497 // read "magic bytes" from start of file
6498 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6499 magic_bytes[0] = '\0';
6501 // check "magic bytes" for correct file format
6502 if (!strPrefix(magic_bytes, "DC2"))
6504 level->no_valid_file = TRUE;
6506 Warn("unknown DC level file '%s' -- using empty level", filename);
6511 if (strPrefix(magic_bytes, "DC2Win95") ||
6512 strPrefix(magic_bytes, "DC2Win98"))
6514 int position_first_level = 0x00fa;
6515 int extra_bytes = 4;
6518 // advance file stream to first level inside the level package
6519 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6521 // each block of level data is followed by block of non-level data
6522 num_levels_to_skip *= 2;
6524 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6525 while (num_levels_to_skip >= 0)
6527 // advance file stream to next level inside the level package
6528 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6530 level->no_valid_file = TRUE;
6532 Warn("cannot fseek in file '%s' -- using empty level", filename);
6537 // skip apparently unused extra bytes following each level
6538 ReadUnusedBytesFromFile(file, extra_bytes);
6540 // read size of next level in level package
6541 skip_bytes = getFile32BitLE(file);
6543 num_levels_to_skip--;
6548 level->no_valid_file = TRUE;
6550 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6556 LoadLevelFromFileStream_DC(file, level);
6562 // ----------------------------------------------------------------------------
6563 // functions for loading SB level
6564 // ----------------------------------------------------------------------------
6566 int getMappedElement_SB(int element_ascii, boolean use_ces)
6574 sb_element_mapping[] =
6576 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6577 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6578 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6579 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6580 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6581 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6582 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6583 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6590 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6591 if (element_ascii == sb_element_mapping[i].ascii)
6592 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6594 return EL_UNDEFINED;
6597 static void SetLevelSettings_SB(struct LevelInfo *level)
6601 level->use_step_counter = TRUE;
6604 level->score[SC_TIME_BONUS] = 0;
6605 level->time_score_base = 1;
6606 level->rate_time_over_score = TRUE;
6609 level->auto_exit_sokoban = TRUE;
6612 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6613 struct LevelFileInfo *level_file_info,
6614 boolean level_info_only)
6616 char *filename = level_file_info->filename;
6617 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6618 char last_comment[MAX_LINE_LEN];
6619 char level_name[MAX_LINE_LEN];
6622 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6623 boolean read_continued_line = FALSE;
6624 boolean reading_playfield = FALSE;
6625 boolean got_valid_playfield_line = FALSE;
6626 boolean invalid_playfield_char = FALSE;
6627 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6628 int file_level_nr = 0;
6629 int x = 0, y = 0; // initialized to make compilers happy
6631 last_comment[0] = '\0';
6632 level_name[0] = '\0';
6634 if (!(file = openFile(filename, MODE_READ)))
6636 level->no_valid_file = TRUE;
6638 if (!level_info_only)
6639 Warn("cannot read level '%s' -- using empty level", filename);
6644 while (!checkEndOfFile(file))
6646 // level successfully read, but next level may follow here
6647 if (!got_valid_playfield_line && reading_playfield)
6649 // read playfield from single level file -- skip remaining file
6650 if (!level_file_info->packed)
6653 if (file_level_nr >= num_levels_to_skip)
6658 last_comment[0] = '\0';
6659 level_name[0] = '\0';
6661 reading_playfield = FALSE;
6664 got_valid_playfield_line = FALSE;
6666 // read next line of input file
6667 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6670 // cut trailing line break (this can be newline and/or carriage return)
6671 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6672 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6675 // copy raw input line for later use (mainly debugging output)
6676 strcpy(line_raw, line);
6678 if (read_continued_line)
6680 // append new line to existing line, if there is enough space
6681 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6682 strcat(previous_line, line_ptr);
6684 strcpy(line, previous_line); // copy storage buffer to line
6686 read_continued_line = FALSE;
6689 // if the last character is '\', continue at next line
6690 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6692 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6693 strcpy(previous_line, line); // copy line to storage buffer
6695 read_continued_line = TRUE;
6701 if (line[0] == '\0')
6704 // extract comment text from comment line
6707 for (line_ptr = line; *line_ptr; line_ptr++)
6708 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6711 strcpy(last_comment, line_ptr);
6716 // extract level title text from line containing level title
6717 if (line[0] == '\'')
6719 strcpy(level_name, &line[1]);
6721 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6722 level_name[strlen(level_name) - 1] = '\0';
6727 // skip lines containing only spaces (or empty lines)
6728 for (line_ptr = line; *line_ptr; line_ptr++)
6729 if (*line_ptr != ' ')
6731 if (*line_ptr == '\0')
6734 // at this point, we have found a line containing part of a playfield
6736 got_valid_playfield_line = TRUE;
6738 if (!reading_playfield)
6740 reading_playfield = TRUE;
6741 invalid_playfield_char = FALSE;
6743 for (x = 0; x < MAX_LEV_FIELDX; x++)
6744 for (y = 0; y < MAX_LEV_FIELDY; y++)
6745 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6750 // start with topmost tile row
6754 // skip playfield line if larger row than allowed
6755 if (y >= MAX_LEV_FIELDY)
6758 // start with leftmost tile column
6761 // read playfield elements from line
6762 for (line_ptr = line; *line_ptr; line_ptr++)
6764 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6766 // stop parsing playfield line if larger column than allowed
6767 if (x >= MAX_LEV_FIELDX)
6770 if (mapped_sb_element == EL_UNDEFINED)
6772 invalid_playfield_char = TRUE;
6777 level->field[x][y] = mapped_sb_element;
6779 // continue with next tile column
6782 level->fieldx = MAX(x, level->fieldx);
6785 if (invalid_playfield_char)
6787 // if first playfield line, treat invalid lines as comment lines
6789 reading_playfield = FALSE;
6794 // continue with next tile row
6802 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6803 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6805 if (!reading_playfield)
6807 level->no_valid_file = TRUE;
6809 Warn("cannot read level '%s' -- using empty level", filename);
6814 if (*level_name != '\0')
6816 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6817 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6819 else if (*last_comment != '\0')
6821 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6822 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6826 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6829 // set all empty fields beyond the border walls to invisible steel wall
6830 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6832 if ((x == 0 || x == level->fieldx - 1 ||
6833 y == 0 || y == level->fieldy - 1) &&
6834 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6835 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6836 level->field, level->fieldx, level->fieldy);
6839 // set special level settings for Sokoban levels
6840 SetLevelSettings_SB(level);
6842 if (load_xsb_to_ces)
6844 // special global settings can now be set in level template
6845 level->use_custom_template = TRUE;
6850 // -------------------------------------------------------------------------
6851 // functions for handling native levels
6852 // -------------------------------------------------------------------------
6854 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6855 struct LevelFileInfo *level_file_info,
6856 boolean level_info_only)
6860 // determine position of requested level inside level package
6861 if (level_file_info->packed)
6862 pos = level_file_info->nr - leveldir_current->first_level;
6864 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6865 level->no_valid_file = TRUE;
6868 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6869 struct LevelFileInfo *level_file_info,
6870 boolean level_info_only)
6872 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6873 level->no_valid_file = TRUE;
6876 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6877 struct LevelFileInfo *level_file_info,
6878 boolean level_info_only)
6882 // determine position of requested level inside level package
6883 if (level_file_info->packed)
6884 pos = level_file_info->nr - leveldir_current->first_level;
6886 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6887 level->no_valid_file = TRUE;
6890 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6891 struct LevelFileInfo *level_file_info,
6892 boolean level_info_only)
6894 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6895 level->no_valid_file = TRUE;
6898 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6900 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6901 CopyNativeLevel_RND_to_BD(level);
6902 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6903 CopyNativeLevel_RND_to_EM(level);
6904 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6905 CopyNativeLevel_RND_to_SP(level);
6906 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6907 CopyNativeLevel_RND_to_MM(level);
6910 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6912 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6913 CopyNativeLevel_BD_to_RND(level);
6914 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6915 CopyNativeLevel_EM_to_RND(level);
6916 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6917 CopyNativeLevel_SP_to_RND(level);
6918 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6919 CopyNativeLevel_MM_to_RND(level);
6922 void SaveNativeLevel(struct LevelInfo *level)
6924 // saving native level files only supported for some game engines
6925 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6926 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6929 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6930 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6931 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6932 char *filename = getLevelFilenameFromBasename(basename);
6934 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6937 boolean success = FALSE;
6939 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6941 CopyNativeLevel_RND_to_BD(level);
6942 // CopyNativeTape_RND_to_BD(level);
6944 success = SaveNativeLevel_BD(filename);
6946 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6948 CopyNativeLevel_RND_to_SP(level);
6949 CopyNativeTape_RND_to_SP(level);
6951 success = SaveNativeLevel_SP(filename);
6955 Request("Native level file saved!", REQ_CONFIRM);
6957 Request("Failed to save native level file!", REQ_CONFIRM);
6961 // ----------------------------------------------------------------------------
6962 // functions for loading generic level
6963 // ----------------------------------------------------------------------------
6965 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6966 struct LevelFileInfo *level_file_info,
6967 boolean level_info_only)
6969 // always start with reliable default values
6970 setLevelInfoToDefaults(level, level_info_only, TRUE);
6972 switch (level_file_info->type)
6974 case LEVEL_FILE_TYPE_RND:
6975 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6978 case LEVEL_FILE_TYPE_BD:
6979 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6980 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6983 case LEVEL_FILE_TYPE_EM:
6984 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6985 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6988 case LEVEL_FILE_TYPE_SP:
6989 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6990 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6993 case LEVEL_FILE_TYPE_MM:
6994 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6995 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6998 case LEVEL_FILE_TYPE_DC:
6999 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7002 case LEVEL_FILE_TYPE_SB:
7003 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7007 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7011 // if level file is invalid, restore level structure to default values
7012 if (level->no_valid_file)
7013 setLevelInfoToDefaults(level, level_info_only, FALSE);
7015 if (check_special_flags("use_native_bd_game_engine"))
7016 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7018 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7019 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7021 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7022 CopyNativeLevel_Native_to_RND(level);
7025 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7027 static struct LevelFileInfo level_file_info;
7029 // always start with reliable default values
7030 setFileInfoToDefaults(&level_file_info);
7032 level_file_info.nr = 0; // unknown level number
7033 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7035 setString(&level_file_info.filename, filename);
7037 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7040 static void LoadLevel_InitVersion(struct LevelInfo *level)
7044 if (leveldir_current == NULL) // only when dumping level
7047 // all engine modifications also valid for levels which use latest engine
7048 if (level->game_version < VERSION_IDENT(3,2,0,5))
7050 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7051 level->time_score_base = 10;
7054 if (leveldir_current->latest_engine)
7056 // ---------- use latest game engine --------------------------------------
7058 /* For all levels which are forced to use the latest game engine version
7059 (normally all but user contributed, private and undefined levels), set
7060 the game engine version to the actual version; this allows for actual
7061 corrections in the game engine to take effect for existing, converted
7062 levels (from "classic" or other existing games) to make the emulation
7063 of the corresponding game more accurate, while (hopefully) not breaking
7064 existing levels created from other players. */
7066 level->game_version = GAME_VERSION_ACTUAL;
7068 /* Set special EM style gems behaviour: EM style gems slip down from
7069 normal, steel and growing wall. As this is a more fundamental change,
7070 it seems better to set the default behaviour to "off" (as it is more
7071 natural) and make it configurable in the level editor (as a property
7072 of gem style elements). Already existing converted levels (neither
7073 private nor contributed levels) are changed to the new behaviour. */
7075 if (level->file_version < FILE_VERSION_2_0)
7076 level->em_slippery_gems = TRUE;
7081 // ---------- use game engine the level was created with --------------------
7083 /* For all levels which are not forced to use the latest game engine
7084 version (normally user contributed, private and undefined levels),
7085 use the version of the game engine the levels were created for.
7087 Since 2.0.1, the game engine version is now directly stored
7088 in the level file (chunk "VERS"), so there is no need anymore
7089 to set the game version from the file version (except for old,
7090 pre-2.0 levels, where the game version is still taken from the
7091 file format version used to store the level -- see above). */
7093 // player was faster than enemies in 1.0.0 and before
7094 if (level->file_version == FILE_VERSION_1_0)
7095 for (i = 0; i < MAX_PLAYERS; i++)
7096 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7098 // default behaviour for EM style gems was "slippery" only in 2.0.1
7099 if (level->game_version == VERSION_IDENT(2,0,1,0))
7100 level->em_slippery_gems = TRUE;
7102 // springs could be pushed over pits before (pre-release version) 2.2.0
7103 if (level->game_version < VERSION_IDENT(2,2,0,0))
7104 level->use_spring_bug = TRUE;
7106 if (level->game_version < VERSION_IDENT(3,2,0,5))
7108 // time orb caused limited time in endless time levels before 3.2.0-5
7109 level->use_time_orb_bug = TRUE;
7111 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7112 level->block_snap_field = FALSE;
7114 // extra time score was same value as time left score before 3.2.0-5
7115 level->extra_time_score = level->score[SC_TIME_BONUS];
7118 if (level->game_version < VERSION_IDENT(3,2,0,7))
7120 // default behaviour for snapping was "not continuous" before 3.2.0-7
7121 level->continuous_snapping = FALSE;
7124 // only few elements were able to actively move into acid before 3.1.0
7125 // trigger settings did not exist before 3.1.0; set to default "any"
7126 if (level->game_version < VERSION_IDENT(3,1,0,0))
7128 // correct "can move into acid" settings (all zero in old levels)
7130 level->can_move_into_acid_bits = 0; // nothing can move into acid
7131 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7133 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7134 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7135 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7136 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7138 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7139 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7141 // correct trigger settings (stored as zero == "none" in old levels)
7143 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7145 int element = EL_CUSTOM_START + i;
7146 struct ElementInfo *ei = &element_info[element];
7148 for (j = 0; j < ei->num_change_pages; j++)
7150 struct ElementChangeInfo *change = &ei->change_page[j];
7152 change->trigger_player = CH_PLAYER_ANY;
7153 change->trigger_page = CH_PAGE_ANY;
7158 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7160 int element = EL_CUSTOM_256;
7161 struct ElementInfo *ei = &element_info[element];
7162 struct ElementChangeInfo *change = &ei->change_page[0];
7164 /* This is needed to fix a problem that was caused by a bugfix in function
7165 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7166 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7167 not replace walkable elements, but instead just placed the player on it,
7168 without placing the Sokoban field under the player). Unfortunately, this
7169 breaks "Snake Bite" style levels when the snake is halfway through a door
7170 that just closes (the snake head is still alive and can be moved in this
7171 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7172 player (without Sokoban element) which then gets killed as designed). */
7174 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7175 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7176 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7177 change->target_element = EL_PLAYER_1;
7180 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7181 if (level->game_version < VERSION_IDENT(3,2,5,0))
7183 /* This is needed to fix a problem that was caused by a bugfix in function
7184 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7185 corrects the behaviour when a custom element changes to another custom
7186 element with a higher element number that has change actions defined.
7187 Normally, only one change per frame is allowed for custom elements.
7188 Therefore, it is checked if a custom element already changed in the
7189 current frame; if it did, subsequent changes are suppressed.
7190 Unfortunately, this is only checked for element changes, but not for
7191 change actions, which are still executed. As the function above loops
7192 through all custom elements from lower to higher, an element change
7193 resulting in a lower CE number won't be checked again, while a target
7194 element with a higher number will also be checked, and potential change
7195 actions will get executed for this CE, too (which is wrong), while
7196 further changes are ignored (which is correct). As this bugfix breaks
7197 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7198 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7199 behaviour for existing levels and tapes that make use of this bug */
7201 level->use_action_after_change_bug = TRUE;
7204 // not centering level after relocating player was default only in 3.2.3
7205 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7206 level->shifted_relocation = TRUE;
7208 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7209 if (level->game_version < VERSION_IDENT(3,2,6,0))
7210 level->em_explodes_by_fire = TRUE;
7212 // levels were solved by the first player entering an exit up to 4.1.0.0
7213 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7214 level->solved_by_one_player = TRUE;
7216 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7217 if (level->game_version < VERSION_IDENT(4,1,1,1))
7218 level->use_life_bugs = TRUE;
7220 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7221 if (level->game_version < VERSION_IDENT(4,1,1,1))
7222 level->sb_objects_needed = FALSE;
7224 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7225 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7226 level->finish_dig_collect = FALSE;
7228 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7229 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7230 level->keep_walkable_ce = TRUE;
7233 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7235 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7238 // check if this level is (not) a Sokoban level
7239 for (y = 0; y < level->fieldy; y++)
7240 for (x = 0; x < level->fieldx; x++)
7241 if (!IS_SB_ELEMENT(Tile[x][y]))
7242 is_sokoban_level = FALSE;
7244 if (is_sokoban_level)
7246 // set special level settings for Sokoban levels
7247 SetLevelSettings_SB(level);
7251 static void LoadLevel_InitSettings(struct LevelInfo *level)
7253 // adjust level settings for (non-native) Sokoban-style levels
7254 LoadLevel_InitSettings_SB(level);
7256 // rename levels with title "nameless level" or if renaming is forced
7257 if (leveldir_current->empty_level_name != NULL &&
7258 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7259 leveldir_current->force_level_name))
7260 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7261 leveldir_current->empty_level_name, level_nr);
7264 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7268 // map elements that have changed in newer versions
7269 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7270 level->game_version);
7271 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7272 for (x = 0; x < 3; x++)
7273 for (y = 0; y < 3; y++)
7274 level->yamyam_content[i].e[x][y] =
7275 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7276 level->game_version);
7280 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7284 // map custom element change events that have changed in newer versions
7285 // (these following values were accidentally changed in version 3.0.1)
7286 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7287 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7289 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7291 int element = EL_CUSTOM_START + i;
7293 // order of checking and copying events to be mapped is important
7294 // (do not change the start and end value -- they are constant)
7295 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7297 if (HAS_CHANGE_EVENT(element, j - 2))
7299 SET_CHANGE_EVENT(element, j - 2, FALSE);
7300 SET_CHANGE_EVENT(element, j, TRUE);
7304 // order of checking and copying events to be mapped is important
7305 // (do not change the start and end value -- they are constant)
7306 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7308 if (HAS_CHANGE_EVENT(element, j - 1))
7310 SET_CHANGE_EVENT(element, j - 1, FALSE);
7311 SET_CHANGE_EVENT(element, j, TRUE);
7317 // initialize "can_change" field for old levels with only one change page
7318 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7320 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7322 int element = EL_CUSTOM_START + i;
7324 if (CAN_CHANGE(element))
7325 element_info[element].change->can_change = TRUE;
7329 // correct custom element values (for old levels without these options)
7330 if (level->game_version < VERSION_IDENT(3,1,1,0))
7332 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7334 int element = EL_CUSTOM_START + i;
7335 struct ElementInfo *ei = &element_info[element];
7337 if (ei->access_direction == MV_NO_DIRECTION)
7338 ei->access_direction = MV_ALL_DIRECTIONS;
7342 // correct custom element values (fix invalid values for all versions)
7345 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7347 int element = EL_CUSTOM_START + i;
7348 struct ElementInfo *ei = &element_info[element];
7350 for (j = 0; j < ei->num_change_pages; j++)
7352 struct ElementChangeInfo *change = &ei->change_page[j];
7354 if (change->trigger_player == CH_PLAYER_NONE)
7355 change->trigger_player = CH_PLAYER_ANY;
7357 if (change->trigger_side == CH_SIDE_NONE)
7358 change->trigger_side = CH_SIDE_ANY;
7363 // initialize "can_explode" field for old levels which did not store this
7364 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7365 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7367 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7369 int element = EL_CUSTOM_START + i;
7371 if (EXPLODES_1X1_OLD(element))
7372 element_info[element].explosion_type = EXPLODES_1X1;
7374 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7375 EXPLODES_SMASHED(element) ||
7376 EXPLODES_IMPACT(element)));
7380 // correct previously hard-coded move delay values for maze runner style
7381 if (level->game_version < VERSION_IDENT(3,1,1,0))
7383 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7385 int element = EL_CUSTOM_START + i;
7387 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7389 // previously hard-coded and therefore ignored
7390 element_info[element].move_delay_fixed = 9;
7391 element_info[element].move_delay_random = 0;
7396 // set some other uninitialized values of custom elements in older levels
7397 if (level->game_version < VERSION_IDENT(3,1,0,0))
7399 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7401 int element = EL_CUSTOM_START + i;
7403 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7405 element_info[element].explosion_delay = 17;
7406 element_info[element].ignition_delay = 8;
7410 // set mouse click change events to work for left/middle/right mouse button
7411 if (level->game_version < VERSION_IDENT(4,2,3,0))
7413 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7415 int element = EL_CUSTOM_START + i;
7416 struct ElementInfo *ei = &element_info[element];
7418 for (j = 0; j < ei->num_change_pages; j++)
7420 struct ElementChangeInfo *change = &ei->change_page[j];
7422 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7423 change->has_event[CE_PRESSED_BY_MOUSE] ||
7424 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7425 change->has_event[CE_MOUSE_PRESSED_ON_X])
7426 change->trigger_side = CH_SIDE_ANY;
7432 static void LoadLevel_InitElements(struct LevelInfo *level)
7434 LoadLevel_InitStandardElements(level);
7436 if (level->file_has_custom_elements)
7437 LoadLevel_InitCustomElements(level);
7439 // initialize element properties for level editor etc.
7440 InitElementPropertiesEngine(level->game_version);
7441 InitElementPropertiesGfxElement();
7444 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7448 // map elements that have changed in newer versions
7449 for (y = 0; y < level->fieldy; y++)
7450 for (x = 0; x < level->fieldx; x++)
7451 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7452 level->game_version);
7454 // clear unused playfield data (nicer if level gets resized in editor)
7455 for (x = 0; x < MAX_LEV_FIELDX; x++)
7456 for (y = 0; y < MAX_LEV_FIELDY; y++)
7457 if (x >= level->fieldx || y >= level->fieldy)
7458 level->field[x][y] = EL_EMPTY;
7460 // copy elements to runtime playfield array
7461 for (x = 0; x < MAX_LEV_FIELDX; x++)
7462 for (y = 0; y < MAX_LEV_FIELDY; y++)
7463 Tile[x][y] = level->field[x][y];
7465 // initialize level size variables for faster access
7466 lev_fieldx = level->fieldx;
7467 lev_fieldy = level->fieldy;
7469 // determine border element for this level
7470 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7471 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7476 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7478 struct LevelFileInfo *level_file_info = &level->file_info;
7480 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7481 CopyNativeLevel_RND_to_Native(level);
7484 static void LoadLevelTemplate_LoadAndInit(void)
7486 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7488 LoadLevel_InitVersion(&level_template);
7489 LoadLevel_InitElements(&level_template);
7490 LoadLevel_InitSettings(&level_template);
7492 ActivateLevelTemplate();
7495 void LoadLevelTemplate(int nr)
7497 if (!fileExists(getGlobalLevelTemplateFilename()))
7499 Warn("no level template found for this level");
7504 setLevelFileInfo(&level_template.file_info, nr);
7506 LoadLevelTemplate_LoadAndInit();
7509 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7511 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7513 LoadLevelTemplate_LoadAndInit();
7516 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7518 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7520 if (level.use_custom_template)
7522 if (network_level != NULL)
7523 LoadNetworkLevelTemplate(network_level);
7525 LoadLevelTemplate(-1);
7528 LoadLevel_InitVersion(&level);
7529 LoadLevel_InitElements(&level);
7530 LoadLevel_InitPlayfield(&level);
7531 LoadLevel_InitSettings(&level);
7533 LoadLevel_InitNativeEngines(&level);
7536 void LoadLevel(int nr)
7538 SetLevelSetInfo(leveldir_current->identifier, nr);
7540 setLevelFileInfo(&level.file_info, nr);
7542 LoadLevel_LoadAndInit(NULL);
7545 void LoadLevelInfoOnly(int nr)
7547 setLevelFileInfo(&level.file_info, nr);
7549 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7552 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7554 SetLevelSetInfo(network_level->leveldir_identifier,
7555 network_level->file_info.nr);
7557 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7559 LoadLevel_LoadAndInit(network_level);
7562 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7566 chunk_size += putFileVersion(file, level->file_version);
7567 chunk_size += putFileVersion(file, level->game_version);
7572 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7576 chunk_size += putFile16BitBE(file, level->creation_date.year);
7577 chunk_size += putFile8Bit(file, level->creation_date.month);
7578 chunk_size += putFile8Bit(file, level->creation_date.day);
7583 #if ENABLE_HISTORIC_CHUNKS
7584 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7588 putFile8Bit(file, level->fieldx);
7589 putFile8Bit(file, level->fieldy);
7591 putFile16BitBE(file, level->time);
7592 putFile16BitBE(file, level->gems_needed);
7594 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7595 putFile8Bit(file, level->name[i]);
7597 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7598 putFile8Bit(file, level->score[i]);
7600 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7601 for (y = 0; y < 3; y++)
7602 for (x = 0; x < 3; x++)
7603 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7604 level->yamyam_content[i].e[x][y]));
7605 putFile8Bit(file, level->amoeba_speed);
7606 putFile8Bit(file, level->time_magic_wall);
7607 putFile8Bit(file, level->time_wheel);
7608 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7609 level->amoeba_content));
7610 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7611 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7612 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7613 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7615 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7617 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7618 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7619 putFile32BitBE(file, level->can_move_into_acid_bits);
7620 putFile8Bit(file, level->dont_collide_with_bits);
7622 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7623 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7625 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7626 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7627 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7629 putFile8Bit(file, level->game_engine_type);
7631 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7635 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7640 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7641 chunk_size += putFile8Bit(file, level->name[i]);
7646 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7651 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7652 chunk_size += putFile8Bit(file, level->author[i]);
7657 #if ENABLE_HISTORIC_CHUNKS
7658 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7663 for (y = 0; y < level->fieldy; y++)
7664 for (x = 0; x < level->fieldx; x++)
7665 if (level->encoding_16bit_field)
7666 chunk_size += putFile16BitBE(file, level->field[x][y]);
7668 chunk_size += putFile8Bit(file, level->field[x][y]);
7674 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7679 for (y = 0; y < level->fieldy; y++)
7680 for (x = 0; x < level->fieldx; x++)
7681 chunk_size += putFile16BitBE(file, level->field[x][y]);
7686 #if ENABLE_HISTORIC_CHUNKS
7687 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7691 putFile8Bit(file, EL_YAMYAM);
7692 putFile8Bit(file, level->num_yamyam_contents);
7693 putFile8Bit(file, 0);
7694 putFile8Bit(file, 0);
7696 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7697 for (y = 0; y < 3; y++)
7698 for (x = 0; x < 3; x++)
7699 if (level->encoding_16bit_field)
7700 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7702 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7706 #if ENABLE_HISTORIC_CHUNKS
7707 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7710 int num_contents, content_xsize, content_ysize;
7711 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7713 if (element == EL_YAMYAM)
7715 num_contents = level->num_yamyam_contents;
7719 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7720 for (y = 0; y < 3; y++)
7721 for (x = 0; x < 3; x++)
7722 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7724 else if (element == EL_BD_AMOEBA)
7730 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7731 for (y = 0; y < 3; y++)
7732 for (x = 0; x < 3; x++)
7733 content_array[i][x][y] = EL_EMPTY;
7734 content_array[0][0][0] = level->amoeba_content;
7738 // chunk header already written -- write empty chunk data
7739 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7741 Warn("cannot save content for element '%d'", element);
7746 putFile16BitBE(file, element);
7747 putFile8Bit(file, num_contents);
7748 putFile8Bit(file, content_xsize);
7749 putFile8Bit(file, content_ysize);
7751 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7753 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7754 for (y = 0; y < 3; y++)
7755 for (x = 0; x < 3; x++)
7756 putFile16BitBE(file, content_array[i][x][y]);
7760 #if ENABLE_HISTORIC_CHUNKS
7761 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7763 int envelope_nr = element - EL_ENVELOPE_1;
7764 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7768 chunk_size += putFile16BitBE(file, element);
7769 chunk_size += putFile16BitBE(file, envelope_len);
7770 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7771 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7773 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7774 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7776 for (i = 0; i < envelope_len; i++)
7777 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7783 #if ENABLE_HISTORIC_CHUNKS
7784 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7785 int num_changed_custom_elements)
7789 putFile16BitBE(file, num_changed_custom_elements);
7791 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7793 int element = EL_CUSTOM_START + i;
7795 struct ElementInfo *ei = &element_info[element];
7797 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7799 if (check < num_changed_custom_elements)
7801 putFile16BitBE(file, element);
7802 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7809 if (check != num_changed_custom_elements) // should not happen
7810 Warn("inconsistent number of custom element properties");
7814 #if ENABLE_HISTORIC_CHUNKS
7815 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7816 int num_changed_custom_elements)
7820 putFile16BitBE(file, num_changed_custom_elements);
7822 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7824 int element = EL_CUSTOM_START + i;
7826 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7828 if (check < num_changed_custom_elements)
7830 putFile16BitBE(file, element);
7831 putFile16BitBE(file, element_info[element].change->target_element);
7838 if (check != num_changed_custom_elements) // should not happen
7839 Warn("inconsistent number of custom target elements");
7843 #if ENABLE_HISTORIC_CHUNKS
7844 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7845 int num_changed_custom_elements)
7847 int i, j, x, y, check = 0;
7849 putFile16BitBE(file, num_changed_custom_elements);
7851 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7853 int element = EL_CUSTOM_START + i;
7854 struct ElementInfo *ei = &element_info[element];
7856 if (ei->modified_settings)
7858 if (check < num_changed_custom_elements)
7860 putFile16BitBE(file, element);
7862 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7863 putFile8Bit(file, ei->description[j]);
7865 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7867 // some free bytes for future properties and padding
7868 WriteUnusedBytesToFile(file, 7);
7870 putFile8Bit(file, ei->use_gfx_element);
7871 putFile16BitBE(file, ei->gfx_element_initial);
7873 putFile8Bit(file, ei->collect_score_initial);
7874 putFile8Bit(file, ei->collect_count_initial);
7876 putFile16BitBE(file, ei->push_delay_fixed);
7877 putFile16BitBE(file, ei->push_delay_random);
7878 putFile16BitBE(file, ei->move_delay_fixed);
7879 putFile16BitBE(file, ei->move_delay_random);
7881 putFile16BitBE(file, ei->move_pattern);
7882 putFile8Bit(file, ei->move_direction_initial);
7883 putFile8Bit(file, ei->move_stepsize);
7885 for (y = 0; y < 3; y++)
7886 for (x = 0; x < 3; x++)
7887 putFile16BitBE(file, ei->content.e[x][y]);
7889 putFile32BitBE(file, ei->change->events);
7891 putFile16BitBE(file, ei->change->target_element);
7893 putFile16BitBE(file, ei->change->delay_fixed);
7894 putFile16BitBE(file, ei->change->delay_random);
7895 putFile16BitBE(file, ei->change->delay_frames);
7897 putFile16BitBE(file, ei->change->initial_trigger_element);
7899 putFile8Bit(file, ei->change->explode);
7900 putFile8Bit(file, ei->change->use_target_content);
7901 putFile8Bit(file, ei->change->only_if_complete);
7902 putFile8Bit(file, ei->change->use_random_replace);
7904 putFile8Bit(file, ei->change->random_percentage);
7905 putFile8Bit(file, ei->change->replace_when);
7907 for (y = 0; y < 3; y++)
7908 for (x = 0; x < 3; x++)
7909 putFile16BitBE(file, ei->change->content.e[x][y]);
7911 putFile8Bit(file, ei->slippery_type);
7913 // some free bytes for future properties and padding
7914 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7921 if (check != num_changed_custom_elements) // should not happen
7922 Warn("inconsistent number of custom element properties");
7926 #if ENABLE_HISTORIC_CHUNKS
7927 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7929 struct ElementInfo *ei = &element_info[element];
7932 // ---------- custom element base property values (96 bytes) ----------------
7934 putFile16BitBE(file, element);
7936 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7937 putFile8Bit(file, ei->description[i]);
7939 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7941 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7943 putFile8Bit(file, ei->num_change_pages);
7945 putFile16BitBE(file, ei->ce_value_fixed_initial);
7946 putFile16BitBE(file, ei->ce_value_random_initial);
7947 putFile8Bit(file, ei->use_last_ce_value);
7949 putFile8Bit(file, ei->use_gfx_element);
7950 putFile16BitBE(file, ei->gfx_element_initial);
7952 putFile8Bit(file, ei->collect_score_initial);
7953 putFile8Bit(file, ei->collect_count_initial);
7955 putFile8Bit(file, ei->drop_delay_fixed);
7956 putFile8Bit(file, ei->push_delay_fixed);
7957 putFile8Bit(file, ei->drop_delay_random);
7958 putFile8Bit(file, ei->push_delay_random);
7959 putFile16BitBE(file, ei->move_delay_fixed);
7960 putFile16BitBE(file, ei->move_delay_random);
7962 // bits 0 - 15 of "move_pattern" ...
7963 putFile16BitBE(file, ei->move_pattern & 0xffff);
7964 putFile8Bit(file, ei->move_direction_initial);
7965 putFile8Bit(file, ei->move_stepsize);
7967 putFile8Bit(file, ei->slippery_type);
7969 for (y = 0; y < 3; y++)
7970 for (x = 0; x < 3; x++)
7971 putFile16BitBE(file, ei->content.e[x][y]);
7973 putFile16BitBE(file, ei->move_enter_element);
7974 putFile16BitBE(file, ei->move_leave_element);
7975 putFile8Bit(file, ei->move_leave_type);
7977 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7978 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7980 putFile8Bit(file, ei->access_direction);
7982 putFile8Bit(file, ei->explosion_delay);
7983 putFile8Bit(file, ei->ignition_delay);
7984 putFile8Bit(file, ei->explosion_type);
7986 // some free bytes for future custom property values and padding
7987 WriteUnusedBytesToFile(file, 1);
7989 // ---------- change page property values (48 bytes) ------------------------
7991 for (i = 0; i < ei->num_change_pages; i++)
7993 struct ElementChangeInfo *change = &ei->change_page[i];
7994 unsigned int event_bits;
7996 // bits 0 - 31 of "has_event[]" ...
7998 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7999 if (change->has_event[j])
8000 event_bits |= (1u << j);
8001 putFile32BitBE(file, event_bits);
8003 putFile16BitBE(file, change->target_element);
8005 putFile16BitBE(file, change->delay_fixed);
8006 putFile16BitBE(file, change->delay_random);
8007 putFile16BitBE(file, change->delay_frames);
8009 putFile16BitBE(file, change->initial_trigger_element);
8011 putFile8Bit(file, change->explode);
8012 putFile8Bit(file, change->use_target_content);
8013 putFile8Bit(file, change->only_if_complete);
8014 putFile8Bit(file, change->use_random_replace);
8016 putFile8Bit(file, change->random_percentage);
8017 putFile8Bit(file, change->replace_when);
8019 for (y = 0; y < 3; y++)
8020 for (x = 0; x < 3; x++)
8021 putFile16BitBE(file, change->target_content.e[x][y]);
8023 putFile8Bit(file, change->can_change);
8025 putFile8Bit(file, change->trigger_side);
8027 putFile8Bit(file, change->trigger_player);
8028 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8029 log_2(change->trigger_page)));
8031 putFile8Bit(file, change->has_action);
8032 putFile8Bit(file, change->action_type);
8033 putFile8Bit(file, change->action_mode);
8034 putFile16BitBE(file, change->action_arg);
8036 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8038 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8039 if (change->has_event[j])
8040 event_bits |= (1u << (j - 32));
8041 putFile8Bit(file, event_bits);
8046 #if ENABLE_HISTORIC_CHUNKS
8047 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8049 struct ElementInfo *ei = &element_info[element];
8050 struct ElementGroupInfo *group = ei->group;
8053 putFile16BitBE(file, element);
8055 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8056 putFile8Bit(file, ei->description[i]);
8058 putFile8Bit(file, group->num_elements);
8060 putFile8Bit(file, ei->use_gfx_element);
8061 putFile16BitBE(file, ei->gfx_element_initial);
8063 putFile8Bit(file, group->choice_mode);
8065 // some free bytes for future values and padding
8066 WriteUnusedBytesToFile(file, 3);
8068 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8069 putFile16BitBE(file, group->element[i]);
8073 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8074 boolean write_element)
8076 int save_type = entry->save_type;
8077 int data_type = entry->data_type;
8078 int conf_type = entry->conf_type;
8079 int byte_mask = conf_type & CONF_MASK_BYTES;
8080 int element = entry->element;
8081 int default_value = entry->default_value;
8083 boolean modified = FALSE;
8085 if (byte_mask != CONF_MASK_MULTI_BYTES)
8087 void *value_ptr = entry->value;
8088 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8091 // check if any settings have been modified before saving them
8092 if (value != default_value)
8095 // do not save if explicitly told or if unmodified default settings
8096 if ((save_type == SAVE_CONF_NEVER) ||
8097 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8101 num_bytes += putFile16BitBE(file, element);
8103 num_bytes += putFile8Bit(file, conf_type);
8104 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8105 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8106 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8109 else if (data_type == TYPE_STRING)
8111 char *default_string = entry->default_string;
8112 char *string = (char *)(entry->value);
8113 int string_length = strlen(string);
8116 // check if any settings have been modified before saving them
8117 if (!strEqual(string, default_string))
8120 // do not save if explicitly told or if unmodified default settings
8121 if ((save_type == SAVE_CONF_NEVER) ||
8122 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8126 num_bytes += putFile16BitBE(file, element);
8128 num_bytes += putFile8Bit(file, conf_type);
8129 num_bytes += putFile16BitBE(file, string_length);
8131 for (i = 0; i < string_length; i++)
8132 num_bytes += putFile8Bit(file, string[i]);
8134 else if (data_type == TYPE_ELEMENT_LIST)
8136 int *element_array = (int *)(entry->value);
8137 int num_elements = *(int *)(entry->num_entities);
8140 // check if any settings have been modified before saving them
8141 for (i = 0; i < num_elements; i++)
8142 if (element_array[i] != default_value)
8145 // do not save if explicitly told or if unmodified default settings
8146 if ((save_type == SAVE_CONF_NEVER) ||
8147 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8151 num_bytes += putFile16BitBE(file, element);
8153 num_bytes += putFile8Bit(file, conf_type);
8154 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8156 for (i = 0; i < num_elements; i++)
8157 num_bytes += putFile16BitBE(file, element_array[i]);
8159 else if (data_type == TYPE_CONTENT_LIST)
8161 struct Content *content = (struct Content *)(entry->value);
8162 int num_contents = *(int *)(entry->num_entities);
8165 // check if any settings have been modified before saving them
8166 for (i = 0; i < num_contents; i++)
8167 for (y = 0; y < 3; y++)
8168 for (x = 0; x < 3; x++)
8169 if (content[i].e[x][y] != default_value)
8172 // do not save if explicitly told or if unmodified default settings
8173 if ((save_type == SAVE_CONF_NEVER) ||
8174 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8178 num_bytes += putFile16BitBE(file, element);
8180 num_bytes += putFile8Bit(file, conf_type);
8181 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8183 for (i = 0; i < num_contents; i++)
8184 for (y = 0; y < 3; y++)
8185 for (x = 0; x < 3; x++)
8186 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8192 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8197 li = *level; // copy level data into temporary buffer
8199 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8200 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8205 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8210 li = *level; // copy level data into temporary buffer
8212 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8213 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8218 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8220 int envelope_nr = element - EL_ENVELOPE_1;
8224 chunk_size += putFile16BitBE(file, element);
8226 // copy envelope data into temporary buffer
8227 xx_envelope = level->envelope[envelope_nr];
8229 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8230 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8235 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8237 struct ElementInfo *ei = &element_info[element];
8241 chunk_size += putFile16BitBE(file, element);
8243 xx_ei = *ei; // copy element data into temporary buffer
8245 // set default description string for this specific element
8246 strcpy(xx_default_description, getDefaultElementDescription(ei));
8248 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8249 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8251 for (i = 0; i < ei->num_change_pages; i++)
8253 struct ElementChangeInfo *change = &ei->change_page[i];
8255 xx_current_change_page = i;
8257 xx_change = *change; // copy change data into temporary buffer
8260 setEventBitsFromEventFlags(change);
8262 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8263 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8270 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8272 struct ElementInfo *ei = &element_info[element];
8273 struct ElementGroupInfo *group = ei->group;
8277 chunk_size += putFile16BitBE(file, element);
8279 xx_ei = *ei; // copy element data into temporary buffer
8280 xx_group = *group; // copy group data into temporary buffer
8282 // set default description string for this specific element
8283 strcpy(xx_default_description, getDefaultElementDescription(ei));
8285 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8286 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8291 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8293 struct ElementInfo *ei = &element_info[element];
8297 chunk_size += putFile16BitBE(file, element);
8299 xx_ei = *ei; // copy element data into temporary buffer
8301 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8302 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8307 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8308 boolean save_as_template)
8314 if (!(file = fopen(filename, MODE_WRITE)))
8316 Warn("cannot save level file '%s'", filename);
8321 level->file_version = FILE_VERSION_ACTUAL;
8322 level->game_version = GAME_VERSION_ACTUAL;
8324 level->creation_date = getCurrentDate();
8326 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8327 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8329 chunk_size = SaveLevel_VERS(NULL, level);
8330 putFileChunkBE(file, "VERS", chunk_size);
8331 SaveLevel_VERS(file, level);
8333 chunk_size = SaveLevel_DATE(NULL, level);
8334 putFileChunkBE(file, "DATE", chunk_size);
8335 SaveLevel_DATE(file, level);
8337 chunk_size = SaveLevel_NAME(NULL, level);
8338 putFileChunkBE(file, "NAME", chunk_size);
8339 SaveLevel_NAME(file, level);
8341 chunk_size = SaveLevel_AUTH(NULL, level);
8342 putFileChunkBE(file, "AUTH", chunk_size);
8343 SaveLevel_AUTH(file, level);
8345 chunk_size = SaveLevel_INFO(NULL, level);
8346 putFileChunkBE(file, "INFO", chunk_size);
8347 SaveLevel_INFO(file, level);
8349 chunk_size = SaveLevel_BODY(NULL, level);
8350 putFileChunkBE(file, "BODY", chunk_size);
8351 SaveLevel_BODY(file, level);
8353 chunk_size = SaveLevel_ELEM(NULL, level);
8354 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8356 putFileChunkBE(file, "ELEM", chunk_size);
8357 SaveLevel_ELEM(file, level);
8360 for (i = 0; i < NUM_ENVELOPES; i++)
8362 int element = EL_ENVELOPE_1 + i;
8364 chunk_size = SaveLevel_NOTE(NULL, level, element);
8365 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8367 putFileChunkBE(file, "NOTE", chunk_size);
8368 SaveLevel_NOTE(file, level, element);
8372 // if not using template level, check for non-default custom/group elements
8373 if (!level->use_custom_template || save_as_template)
8375 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8377 int element = EL_CUSTOM_START + i;
8379 chunk_size = SaveLevel_CUSX(NULL, level, element);
8380 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8382 putFileChunkBE(file, "CUSX", chunk_size);
8383 SaveLevel_CUSX(file, level, element);
8387 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8389 int element = EL_GROUP_START + i;
8391 chunk_size = SaveLevel_GRPX(NULL, level, element);
8392 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8394 putFileChunkBE(file, "GRPX", chunk_size);
8395 SaveLevel_GRPX(file, level, element);
8399 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8401 int element = GET_EMPTY_ELEMENT(i);
8403 chunk_size = SaveLevel_EMPX(NULL, level, element);
8404 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8406 putFileChunkBE(file, "EMPX", chunk_size);
8407 SaveLevel_EMPX(file, level, element);
8414 SetFilePermissions(filename, PERMS_PRIVATE);
8417 void SaveLevel(int nr)
8419 char *filename = getDefaultLevelFilename(nr);
8421 SaveLevelFromFilename(&level, filename, FALSE);
8424 void SaveLevelTemplate(void)
8426 char *filename = getLocalLevelTemplateFilename();
8428 SaveLevelFromFilename(&level, filename, TRUE);
8431 boolean SaveLevelChecked(int nr)
8433 char *filename = getDefaultLevelFilename(nr);
8434 boolean new_level = !fileExists(filename);
8435 boolean level_saved = FALSE;
8437 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8442 Request("Level saved!", REQ_CONFIRM);
8450 void DumpLevel(struct LevelInfo *level)
8452 if (level->no_level_file || level->no_valid_file)
8454 Warn("cannot dump -- no valid level file found");
8460 Print("Level xxx (file version %08d, game version %08d)\n",
8461 level->file_version, level->game_version);
8464 Print("Level author: '%s'\n", level->author);
8465 Print("Level title: '%s'\n", level->name);
8467 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8469 Print("Level time: %d seconds\n", level->time);
8470 Print("Gems needed: %d\n", level->gems_needed);
8472 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8473 Print("Time for wheel: %d seconds\n", level->time_wheel);
8474 Print("Time for light: %d seconds\n", level->time_light);
8475 Print("Time for timegate: %d seconds\n", level->time_timegate);
8477 Print("Amoeba speed: %d\n", level->amoeba_speed);
8480 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8481 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8482 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8483 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8484 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8485 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8491 for (i = 0; i < NUM_ENVELOPES; i++)
8493 char *text = level->envelope[i].text;
8494 int text_len = strlen(text);
8495 boolean has_text = FALSE;
8497 for (j = 0; j < text_len; j++)
8498 if (text[j] != ' ' && text[j] != '\n')
8504 Print("Envelope %d:\n'%s'\n", i + 1, text);
8512 void DumpLevels(void)
8514 static LevelDirTree *dumplevel_leveldir = NULL;
8516 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8517 global.dumplevel_leveldir);
8519 if (dumplevel_leveldir == NULL)
8520 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8522 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8523 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8524 Fail("no such level number: %d", global.dumplevel_level_nr);
8526 leveldir_current = dumplevel_leveldir;
8528 LoadLevel(global.dumplevel_level_nr);
8535 // ============================================================================
8536 // tape file functions
8537 // ============================================================================
8539 static void setTapeInfoToDefaults(void)
8543 // always start with reliable default values (empty tape)
8546 // default values (also for pre-1.2 tapes) with only the first player
8547 tape.player_participates[0] = TRUE;
8548 for (i = 1; i < MAX_PLAYERS; i++)
8549 tape.player_participates[i] = FALSE;
8551 // at least one (default: the first) player participates in every tape
8552 tape.num_participating_players = 1;
8554 tape.property_bits = TAPE_PROPERTY_NONE;
8556 tape.level_nr = level_nr;
8558 tape.changed = FALSE;
8559 tape.solved = FALSE;
8561 tape.recording = FALSE;
8562 tape.playing = FALSE;
8563 tape.pausing = FALSE;
8565 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8566 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8568 tape.no_info_chunk = TRUE;
8569 tape.no_valid_file = FALSE;
8572 static int getTapePosSize(struct TapeInfo *tape)
8574 int tape_pos_size = 0;
8576 if (tape->use_key_actions)
8577 tape_pos_size += tape->num_participating_players;
8579 if (tape->use_mouse_actions)
8580 tape_pos_size += 3; // x and y position and mouse button mask
8582 tape_pos_size += 1; // tape action delay value
8584 return tape_pos_size;
8587 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8589 tape->use_key_actions = FALSE;
8590 tape->use_mouse_actions = FALSE;
8592 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8593 tape->use_key_actions = TRUE;
8595 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8596 tape->use_mouse_actions = TRUE;
8599 static int getTapeActionValue(struct TapeInfo *tape)
8601 return (tape->use_key_actions &&
8602 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8603 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8604 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8605 TAPE_ACTIONS_DEFAULT);
8608 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8610 tape->file_version = getFileVersion(file);
8611 tape->game_version = getFileVersion(file);
8616 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8620 tape->random_seed = getFile32BitBE(file);
8621 tape->date = getFile32BitBE(file);
8622 tape->length = getFile32BitBE(file);
8624 // read header fields that are new since version 1.2
8625 if (tape->file_version >= FILE_VERSION_1_2)
8627 byte store_participating_players = getFile8Bit(file);
8630 // since version 1.2, tapes store which players participate in the tape
8631 tape->num_participating_players = 0;
8632 for (i = 0; i < MAX_PLAYERS; i++)
8634 tape->player_participates[i] = FALSE;
8636 if (store_participating_players & (1 << i))
8638 tape->player_participates[i] = TRUE;
8639 tape->num_participating_players++;
8643 setTapeActionFlags(tape, getFile8Bit(file));
8645 tape->property_bits = getFile8Bit(file);
8646 tape->solved = getFile8Bit(file);
8648 engine_version = getFileVersion(file);
8649 if (engine_version > 0)
8650 tape->engine_version = engine_version;
8652 tape->engine_version = tape->game_version;
8658 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8660 tape->scr_fieldx = getFile8Bit(file);
8661 tape->scr_fieldy = getFile8Bit(file);
8666 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8668 char *level_identifier = NULL;
8669 int level_identifier_size;
8672 tape->no_info_chunk = FALSE;
8674 level_identifier_size = getFile16BitBE(file);
8676 level_identifier = checked_malloc(level_identifier_size);
8678 for (i = 0; i < level_identifier_size; i++)
8679 level_identifier[i] = getFile8Bit(file);
8681 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8682 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8684 checked_free(level_identifier);
8686 tape->level_nr = getFile16BitBE(file);
8688 chunk_size = 2 + level_identifier_size + 2;
8693 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8696 int tape_pos_size = getTapePosSize(tape);
8697 int chunk_size_expected = tape_pos_size * tape->length;
8699 if (chunk_size_expected != chunk_size)
8701 ReadUnusedBytesFromFile(file, chunk_size);
8702 return chunk_size_expected;
8705 for (i = 0; i < tape->length; i++)
8707 if (i >= MAX_TAPE_LEN)
8709 Warn("tape truncated -- size exceeds maximum tape size %d",
8712 // tape too large; read and ignore remaining tape data from this chunk
8713 for (;i < tape->length; i++)
8714 ReadUnusedBytesFromFile(file, tape_pos_size);
8719 if (tape->use_key_actions)
8721 for (j = 0; j < MAX_PLAYERS; j++)
8723 tape->pos[i].action[j] = MV_NONE;
8725 if (tape->player_participates[j])
8726 tape->pos[i].action[j] = getFile8Bit(file);
8730 if (tape->use_mouse_actions)
8732 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8733 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8734 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8737 tape->pos[i].delay = getFile8Bit(file);
8739 if (tape->file_version == FILE_VERSION_1_0)
8741 // eliminate possible diagonal moves in old tapes
8742 // this is only for backward compatibility
8744 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8745 byte action = tape->pos[i].action[0];
8746 int k, num_moves = 0;
8748 for (k = 0; k < 4; k++)
8750 if (action & joy_dir[k])
8752 tape->pos[i + num_moves].action[0] = joy_dir[k];
8754 tape->pos[i + num_moves].delay = 0;
8763 tape->length += num_moves;
8766 else if (tape->file_version < FILE_VERSION_2_0)
8768 // convert pre-2.0 tapes to new tape format
8770 if (tape->pos[i].delay > 1)
8773 tape->pos[i + 1] = tape->pos[i];
8774 tape->pos[i + 1].delay = 1;
8777 for (j = 0; j < MAX_PLAYERS; j++)
8778 tape->pos[i].action[j] = MV_NONE;
8779 tape->pos[i].delay--;
8786 if (checkEndOfFile(file))
8790 if (i != tape->length)
8791 chunk_size = tape_pos_size * i;
8796 static void LoadTape_SokobanSolution(char *filename)
8799 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8801 if (!(file = openFile(filename, MODE_READ)))
8803 tape.no_valid_file = TRUE;
8808 while (!checkEndOfFile(file))
8810 unsigned char c = getByteFromFile(file);
8812 if (checkEndOfFile(file))
8819 tape.pos[tape.length].action[0] = MV_UP;
8820 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8826 tape.pos[tape.length].action[0] = MV_DOWN;
8827 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8833 tape.pos[tape.length].action[0] = MV_LEFT;
8834 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8840 tape.pos[tape.length].action[0] = MV_RIGHT;
8841 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8849 // ignore white-space characters
8853 tape.no_valid_file = TRUE;
8855 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8863 if (tape.no_valid_file)
8866 tape.length_frames = GetTapeLengthFrames();
8867 tape.length_seconds = GetTapeLengthSeconds();
8870 void LoadTapeFromFilename(char *filename)
8872 char cookie[MAX_LINE_LEN];
8873 char chunk_name[CHUNK_ID_LEN + 1];
8877 // always start with reliable default values
8878 setTapeInfoToDefaults();
8880 if (strSuffix(filename, ".sln"))
8882 LoadTape_SokobanSolution(filename);
8887 if (!(file = openFile(filename, MODE_READ)))
8889 tape.no_valid_file = TRUE;
8894 getFileChunkBE(file, chunk_name, NULL);
8895 if (strEqual(chunk_name, "RND1"))
8897 getFile32BitBE(file); // not used
8899 getFileChunkBE(file, chunk_name, NULL);
8900 if (!strEqual(chunk_name, "TAPE"))
8902 tape.no_valid_file = TRUE;
8904 Warn("unknown format of tape file '%s'", filename);
8911 else // check for pre-2.0 file format with cookie string
8913 strcpy(cookie, chunk_name);
8914 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8916 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8917 cookie[strlen(cookie) - 1] = '\0';
8919 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8921 tape.no_valid_file = TRUE;
8923 Warn("unknown format of tape file '%s'", filename);
8930 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8932 tape.no_valid_file = TRUE;
8934 Warn("unsupported version of tape file '%s'", filename);
8941 // pre-2.0 tape files have no game version, so use file version here
8942 tape.game_version = tape.file_version;
8945 if (tape.file_version < FILE_VERSION_1_2)
8947 // tape files from versions before 1.2.0 without chunk structure
8948 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8949 LoadTape_BODY(file, 2 * tape.length, &tape);
8957 int (*loader)(File *, int, struct TapeInfo *);
8961 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8962 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8963 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8964 { "INFO", -1, LoadTape_INFO },
8965 { "BODY", -1, LoadTape_BODY },
8969 while (getFileChunkBE(file, chunk_name, &chunk_size))
8973 while (chunk_info[i].name != NULL &&
8974 !strEqual(chunk_name, chunk_info[i].name))
8977 if (chunk_info[i].name == NULL)
8979 Warn("unknown chunk '%s' in tape file '%s'",
8980 chunk_name, filename);
8982 ReadUnusedBytesFromFile(file, chunk_size);
8984 else if (chunk_info[i].size != -1 &&
8985 chunk_info[i].size != chunk_size)
8987 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8988 chunk_size, chunk_name, filename);
8990 ReadUnusedBytesFromFile(file, chunk_size);
8994 // call function to load this tape chunk
8995 int chunk_size_expected =
8996 (chunk_info[i].loader)(file, chunk_size, &tape);
8998 // the size of some chunks cannot be checked before reading other
8999 // chunks first (like "HEAD" and "BODY") that contain some header
9000 // information, so check them here
9001 if (chunk_size_expected != chunk_size)
9003 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9004 chunk_size, chunk_name, filename);
9012 tape.length_frames = GetTapeLengthFrames();
9013 tape.length_seconds = GetTapeLengthSeconds();
9016 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9018 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9020 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9021 tape.engine_version);
9025 void LoadTape(int nr)
9027 char *filename = getTapeFilename(nr);
9029 LoadTapeFromFilename(filename);
9032 void LoadSolutionTape(int nr)
9034 char *filename = getSolutionTapeFilename(nr);
9036 LoadTapeFromFilename(filename);
9038 if (TAPE_IS_EMPTY(tape))
9040 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9041 level.native_bd_level->replay != NULL)
9042 CopyNativeTape_BD_to_RND(&level);
9043 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9044 level.native_sp_level->demo.is_available)
9045 CopyNativeTape_SP_to_RND(&level);
9049 void LoadScoreTape(char *score_tape_basename, int nr)
9051 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9053 LoadTapeFromFilename(filename);
9056 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9058 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9060 LoadTapeFromFilename(filename);
9063 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9065 // chunk required for team mode tapes with non-default screen size
9066 return (tape->num_participating_players > 1 &&
9067 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9068 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9071 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9073 putFileVersion(file, tape->file_version);
9074 putFileVersion(file, tape->game_version);
9077 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9080 byte store_participating_players = 0;
9082 // set bits for participating players for compact storage
9083 for (i = 0; i < MAX_PLAYERS; i++)
9084 if (tape->player_participates[i])
9085 store_participating_players |= (1 << i);
9087 putFile32BitBE(file, tape->random_seed);
9088 putFile32BitBE(file, tape->date);
9089 putFile32BitBE(file, tape->length);
9091 putFile8Bit(file, store_participating_players);
9093 putFile8Bit(file, getTapeActionValue(tape));
9095 putFile8Bit(file, tape->property_bits);
9096 putFile8Bit(file, tape->solved);
9098 putFileVersion(file, tape->engine_version);
9101 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9103 putFile8Bit(file, tape->scr_fieldx);
9104 putFile8Bit(file, tape->scr_fieldy);
9107 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9109 int level_identifier_size = strlen(tape->level_identifier) + 1;
9112 putFile16BitBE(file, level_identifier_size);
9114 for (i = 0; i < level_identifier_size; i++)
9115 putFile8Bit(file, tape->level_identifier[i]);
9117 putFile16BitBE(file, tape->level_nr);
9120 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9124 for (i = 0; i < tape->length; i++)
9126 if (tape->use_key_actions)
9128 for (j = 0; j < MAX_PLAYERS; j++)
9129 if (tape->player_participates[j])
9130 putFile8Bit(file, tape->pos[i].action[j]);
9133 if (tape->use_mouse_actions)
9135 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9136 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9137 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9140 putFile8Bit(file, tape->pos[i].delay);
9144 void SaveTapeToFilename(char *filename)
9148 int info_chunk_size;
9149 int body_chunk_size;
9151 if (!(file = fopen(filename, MODE_WRITE)))
9153 Warn("cannot save level recording file '%s'", filename);
9158 tape_pos_size = getTapePosSize(&tape);
9160 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9161 body_chunk_size = tape_pos_size * tape.length;
9163 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9164 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9166 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9167 SaveTape_VERS(file, &tape);
9169 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9170 SaveTape_HEAD(file, &tape);
9172 if (checkSaveTape_SCRN(&tape))
9174 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9175 SaveTape_SCRN(file, &tape);
9178 putFileChunkBE(file, "INFO", info_chunk_size);
9179 SaveTape_INFO(file, &tape);
9181 putFileChunkBE(file, "BODY", body_chunk_size);
9182 SaveTape_BODY(file, &tape);
9186 SetFilePermissions(filename, PERMS_PRIVATE);
9189 static void SaveTapeExt(char *filename)
9193 tape.file_version = FILE_VERSION_ACTUAL;
9194 tape.game_version = GAME_VERSION_ACTUAL;
9196 tape.num_participating_players = 0;
9198 // count number of participating players
9199 for (i = 0; i < MAX_PLAYERS; i++)
9200 if (tape.player_participates[i])
9201 tape.num_participating_players++;
9203 SaveTapeToFilename(filename);
9205 tape.changed = FALSE;
9208 void SaveTape(int nr)
9210 char *filename = getTapeFilename(nr);
9212 InitTapeDirectory(leveldir_current->subdir);
9214 SaveTapeExt(filename);
9217 void SaveScoreTape(int nr)
9219 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9221 // used instead of "leveldir_current->subdir" (for network games)
9222 InitScoreTapeDirectory(levelset.identifier, nr);
9224 SaveTapeExt(filename);
9227 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9228 unsigned int req_state_added)
9230 char *filename = getTapeFilename(nr);
9231 boolean new_tape = !fileExists(filename);
9232 boolean tape_saved = FALSE;
9234 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9239 Request(msg_saved, REQ_CONFIRM | req_state_added);
9247 boolean SaveTapeChecked(int nr)
9249 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9252 boolean SaveTapeChecked_LevelSolved(int nr)
9254 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9255 "Level solved! Tape saved!", REQ_STAY_OPEN);
9258 void DumpTape(struct TapeInfo *tape)
9260 int tape_frame_counter;
9263 if (tape->no_valid_file)
9265 Warn("cannot dump -- no valid tape file found");
9272 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9273 tape->level_nr, tape->file_version, tape->game_version);
9274 Print(" (effective engine version %08d)\n",
9275 tape->engine_version);
9276 Print("Level series identifier: '%s'\n", tape->level_identifier);
9278 Print("Solution tape: %s\n",
9279 tape->solved ? "yes" :
9280 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9282 Print("Special tape properties: ");
9283 if (tape->property_bits == TAPE_PROPERTY_NONE)
9285 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9286 Print("[em_random_bug]");
9287 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9288 Print("[game_speed]");
9289 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9291 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9292 Print("[single_step]");
9293 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9294 Print("[snapshot]");
9295 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9296 Print("[replayed]");
9297 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9298 Print("[tas_keys]");
9299 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9300 Print("[small_graphics]");
9303 int year2 = tape->date / 10000;
9304 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9305 int month_index_raw = (tape->date / 100) % 100;
9306 int month_index = month_index_raw % 12; // prevent invalid index
9307 int month = month_index + 1;
9308 int day = tape->date % 100;
9310 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9314 tape_frame_counter = 0;
9316 for (i = 0; i < tape->length; i++)
9318 if (i >= MAX_TAPE_LEN)
9323 for (j = 0; j < MAX_PLAYERS; j++)
9325 if (tape->player_participates[j])
9327 int action = tape->pos[i].action[j];
9329 Print("%d:%02x ", j, action);
9330 Print("[%c%c%c%c|%c%c] - ",
9331 (action & JOY_LEFT ? '<' : ' '),
9332 (action & JOY_RIGHT ? '>' : ' '),
9333 (action & JOY_UP ? '^' : ' '),
9334 (action & JOY_DOWN ? 'v' : ' '),
9335 (action & JOY_BUTTON_1 ? '1' : ' '),
9336 (action & JOY_BUTTON_2 ? '2' : ' '));
9340 Print("(%03d) ", tape->pos[i].delay);
9341 Print("[%05d]\n", tape_frame_counter);
9343 tape_frame_counter += tape->pos[i].delay;
9349 void DumpTapes(void)
9351 static LevelDirTree *dumptape_leveldir = NULL;
9353 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9354 global.dumptape_leveldir);
9356 if (dumptape_leveldir == NULL)
9357 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9359 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9360 global.dumptape_level_nr > dumptape_leveldir->last_level)
9361 Fail("no such level number: %d", global.dumptape_level_nr);
9363 leveldir_current = dumptape_leveldir;
9365 if (options.mytapes)
9366 LoadTape(global.dumptape_level_nr);
9368 LoadSolutionTape(global.dumptape_level_nr);
9376 // ============================================================================
9377 // score file functions
9378 // ============================================================================
9380 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9384 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9386 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9387 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9388 scores->entry[i].score = 0;
9389 scores->entry[i].time = 0;
9391 scores->entry[i].id = -1;
9392 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9393 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9394 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9395 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9396 strcpy(scores->entry[i].country_code, "??");
9399 scores->num_entries = 0;
9400 scores->last_added = -1;
9401 scores->last_added_local = -1;
9403 scores->updated = FALSE;
9404 scores->uploaded = FALSE;
9405 scores->tape_downloaded = FALSE;
9406 scores->force_last_added = FALSE;
9408 // The following values are intentionally not reset here:
9412 // - continue_playing
9413 // - continue_on_return
9416 static void setScoreInfoToDefaults(void)
9418 setScoreInfoToDefaultsExt(&scores);
9421 static void setServerScoreInfoToDefaults(void)
9423 setScoreInfoToDefaultsExt(&server_scores);
9426 static void LoadScore_OLD(int nr)
9429 char *filename = getScoreFilename(nr);
9430 char cookie[MAX_LINE_LEN];
9431 char line[MAX_LINE_LEN];
9435 if (!(file = fopen(filename, MODE_READ)))
9438 // check file identifier
9439 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9441 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9442 cookie[strlen(cookie) - 1] = '\0';
9444 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9446 Warn("unknown format of score file '%s'", filename);
9453 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9455 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9456 Warn("fscanf() failed; %s", strerror(errno));
9458 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9461 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9462 line[strlen(line) - 1] = '\0';
9464 for (line_ptr = line; *line_ptr; line_ptr++)
9466 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9468 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9469 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9478 static void ConvertScore_OLD(void)
9480 // only convert score to time for levels that rate playing time over score
9481 if (!level.rate_time_over_score)
9484 // convert old score to playing time for score-less levels (like Supaplex)
9485 int time_final_max = 999;
9488 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9490 int score = scores.entry[i].score;
9492 if (score > 0 && score < time_final_max)
9493 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9497 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9499 scores->file_version = getFileVersion(file);
9500 scores->game_version = getFileVersion(file);
9505 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9507 char *level_identifier = NULL;
9508 int level_identifier_size;
9511 level_identifier_size = getFile16BitBE(file);
9513 level_identifier = checked_malloc(level_identifier_size);
9515 for (i = 0; i < level_identifier_size; i++)
9516 level_identifier[i] = getFile8Bit(file);
9518 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9519 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9521 checked_free(level_identifier);
9523 scores->level_nr = getFile16BitBE(file);
9524 scores->num_entries = getFile16BitBE(file);
9526 chunk_size = 2 + level_identifier_size + 2 + 2;
9531 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9535 for (i = 0; i < scores->num_entries; i++)
9537 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9538 scores->entry[i].name[j] = getFile8Bit(file);
9540 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9543 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9548 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9552 for (i = 0; i < scores->num_entries; i++)
9553 scores->entry[i].score = getFile16BitBE(file);
9555 chunk_size = scores->num_entries * 2;
9560 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9564 for (i = 0; i < scores->num_entries; i++)
9565 scores->entry[i].score = getFile32BitBE(file);
9567 chunk_size = scores->num_entries * 4;
9572 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9576 for (i = 0; i < scores->num_entries; i++)
9577 scores->entry[i].time = getFile32BitBE(file);
9579 chunk_size = scores->num_entries * 4;
9584 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9588 for (i = 0; i < scores->num_entries; i++)
9590 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9591 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9593 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9596 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9601 void LoadScore(int nr)
9603 char *filename = getScoreFilename(nr);
9604 char cookie[MAX_LINE_LEN];
9605 char chunk_name[CHUNK_ID_LEN + 1];
9607 boolean old_score_file_format = FALSE;
9610 // always start with reliable default values
9611 setScoreInfoToDefaults();
9613 if (!(file = openFile(filename, MODE_READ)))
9616 getFileChunkBE(file, chunk_name, NULL);
9617 if (strEqual(chunk_name, "RND1"))
9619 getFile32BitBE(file); // not used
9621 getFileChunkBE(file, chunk_name, NULL);
9622 if (!strEqual(chunk_name, "SCOR"))
9624 Warn("unknown format of score file '%s'", filename);
9631 else // check for old file format with cookie string
9633 strcpy(cookie, chunk_name);
9634 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9636 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9637 cookie[strlen(cookie) - 1] = '\0';
9639 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9641 Warn("unknown format of score file '%s'", filename);
9648 old_score_file_format = TRUE;
9651 if (old_score_file_format)
9653 // score files from versions before 4.2.4.0 without chunk structure
9656 // convert score to time, if possible (mainly for Supaplex levels)
9665 int (*loader)(File *, int, struct ScoreInfo *);
9669 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9670 { "INFO", -1, LoadScore_INFO },
9671 { "NAME", -1, LoadScore_NAME },
9672 { "SCOR", -1, LoadScore_SCOR },
9673 { "SC4R", -1, LoadScore_SC4R },
9674 { "TIME", -1, LoadScore_TIME },
9675 { "TAPE", -1, LoadScore_TAPE },
9680 while (getFileChunkBE(file, chunk_name, &chunk_size))
9684 while (chunk_info[i].name != NULL &&
9685 !strEqual(chunk_name, chunk_info[i].name))
9688 if (chunk_info[i].name == NULL)
9690 Warn("unknown chunk '%s' in score file '%s'",
9691 chunk_name, filename);
9693 ReadUnusedBytesFromFile(file, chunk_size);
9695 else if (chunk_info[i].size != -1 &&
9696 chunk_info[i].size != chunk_size)
9698 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9699 chunk_size, chunk_name, filename);
9701 ReadUnusedBytesFromFile(file, chunk_size);
9705 // call function to load this score chunk
9706 int chunk_size_expected =
9707 (chunk_info[i].loader)(file, chunk_size, &scores);
9709 // the size of some chunks cannot be checked before reading other
9710 // chunks first (like "HEAD" and "BODY") that contain some header
9711 // information, so check them here
9712 if (chunk_size_expected != chunk_size)
9714 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9715 chunk_size, chunk_name, filename);
9724 #if ENABLE_HISTORIC_CHUNKS
9725 void SaveScore_OLD(int nr)
9728 char *filename = getScoreFilename(nr);
9731 // used instead of "leveldir_current->subdir" (for network games)
9732 InitScoreDirectory(levelset.identifier);
9734 if (!(file = fopen(filename, MODE_WRITE)))
9736 Warn("cannot save score for level %d", nr);
9741 fprintf(file, "%s\n\n", SCORE_COOKIE);
9743 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9744 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9748 SetFilePermissions(filename, PERMS_PRIVATE);
9752 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9754 putFileVersion(file, scores->file_version);
9755 putFileVersion(file, scores->game_version);
9758 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9760 int level_identifier_size = strlen(scores->level_identifier) + 1;
9763 putFile16BitBE(file, level_identifier_size);
9765 for (i = 0; i < level_identifier_size; i++)
9766 putFile8Bit(file, scores->level_identifier[i]);
9768 putFile16BitBE(file, scores->level_nr);
9769 putFile16BitBE(file, scores->num_entries);
9772 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9776 for (i = 0; i < scores->num_entries; i++)
9778 int name_size = strlen(scores->entry[i].name);
9780 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9781 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9785 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9789 for (i = 0; i < scores->num_entries; i++)
9790 putFile16BitBE(file, scores->entry[i].score);
9793 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9797 for (i = 0; i < scores->num_entries; i++)
9798 putFile32BitBE(file, scores->entry[i].score);
9801 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9805 for (i = 0; i < scores->num_entries; i++)
9806 putFile32BitBE(file, scores->entry[i].time);
9809 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9813 for (i = 0; i < scores->num_entries; i++)
9815 int size = strlen(scores->entry[i].tape_basename);
9817 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9818 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9822 static void SaveScoreToFilename(char *filename)
9825 int info_chunk_size;
9826 int name_chunk_size;
9827 int scor_chunk_size;
9828 int sc4r_chunk_size;
9829 int time_chunk_size;
9830 int tape_chunk_size;
9831 boolean has_large_score_values;
9834 if (!(file = fopen(filename, MODE_WRITE)))
9836 Warn("cannot save score file '%s'", filename);
9841 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9842 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9843 scor_chunk_size = scores.num_entries * 2;
9844 sc4r_chunk_size = scores.num_entries * 4;
9845 time_chunk_size = scores.num_entries * 4;
9846 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9848 has_large_score_values = FALSE;
9849 for (i = 0; i < scores.num_entries; i++)
9850 if (scores.entry[i].score > 0xffff)
9851 has_large_score_values = TRUE;
9853 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9854 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9856 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9857 SaveScore_VERS(file, &scores);
9859 putFileChunkBE(file, "INFO", info_chunk_size);
9860 SaveScore_INFO(file, &scores);
9862 putFileChunkBE(file, "NAME", name_chunk_size);
9863 SaveScore_NAME(file, &scores);
9865 if (has_large_score_values)
9867 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9868 SaveScore_SC4R(file, &scores);
9872 putFileChunkBE(file, "SCOR", scor_chunk_size);
9873 SaveScore_SCOR(file, &scores);
9876 putFileChunkBE(file, "TIME", time_chunk_size);
9877 SaveScore_TIME(file, &scores);
9879 putFileChunkBE(file, "TAPE", tape_chunk_size);
9880 SaveScore_TAPE(file, &scores);
9884 SetFilePermissions(filename, PERMS_PRIVATE);
9887 void SaveScore(int nr)
9889 char *filename = getScoreFilename(nr);
9892 // used instead of "leveldir_current->subdir" (for network games)
9893 InitScoreDirectory(levelset.identifier);
9895 scores.file_version = FILE_VERSION_ACTUAL;
9896 scores.game_version = GAME_VERSION_ACTUAL;
9898 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9899 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9900 scores.level_nr = level_nr;
9902 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9903 if (scores.entry[i].score == 0 &&
9904 scores.entry[i].time == 0 &&
9905 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9908 scores.num_entries = i;
9910 if (scores.num_entries == 0)
9913 SaveScoreToFilename(filename);
9916 static void LoadServerScoreFromCache(int nr)
9918 struct ScoreEntry score_entry;
9927 { &score_entry.score, FALSE, 0 },
9928 { &score_entry.time, FALSE, 0 },
9929 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9930 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9931 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9932 { &score_entry.id, FALSE, 0 },
9933 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9934 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9935 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9936 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9940 char *filename = getScoreCacheFilename(nr);
9941 SetupFileHash *score_hash = loadSetupFileHash(filename);
9944 server_scores.num_entries = 0;
9946 if (score_hash == NULL)
9949 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9951 score_entry = server_scores.entry[i];
9953 for (j = 0; score_mapping[j].value != NULL; j++)
9957 sprintf(token, "%02d.%d", i, j);
9959 char *value = getHashEntry(score_hash, token);
9964 if (score_mapping[j].is_string)
9966 char *score_value = (char *)score_mapping[j].value;
9967 int value_size = score_mapping[j].string_size;
9969 strncpy(score_value, value, value_size);
9970 score_value[value_size] = '\0';
9974 int *score_value = (int *)score_mapping[j].value;
9976 *score_value = atoi(value);
9979 server_scores.num_entries = i + 1;
9982 server_scores.entry[i] = score_entry;
9985 freeSetupFileHash(score_hash);
9988 void LoadServerScore(int nr, boolean download_score)
9990 if (!setup.use_api_server)
9993 // always start with reliable default values
9994 setServerScoreInfoToDefaults();
9996 // 1st step: load server scores from cache file (which may not exist)
9997 // (this should prevent reading it while the thread is writing to it)
9998 LoadServerScoreFromCache(nr);
10000 if (download_score && runtime.use_api_server)
10002 // 2nd step: download server scores from score server to cache file
10003 // (as thread, as it might time out if the server is not reachable)
10004 ApiGetScoreAsThread(nr);
10008 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10010 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10012 // if score tape not uploaded, ask for uploading missing tapes later
10013 if (!setup.has_remaining_tapes)
10014 setup.ask_for_remaining_tapes = TRUE;
10016 setup.provide_uploading_tapes = TRUE;
10017 setup.has_remaining_tapes = TRUE;
10019 SaveSetup_ServerSetup();
10022 void SaveServerScore(int nr, boolean tape_saved)
10024 if (!runtime.use_api_server)
10026 PrepareScoreTapesForUpload(leveldir_current->subdir);
10031 ApiAddScoreAsThread(nr, tape_saved, NULL);
10034 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10035 char *score_tape_filename)
10037 if (!runtime.use_api_server)
10040 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10043 void LoadLocalAndServerScore(int nr, boolean download_score)
10045 int last_added_local = scores.last_added_local;
10046 boolean force_last_added = scores.force_last_added;
10048 // needed if only showing server scores
10049 setScoreInfoToDefaults();
10051 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10054 // restore last added local score entry (before merging server scores)
10055 scores.last_added = scores.last_added_local = last_added_local;
10057 if (setup.use_api_server &&
10058 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10060 // load server scores from cache file and trigger update from server
10061 LoadServerScore(nr, download_score);
10063 // merge local scores with scores from server
10064 MergeServerScore();
10067 if (force_last_added)
10068 scores.force_last_added = force_last_added;
10072 // ============================================================================
10073 // setup file functions
10074 // ============================================================================
10076 #define TOKEN_STR_PLAYER_PREFIX "player_"
10079 static struct TokenInfo global_setup_tokens[] =
10083 &setup.player_name, "player_name"
10087 &setup.multiple_users, "multiple_users"
10091 &setup.sound, "sound"
10095 &setup.sound_loops, "repeating_sound_loops"
10099 &setup.sound_music, "background_music"
10103 &setup.sound_simple, "simple_sound_effects"
10107 &setup.toons, "toons"
10111 &setup.global_animations, "global_animations"
10115 &setup.scroll_delay, "scroll_delay"
10119 &setup.forced_scroll_delay, "forced_scroll_delay"
10123 &setup.scroll_delay_value, "scroll_delay_value"
10127 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10131 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10135 &setup.fade_screens, "fade_screens"
10139 &setup.autorecord, "automatic_tape_recording"
10143 &setup.autorecord_after_replay, "autorecord_after_replay"
10147 &setup.auto_pause_on_start, "auto_pause_on_start"
10151 &setup.show_titlescreen, "show_titlescreen"
10155 &setup.quick_doors, "quick_doors"
10159 &setup.team_mode, "team_mode"
10163 &setup.handicap, "handicap"
10167 &setup.skip_levels, "skip_levels"
10171 &setup.increment_levels, "increment_levels"
10175 &setup.auto_play_next_level, "auto_play_next_level"
10179 &setup.count_score_after_game, "count_score_after_game"
10183 &setup.show_scores_after_game, "show_scores_after_game"
10187 &setup.time_limit, "time_limit"
10191 &setup.fullscreen, "fullscreen"
10195 &setup.window_scaling_percent, "window_scaling_percent"
10199 &setup.window_scaling_quality, "window_scaling_quality"
10203 &setup.screen_rendering_mode, "screen_rendering_mode"
10207 &setup.vsync_mode, "vsync_mode"
10211 &setup.ask_on_escape, "ask_on_escape"
10215 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10219 &setup.ask_on_game_over, "ask_on_game_over"
10223 &setup.ask_on_quit_game, "ask_on_quit_game"
10227 &setup.ask_on_quit_program, "ask_on_quit_program"
10231 &setup.quick_switch, "quick_player_switch"
10235 &setup.input_on_focus, "input_on_focus"
10239 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10243 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10247 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10251 &setup.game_speed_extended, "game_speed_extended"
10255 &setup.game_frame_delay, "game_frame_delay"
10259 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10263 &setup.bd_skip_hatching, "bd_skip_hatching"
10267 &setup.bd_scroll_delay, "bd_scroll_delay"
10271 &setup.bd_smooth_movements, "bd_smooth_movements"
10275 &setup.sp_show_border_elements, "sp_show_border_elements"
10279 &setup.small_game_graphics, "small_game_graphics"
10283 &setup.show_load_save_buttons, "show_load_save_buttons"
10287 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10291 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10295 &setup.graphics_set, "graphics_set"
10299 &setup.sounds_set, "sounds_set"
10303 &setup.music_set, "music_set"
10307 &setup.override_level_graphics, "override_level_graphics"
10311 &setup.override_level_sounds, "override_level_sounds"
10315 &setup.override_level_music, "override_level_music"
10319 &setup.volume_simple, "volume_simple"
10323 &setup.volume_loops, "volume_loops"
10327 &setup.volume_music, "volume_music"
10331 &setup.network_mode, "network_mode"
10335 &setup.network_player_nr, "network_player"
10339 &setup.network_server_hostname, "network_server_hostname"
10343 &setup.touch.control_type, "touch.control_type"
10347 &setup.touch.move_distance, "touch.move_distance"
10351 &setup.touch.drop_distance, "touch.drop_distance"
10355 &setup.touch.transparency, "touch.transparency"
10359 &setup.touch.draw_outlined, "touch.draw_outlined"
10363 &setup.touch.draw_pressed, "touch.draw_pressed"
10367 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10371 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10375 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10379 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10383 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10387 static struct TokenInfo auto_setup_tokens[] =
10391 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10395 static struct TokenInfo server_setup_tokens[] =
10399 &setup.player_uuid, "player_uuid"
10403 &setup.player_version, "player_version"
10407 &setup.use_api_server, TEST_PREFIX "use_api_server"
10411 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10415 &setup.api_server_password, TEST_PREFIX "api_server_password"
10419 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10423 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10427 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10431 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10435 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10439 static struct TokenInfo editor_setup_tokens[] =
10443 &setup.editor.el_classic, "editor.el_classic"
10447 &setup.editor.el_custom, "editor.el_custom"
10451 &setup.editor.el_user_defined, "editor.el_user_defined"
10455 &setup.editor.el_dynamic, "editor.el_dynamic"
10459 &setup.editor.el_headlines, "editor.el_headlines"
10463 &setup.editor.show_element_token, "editor.show_element_token"
10467 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10471 static struct TokenInfo editor_cascade_setup_tokens[] =
10475 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10479 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10483 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10487 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10491 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10495 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10499 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10503 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10507 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10511 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10515 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10519 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10523 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10527 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10531 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10535 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10539 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10543 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10547 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10551 static struct TokenInfo shortcut_setup_tokens[] =
10555 &setup.shortcut.save_game, "shortcut.save_game"
10559 &setup.shortcut.load_game, "shortcut.load_game"
10563 &setup.shortcut.restart_game, "shortcut.restart_game"
10567 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10571 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10575 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10579 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10583 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10587 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10591 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10595 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10599 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10603 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10607 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10611 &setup.shortcut.tape_record, "shortcut.tape_record"
10615 &setup.shortcut.tape_play, "shortcut.tape_play"
10619 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10623 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10627 &setup.shortcut.sound_music, "shortcut.sound_music"
10631 &setup.shortcut.snap_left, "shortcut.snap_left"
10635 &setup.shortcut.snap_right, "shortcut.snap_right"
10639 &setup.shortcut.snap_up, "shortcut.snap_up"
10643 &setup.shortcut.snap_down, "shortcut.snap_down"
10647 static struct SetupInputInfo setup_input;
10648 static struct TokenInfo player_setup_tokens[] =
10652 &setup_input.use_joystick, ".use_joystick"
10656 &setup_input.joy.device_name, ".joy.device_name"
10660 &setup_input.joy.xleft, ".joy.xleft"
10664 &setup_input.joy.xmiddle, ".joy.xmiddle"
10668 &setup_input.joy.xright, ".joy.xright"
10672 &setup_input.joy.yupper, ".joy.yupper"
10676 &setup_input.joy.ymiddle, ".joy.ymiddle"
10680 &setup_input.joy.ylower, ".joy.ylower"
10684 &setup_input.joy.snap, ".joy.snap_field"
10688 &setup_input.joy.drop, ".joy.place_bomb"
10692 &setup_input.key.left, ".key.move_left"
10696 &setup_input.key.right, ".key.move_right"
10700 &setup_input.key.up, ".key.move_up"
10704 &setup_input.key.down, ".key.move_down"
10708 &setup_input.key.snap, ".key.snap_field"
10712 &setup_input.key.drop, ".key.place_bomb"
10716 static struct TokenInfo system_setup_tokens[] =
10720 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10724 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10728 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10732 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10736 static struct TokenInfo internal_setup_tokens[] =
10740 &setup.internal.program_title, "program_title"
10744 &setup.internal.program_version, "program_version"
10748 &setup.internal.program_author, "program_author"
10752 &setup.internal.program_email, "program_email"
10756 &setup.internal.program_website, "program_website"
10760 &setup.internal.program_copyright, "program_copyright"
10764 &setup.internal.program_company, "program_company"
10768 &setup.internal.program_icon_file, "program_icon_file"
10772 &setup.internal.default_graphics_set, "default_graphics_set"
10776 &setup.internal.default_sounds_set, "default_sounds_set"
10780 &setup.internal.default_music_set, "default_music_set"
10784 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10788 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10792 &setup.internal.fallback_music_file, "fallback_music_file"
10796 &setup.internal.default_level_series, "default_level_series"
10800 &setup.internal.default_window_width, "default_window_width"
10804 &setup.internal.default_window_height, "default_window_height"
10808 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10812 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10816 &setup.internal.create_user_levelset, "create_user_levelset"
10820 &setup.internal.info_screens_from_main, "info_screens_from_main"
10824 &setup.internal.menu_game, "menu_game"
10828 &setup.internal.menu_engines, "menu_engines"
10832 &setup.internal.menu_editor, "menu_editor"
10836 &setup.internal.menu_graphics, "menu_graphics"
10840 &setup.internal.menu_sound, "menu_sound"
10844 &setup.internal.menu_artwork, "menu_artwork"
10848 &setup.internal.menu_input, "menu_input"
10852 &setup.internal.menu_touch, "menu_touch"
10856 &setup.internal.menu_shortcuts, "menu_shortcuts"
10860 &setup.internal.menu_exit, "menu_exit"
10864 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10868 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10872 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10876 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10880 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10884 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10888 &setup.internal.info_title, "info_title"
10892 &setup.internal.info_elements, "info_elements"
10896 &setup.internal.info_music, "info_music"
10900 &setup.internal.info_credits, "info_credits"
10904 &setup.internal.info_program, "info_program"
10908 &setup.internal.info_version, "info_version"
10912 &setup.internal.info_levelset, "info_levelset"
10916 &setup.internal.info_exit, "info_exit"
10920 static struct TokenInfo debug_setup_tokens[] =
10924 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10928 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10932 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10936 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10940 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10944 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10948 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10952 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10956 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10960 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10964 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10968 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10972 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10976 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10980 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10984 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10988 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10992 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10996 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11000 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11004 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11007 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11011 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11015 &setup.debug.xsn_mode, "debug.xsn_mode"
11019 &setup.debug.xsn_percent, "debug.xsn_percent"
11023 static struct TokenInfo options_setup_tokens[] =
11027 &setup.options.verbose, "options.verbose"
11031 &setup.options.debug, "options.debug"
11035 &setup.options.debug_mode, "options.debug_mode"
11039 static void setSetupInfoToDefaults(struct SetupInfo *si)
11043 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11045 si->multiple_users = TRUE;
11048 si->sound_loops = TRUE;
11049 si->sound_music = TRUE;
11050 si->sound_simple = TRUE;
11052 si->global_animations = TRUE;
11053 si->scroll_delay = TRUE;
11054 si->forced_scroll_delay = FALSE;
11055 si->scroll_delay_value = STD_SCROLL_DELAY;
11056 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11057 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11058 si->fade_screens = TRUE;
11059 si->autorecord = TRUE;
11060 si->autorecord_after_replay = TRUE;
11061 si->auto_pause_on_start = FALSE;
11062 si->show_titlescreen = TRUE;
11063 si->quick_doors = FALSE;
11064 si->team_mode = FALSE;
11065 si->handicap = TRUE;
11066 si->skip_levels = TRUE;
11067 si->increment_levels = TRUE;
11068 si->auto_play_next_level = TRUE;
11069 si->count_score_after_game = TRUE;
11070 si->show_scores_after_game = TRUE;
11071 si->time_limit = TRUE;
11072 si->fullscreen = FALSE;
11073 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11074 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11075 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11076 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11077 si->ask_on_escape = TRUE;
11078 si->ask_on_escape_editor = TRUE;
11079 si->ask_on_game_over = TRUE;
11080 si->ask_on_quit_game = TRUE;
11081 si->ask_on_quit_program = TRUE;
11082 si->quick_switch = FALSE;
11083 si->input_on_focus = FALSE;
11084 si->prefer_aga_graphics = TRUE;
11085 si->prefer_lowpass_sounds = FALSE;
11086 si->prefer_extra_panel_items = TRUE;
11087 si->game_speed_extended = FALSE;
11088 si->game_frame_delay = GAME_FRAME_DELAY;
11089 si->bd_skip_uncovering = FALSE;
11090 si->bd_skip_hatching = FALSE;
11091 si->bd_scroll_delay = TRUE;
11092 si->bd_smooth_movements = AUTO;
11093 si->sp_show_border_elements = FALSE;
11094 si->small_game_graphics = FALSE;
11095 si->show_load_save_buttons = FALSE;
11096 si->show_undo_redo_buttons = FALSE;
11097 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11099 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11100 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11101 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11103 si->override_level_graphics = FALSE;
11104 si->override_level_sounds = FALSE;
11105 si->override_level_music = FALSE;
11107 si->volume_simple = 100; // percent
11108 si->volume_loops = 100; // percent
11109 si->volume_music = 100; // percent
11111 si->network_mode = FALSE;
11112 si->network_player_nr = 0; // first player
11113 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11115 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11116 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11117 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11118 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11119 si->touch.draw_outlined = TRUE;
11120 si->touch.draw_pressed = TRUE;
11122 for (i = 0; i < 2; i++)
11124 char *default_grid_button[6][2] =
11130 { "111222", " vv " },
11131 { "111222", " vv " }
11133 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11134 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11135 int min_xsize = MIN(6, grid_xsize);
11136 int min_ysize = MIN(6, grid_ysize);
11137 int startx = grid_xsize - min_xsize;
11138 int starty = grid_ysize - min_ysize;
11141 // virtual buttons grid can only be set to defaults if video is initialized
11142 // (this will be repeated if virtual buttons are not loaded from setup file)
11143 if (video.initialized)
11145 si->touch.grid_xsize[i] = grid_xsize;
11146 si->touch.grid_ysize[i] = grid_ysize;
11150 si->touch.grid_xsize[i] = -1;
11151 si->touch.grid_ysize[i] = -1;
11154 for (x = 0; x < MAX_GRID_XSIZE; x++)
11155 for (y = 0; y < MAX_GRID_YSIZE; y++)
11156 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11158 for (x = 0; x < min_xsize; x++)
11159 for (y = 0; y < min_ysize; y++)
11160 si->touch.grid_button[i][x][starty + y] =
11161 default_grid_button[y][0][x];
11163 for (x = 0; x < min_xsize; x++)
11164 for (y = 0; y < min_ysize; y++)
11165 si->touch.grid_button[i][startx + x][starty + y] =
11166 default_grid_button[y][1][x];
11169 si->touch.grid_initialized = video.initialized;
11171 si->touch.overlay_buttons = FALSE;
11173 si->editor.el_boulderdash = TRUE;
11174 si->editor.el_boulderdash_native = TRUE;
11175 si->editor.el_emerald_mine = TRUE;
11176 si->editor.el_emerald_mine_club = TRUE;
11177 si->editor.el_more = TRUE;
11178 si->editor.el_sokoban = TRUE;
11179 si->editor.el_supaplex = TRUE;
11180 si->editor.el_diamond_caves = TRUE;
11181 si->editor.el_dx_boulderdash = TRUE;
11183 si->editor.el_mirror_magic = TRUE;
11184 si->editor.el_deflektor = TRUE;
11186 si->editor.el_chars = TRUE;
11187 si->editor.el_steel_chars = TRUE;
11189 si->editor.el_classic = TRUE;
11190 si->editor.el_custom = TRUE;
11192 si->editor.el_user_defined = FALSE;
11193 si->editor.el_dynamic = TRUE;
11195 si->editor.el_headlines = TRUE;
11197 si->editor.show_element_token = FALSE;
11199 si->editor.show_read_only_warning = TRUE;
11201 si->editor.use_template_for_new_levels = TRUE;
11203 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11204 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11205 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11206 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11207 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11209 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11210 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11211 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11212 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11213 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11215 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11216 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11217 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11218 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11219 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11220 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11222 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11223 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11224 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11226 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11227 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11228 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11229 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11231 for (i = 0; i < MAX_PLAYERS; i++)
11233 si->input[i].use_joystick = FALSE;
11234 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11235 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11236 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11237 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11238 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11239 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11240 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11241 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11242 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11243 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11244 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11245 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11246 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11247 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11248 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11251 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11252 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11253 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11254 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11256 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11257 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11258 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11259 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11260 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11261 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11262 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11264 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11266 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11267 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11268 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11270 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11271 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11272 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11274 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11275 si->internal.choose_from_top_leveldir = FALSE;
11276 si->internal.show_scaling_in_title = TRUE;
11277 si->internal.create_user_levelset = TRUE;
11278 si->internal.info_screens_from_main = FALSE;
11280 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11281 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11283 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11284 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11285 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11286 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11287 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11288 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11289 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11290 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11291 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11292 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11294 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11295 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11296 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11297 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11298 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11299 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11300 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11301 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11302 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11303 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11305 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11306 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11308 si->debug.show_frames_per_second = FALSE;
11310 si->debug.xsn_mode = AUTO;
11311 si->debug.xsn_percent = 0;
11313 si->options.verbose = FALSE;
11314 si->options.debug = FALSE;
11315 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11317 #if defined(PLATFORM_ANDROID)
11318 si->fullscreen = TRUE;
11319 si->touch.overlay_buttons = TRUE;
11322 setHideSetupEntry(&setup.debug.xsn_mode);
11325 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11327 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11330 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11332 si->player_uuid = NULL; // (will be set later)
11333 si->player_version = 1; // (will be set later)
11335 si->use_api_server = TRUE;
11336 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11337 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11338 si->ask_for_uploading_tapes = TRUE;
11339 si->ask_for_remaining_tapes = FALSE;
11340 si->provide_uploading_tapes = TRUE;
11341 si->ask_for_using_api_server = TRUE;
11342 si->has_remaining_tapes = FALSE;
11345 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11347 si->editor_cascade.el_bd = TRUE;
11348 si->editor_cascade.el_bd_native = TRUE;
11349 si->editor_cascade.el_em = TRUE;
11350 si->editor_cascade.el_emc = TRUE;
11351 si->editor_cascade.el_rnd = TRUE;
11352 si->editor_cascade.el_sb = TRUE;
11353 si->editor_cascade.el_sp = TRUE;
11354 si->editor_cascade.el_dc = TRUE;
11355 si->editor_cascade.el_dx = TRUE;
11357 si->editor_cascade.el_mm = TRUE;
11358 si->editor_cascade.el_df = TRUE;
11360 si->editor_cascade.el_chars = FALSE;
11361 si->editor_cascade.el_steel_chars = FALSE;
11362 si->editor_cascade.el_ce = FALSE;
11363 si->editor_cascade.el_ge = FALSE;
11364 si->editor_cascade.el_es = FALSE;
11365 si->editor_cascade.el_ref = FALSE;
11366 si->editor_cascade.el_user = FALSE;
11367 si->editor_cascade.el_dynamic = FALSE;
11370 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11372 static char *getHideSetupToken(void *setup_value)
11374 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11376 if (setup_value != NULL)
11377 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11379 return hide_setup_token;
11382 void setHideSetupEntry(void *setup_value)
11384 char *hide_setup_token = getHideSetupToken(setup_value);
11386 if (hide_setup_hash == NULL)
11387 hide_setup_hash = newSetupFileHash();
11389 if (setup_value != NULL)
11390 setHashEntry(hide_setup_hash, hide_setup_token, "");
11393 void removeHideSetupEntry(void *setup_value)
11395 char *hide_setup_token = getHideSetupToken(setup_value);
11397 if (setup_value != NULL)
11398 removeHashEntry(hide_setup_hash, hide_setup_token);
11401 boolean hideSetupEntry(void *setup_value)
11403 char *hide_setup_token = getHideSetupToken(setup_value);
11405 return (setup_value != NULL &&
11406 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11409 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11410 struct TokenInfo *token_info,
11411 int token_nr, char *token_text)
11413 char *token_hide_text = getStringCat2(token_text, ".hide");
11414 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11416 // set the value of this setup option in the setup option structure
11417 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11419 // check if this setup option should be hidden in the setup menu
11420 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11421 setHideSetupEntry(token_info[token_nr].value);
11423 free(token_hide_text);
11426 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11427 struct TokenInfo *token_info,
11430 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11431 token_info[token_nr].text);
11434 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11438 if (!setup_file_hash)
11441 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11442 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11444 setup.touch.grid_initialized = TRUE;
11445 for (i = 0; i < 2; i++)
11447 int grid_xsize = setup.touch.grid_xsize[i];
11448 int grid_ysize = setup.touch.grid_ysize[i];
11451 // if virtual buttons are not loaded from setup file, repeat initializing
11452 // virtual buttons grid with default values later when video is initialized
11453 if (grid_xsize == -1 ||
11456 setup.touch.grid_initialized = FALSE;
11461 for (y = 0; y < grid_ysize; y++)
11463 char token_string[MAX_LINE_LEN];
11465 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11467 char *value_string = getHashEntry(setup_file_hash, token_string);
11469 if (value_string == NULL)
11472 for (x = 0; x < grid_xsize; x++)
11474 char c = value_string[x];
11476 setup.touch.grid_button[i][x][y] =
11477 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11482 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11483 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11485 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11486 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11488 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11492 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11494 setup_input = setup.input[pnr];
11495 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11497 char full_token[100];
11499 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11500 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11503 setup.input[pnr] = setup_input;
11506 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11507 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11509 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11510 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11512 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11513 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11515 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11516 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11518 setHideRelatedSetupEntries();
11521 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11525 if (!setup_file_hash)
11528 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11529 setSetupInfo(auto_setup_tokens, i,
11530 getHashEntry(setup_file_hash,
11531 auto_setup_tokens[i].text));
11534 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11538 if (!setup_file_hash)
11541 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11542 setSetupInfo(server_setup_tokens, i,
11543 getHashEntry(setup_file_hash,
11544 server_setup_tokens[i].text));
11547 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11551 if (!setup_file_hash)
11554 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11555 setSetupInfo(editor_cascade_setup_tokens, i,
11556 getHashEntry(setup_file_hash,
11557 editor_cascade_setup_tokens[i].text));
11560 void LoadUserNames(void)
11562 int last_user_nr = user.nr;
11565 if (global.user_names != NULL)
11567 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11568 checked_free(global.user_names[i]);
11570 checked_free(global.user_names);
11573 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11575 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11579 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11581 if (setup_file_hash)
11583 char *player_name = getHashEntry(setup_file_hash, "player_name");
11585 global.user_names[i] = getFixedUserName(player_name);
11587 freeSetupFileHash(setup_file_hash);
11590 if (global.user_names[i] == NULL)
11591 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11594 user.nr = last_user_nr;
11597 void LoadSetupFromFilename(char *filename)
11599 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11601 if (setup_file_hash)
11603 decodeSetupFileHash_Default(setup_file_hash);
11605 freeSetupFileHash(setup_file_hash);
11609 Debug("setup", "using default setup values");
11613 static void LoadSetup_SpecialPostProcessing(void)
11615 char *player_name_new;
11617 // needed to work around problems with fixed length strings
11618 player_name_new = getFixedUserName(setup.player_name);
11619 free(setup.player_name);
11620 setup.player_name = player_name_new;
11622 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11623 if (setup.scroll_delay == FALSE)
11625 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11626 setup.scroll_delay = TRUE; // now always "on"
11629 // make sure that scroll delay value stays inside valid range
11630 setup.scroll_delay_value =
11631 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11634 void LoadSetup_Default(void)
11638 // always start with reliable default values
11639 setSetupInfoToDefaults(&setup);
11641 // try to load setup values from default setup file
11642 filename = getDefaultSetupFilename();
11644 if (fileExists(filename))
11645 LoadSetupFromFilename(filename);
11647 // try to load setup values from platform setup file
11648 filename = getPlatformSetupFilename();
11650 if (fileExists(filename))
11651 LoadSetupFromFilename(filename);
11653 // try to load setup values from user setup file
11654 filename = getSetupFilename();
11656 LoadSetupFromFilename(filename);
11658 LoadSetup_SpecialPostProcessing();
11661 void LoadSetup_AutoSetup(void)
11663 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11664 SetupFileHash *setup_file_hash = NULL;
11666 // always start with reliable default values
11667 setSetupInfoToDefaults_AutoSetup(&setup);
11669 setup_file_hash = loadSetupFileHash(filename);
11671 if (setup_file_hash)
11673 decodeSetupFileHash_AutoSetup(setup_file_hash);
11675 freeSetupFileHash(setup_file_hash);
11681 void LoadSetup_ServerSetup(void)
11683 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11684 SetupFileHash *setup_file_hash = NULL;
11686 // always start with reliable default values
11687 setSetupInfoToDefaults_ServerSetup(&setup);
11689 setup_file_hash = loadSetupFileHash(filename);
11691 if (setup_file_hash)
11693 decodeSetupFileHash_ServerSetup(setup_file_hash);
11695 freeSetupFileHash(setup_file_hash);
11700 if (setup.player_uuid == NULL)
11702 // player UUID does not yet exist in setup file
11703 setup.player_uuid = getStringCopy(getUUID());
11704 setup.player_version = 2;
11706 SaveSetup_ServerSetup();
11710 void LoadSetup_EditorCascade(void)
11712 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11713 SetupFileHash *setup_file_hash = NULL;
11715 // always start with reliable default values
11716 setSetupInfoToDefaults_EditorCascade(&setup);
11718 setup_file_hash = loadSetupFileHash(filename);
11720 if (setup_file_hash)
11722 decodeSetupFileHash_EditorCascade(setup_file_hash);
11724 freeSetupFileHash(setup_file_hash);
11730 void LoadSetup(void)
11732 LoadSetup_Default();
11733 LoadSetup_AutoSetup();
11734 LoadSetup_ServerSetup();
11735 LoadSetup_EditorCascade();
11738 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11739 char *mapping_line)
11741 char mapping_guid[MAX_LINE_LEN];
11742 char *mapping_start, *mapping_end;
11744 // get GUID from game controller mapping line: copy complete line
11745 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11746 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11748 // get GUID from game controller mapping line: cut after GUID part
11749 mapping_start = strchr(mapping_guid, ',');
11750 if (mapping_start != NULL)
11751 *mapping_start = '\0';
11753 // cut newline from game controller mapping line
11754 mapping_end = strchr(mapping_line, '\n');
11755 if (mapping_end != NULL)
11756 *mapping_end = '\0';
11758 // add mapping entry to game controller mappings hash
11759 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11762 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11767 if (!(file = fopen(filename, MODE_READ)))
11769 Warn("cannot read game controller mappings file '%s'", filename);
11774 while (!feof(file))
11776 char line[MAX_LINE_LEN];
11778 if (!fgets(line, MAX_LINE_LEN, file))
11781 addGameControllerMappingToHash(mappings_hash, line);
11787 void SaveSetup_Default(void)
11789 char *filename = getSetupFilename();
11793 InitUserDataDirectory();
11795 if (!(file = fopen(filename, MODE_WRITE)))
11797 Warn("cannot write setup file '%s'", filename);
11802 fprintFileHeader(file, SETUP_FILENAME);
11804 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11806 // just to make things nicer :)
11807 if (global_setup_tokens[i].value == &setup.multiple_users ||
11808 global_setup_tokens[i].value == &setup.sound ||
11809 global_setup_tokens[i].value == &setup.graphics_set ||
11810 global_setup_tokens[i].value == &setup.volume_simple ||
11811 global_setup_tokens[i].value == &setup.network_mode ||
11812 global_setup_tokens[i].value == &setup.touch.control_type ||
11813 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11814 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11815 fprintf(file, "\n");
11817 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11820 for (i = 0; i < 2; i++)
11822 int grid_xsize = setup.touch.grid_xsize[i];
11823 int grid_ysize = setup.touch.grid_ysize[i];
11826 fprintf(file, "\n");
11828 for (y = 0; y < grid_ysize; y++)
11830 char token_string[MAX_LINE_LEN];
11831 char value_string[MAX_LINE_LEN];
11833 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11835 for (x = 0; x < grid_xsize; x++)
11837 char c = setup.touch.grid_button[i][x][y];
11839 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11842 value_string[grid_xsize] = '\0';
11844 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11848 fprintf(file, "\n");
11849 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11850 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11852 fprintf(file, "\n");
11853 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11854 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11856 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11860 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11861 fprintf(file, "\n");
11863 setup_input = setup.input[pnr];
11864 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11865 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11868 fprintf(file, "\n");
11869 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11870 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11872 // (internal setup values not saved to user setup file)
11874 fprintf(file, "\n");
11875 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11876 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11877 setup.debug.xsn_mode != AUTO)
11878 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11880 fprintf(file, "\n");
11881 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11882 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11886 SetFilePermissions(filename, PERMS_PRIVATE);
11889 void SaveSetup_AutoSetup(void)
11891 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11895 InitUserDataDirectory();
11897 if (!(file = fopen(filename, MODE_WRITE)))
11899 Warn("cannot write auto setup file '%s'", filename);
11906 fprintFileHeader(file, AUTOSETUP_FILENAME);
11908 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11909 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11913 SetFilePermissions(filename, PERMS_PRIVATE);
11918 void SaveSetup_ServerSetup(void)
11920 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11924 InitUserDataDirectory();
11926 if (!(file = fopen(filename, MODE_WRITE)))
11928 Warn("cannot write server setup file '%s'", filename);
11935 fprintFileHeader(file, SERVERSETUP_FILENAME);
11937 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11939 // just to make things nicer :)
11940 if (server_setup_tokens[i].value == &setup.use_api_server)
11941 fprintf(file, "\n");
11943 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11948 SetFilePermissions(filename, PERMS_PRIVATE);
11953 void SaveSetup_EditorCascade(void)
11955 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11959 InitUserDataDirectory();
11961 if (!(file = fopen(filename, MODE_WRITE)))
11963 Warn("cannot write editor cascade state file '%s'", filename);
11970 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11972 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11973 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11977 SetFilePermissions(filename, PERMS_PRIVATE);
11982 void SaveSetup(void)
11984 SaveSetup_Default();
11985 SaveSetup_AutoSetup();
11986 SaveSetup_ServerSetup();
11987 SaveSetup_EditorCascade();
11990 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11995 if (!(file = fopen(filename, MODE_WRITE)))
11997 Warn("cannot write game controller mappings file '%s'", filename);
12002 BEGIN_HASH_ITERATION(mappings_hash, itr)
12004 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12006 END_HASH_ITERATION(mappings_hash, itr)
12011 void SaveSetup_AddGameControllerMapping(char *mapping)
12013 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12014 SetupFileHash *mappings_hash = newSetupFileHash();
12016 InitUserDataDirectory();
12018 // load existing personal game controller mappings
12019 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12021 // add new mapping to personal game controller mappings
12022 addGameControllerMappingToHash(mappings_hash, mapping);
12024 // save updated personal game controller mappings
12025 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12027 freeSetupFileHash(mappings_hash);
12031 void LoadCustomElementDescriptions(void)
12033 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12034 SetupFileHash *setup_file_hash;
12037 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12039 if (element_info[i].custom_description != NULL)
12041 free(element_info[i].custom_description);
12042 element_info[i].custom_description = NULL;
12046 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12049 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12051 char *token = getStringCat2(element_info[i].token_name, ".name");
12052 char *value = getHashEntry(setup_file_hash, token);
12055 element_info[i].custom_description = getStringCopy(value);
12060 freeSetupFileHash(setup_file_hash);
12063 static int getElementFromToken(char *token)
12065 char *value = getHashEntry(element_token_hash, token);
12068 return atoi(value);
12070 Warn("unknown element token '%s'", token);
12072 return EL_UNDEFINED;
12075 void FreeGlobalAnimEventInfo(void)
12077 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12079 if (gaei->event_list == NULL)
12084 for (i = 0; i < gaei->num_event_lists; i++)
12086 checked_free(gaei->event_list[i]->event_value);
12087 checked_free(gaei->event_list[i]);
12090 checked_free(gaei->event_list);
12092 gaei->event_list = NULL;
12093 gaei->num_event_lists = 0;
12096 static int AddGlobalAnimEventList(void)
12098 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12099 int list_pos = gaei->num_event_lists++;
12101 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12102 sizeof(struct GlobalAnimEventListInfo *));
12104 gaei->event_list[list_pos] =
12105 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12107 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12109 gaeli->event_value = NULL;
12110 gaeli->num_event_values = 0;
12115 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12117 // do not add empty global animation events
12118 if (event_value == ANIM_EVENT_NONE)
12121 // if list position is undefined, create new list
12122 if (list_pos == ANIM_EVENT_UNDEFINED)
12123 list_pos = AddGlobalAnimEventList();
12125 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12126 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12127 int value_pos = gaeli->num_event_values++;
12129 gaeli->event_value = checked_realloc(gaeli->event_value,
12130 gaeli->num_event_values * sizeof(int *));
12132 gaeli->event_value[value_pos] = event_value;
12137 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12139 if (list_pos == ANIM_EVENT_UNDEFINED)
12140 return ANIM_EVENT_NONE;
12142 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12143 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12145 return gaeli->event_value[value_pos];
12148 int GetGlobalAnimEventValueCount(int list_pos)
12150 if (list_pos == ANIM_EVENT_UNDEFINED)
12153 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12154 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12156 return gaeli->num_event_values;
12159 // This function checks if a string <s> of the format "string1, string2, ..."
12160 // exactly contains a string <s_contained>.
12162 static boolean string_has_parameter(char *s, char *s_contained)
12166 if (s == NULL || s_contained == NULL)
12169 if (strlen(s_contained) > strlen(s))
12172 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12174 char next_char = s[strlen(s_contained)];
12176 // check if next character is delimiter or whitespace
12177 if (next_char == ',' || next_char == '\0' ||
12178 next_char == ' ' || next_char == '\t')
12182 // check if string contains another parameter string after a comma
12183 substring = strchr(s, ',');
12184 if (substring == NULL) // string does not contain a comma
12187 // advance string pointer to next character after the comma
12190 // skip potential whitespaces after the comma
12191 while (*substring == ' ' || *substring == '\t')
12194 return string_has_parameter(substring, s_contained);
12197 static int get_anim_parameter_value_ce(char *s)
12200 char *pattern_1 = "ce_change:custom_";
12201 char *pattern_2 = ".page_";
12202 int pattern_1_len = strlen(pattern_1);
12203 char *matching_char = strstr(s_ptr, pattern_1);
12204 int result = ANIM_EVENT_NONE;
12206 if (matching_char == NULL)
12207 return ANIM_EVENT_NONE;
12209 result = ANIM_EVENT_CE_CHANGE;
12211 s_ptr = matching_char + pattern_1_len;
12213 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12214 if (*s_ptr >= '0' && *s_ptr <= '9')
12216 int gic_ce_nr = (*s_ptr++ - '0');
12218 if (*s_ptr >= '0' && *s_ptr <= '9')
12220 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12222 if (*s_ptr >= '0' && *s_ptr <= '9')
12223 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12226 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12227 return ANIM_EVENT_NONE;
12229 // custom element stored as 0 to 255
12232 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12236 // invalid custom element number specified
12238 return ANIM_EVENT_NONE;
12241 // check for change page number ("page_X" or "page_XX") (optional)
12242 if (strPrefix(s_ptr, pattern_2))
12244 s_ptr += strlen(pattern_2);
12246 if (*s_ptr >= '0' && *s_ptr <= '9')
12248 int gic_page_nr = (*s_ptr++ - '0');
12250 if (*s_ptr >= '0' && *s_ptr <= '9')
12251 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12253 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12254 return ANIM_EVENT_NONE;
12256 // change page stored as 1 to 32 (0 means "all change pages")
12258 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12262 // invalid animation part number specified
12264 return ANIM_EVENT_NONE;
12268 // discard result if next character is neither delimiter nor whitespace
12269 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12270 *s_ptr == ' ' || *s_ptr == '\t'))
12271 return ANIM_EVENT_NONE;
12276 static int get_anim_parameter_value(char *s)
12278 int event_value[] =
12286 char *pattern_1[] =
12294 char *pattern_2 = ".part_";
12295 char *matching_char = NULL;
12297 int pattern_1_len = 0;
12298 int result = ANIM_EVENT_NONE;
12301 result = get_anim_parameter_value_ce(s);
12303 if (result != ANIM_EVENT_NONE)
12306 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12308 matching_char = strstr(s_ptr, pattern_1[i]);
12309 pattern_1_len = strlen(pattern_1[i]);
12310 result = event_value[i];
12312 if (matching_char != NULL)
12316 if (matching_char == NULL)
12317 return ANIM_EVENT_NONE;
12319 s_ptr = matching_char + pattern_1_len;
12321 // check for main animation number ("anim_X" or "anim_XX")
12322 if (*s_ptr >= '0' && *s_ptr <= '9')
12324 int gic_anim_nr = (*s_ptr++ - '0');
12326 if (*s_ptr >= '0' && *s_ptr <= '9')
12327 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12329 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12330 return ANIM_EVENT_NONE;
12332 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12336 // invalid main animation number specified
12338 return ANIM_EVENT_NONE;
12341 // check for animation part number ("part_X" or "part_XX") (optional)
12342 if (strPrefix(s_ptr, pattern_2))
12344 s_ptr += strlen(pattern_2);
12346 if (*s_ptr >= '0' && *s_ptr <= '9')
12348 int gic_part_nr = (*s_ptr++ - '0');
12350 if (*s_ptr >= '0' && *s_ptr <= '9')
12351 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12353 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12354 return ANIM_EVENT_NONE;
12356 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12360 // invalid animation part number specified
12362 return ANIM_EVENT_NONE;
12366 // discard result if next character is neither delimiter nor whitespace
12367 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12368 *s_ptr == ' ' || *s_ptr == '\t'))
12369 return ANIM_EVENT_NONE;
12374 static int get_anim_parameter_values(char *s)
12376 int list_pos = ANIM_EVENT_UNDEFINED;
12377 int event_value = ANIM_EVENT_DEFAULT;
12379 if (string_has_parameter(s, "any"))
12380 event_value |= ANIM_EVENT_ANY;
12382 if (string_has_parameter(s, "click:self") ||
12383 string_has_parameter(s, "click") ||
12384 string_has_parameter(s, "self"))
12385 event_value |= ANIM_EVENT_SELF;
12387 if (string_has_parameter(s, "unclick:any"))
12388 event_value |= ANIM_EVENT_UNCLICK_ANY;
12390 // if animation event found, add it to global animation event list
12391 if (event_value != ANIM_EVENT_NONE)
12392 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12396 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12397 event_value = get_anim_parameter_value(s);
12399 // if animation event found, add it to global animation event list
12400 if (event_value != ANIM_EVENT_NONE)
12401 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12403 // continue with next part of the string, starting with next comma
12404 s = strchr(s + 1, ',');
12410 static int get_anim_action_parameter_value(char *token)
12412 // check most common default case first to massively speed things up
12413 if (strEqual(token, ARG_UNDEFINED))
12414 return ANIM_EVENT_ACTION_NONE;
12416 int result = getImageIDFromToken(token);
12420 char *gfx_token = getStringCat2("gfx.", token);
12422 result = getImageIDFromToken(gfx_token);
12424 checked_free(gfx_token);
12429 Key key = getKeyFromX11KeyName(token);
12431 if (key != KSYM_UNDEFINED)
12432 result = -(int)key;
12439 result = get_hash_from_string(token); // unsigned int => int
12440 result = ABS(result); // may be negative now
12441 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12443 setHashEntry(anim_url_hash, int2str(result, 0), token);
12448 result = ANIM_EVENT_ACTION_NONE;
12453 int get_parameter_value(char *value_raw, char *suffix, int type)
12455 char *value = getStringToLower(value_raw);
12456 int result = 0; // probably a save default value
12458 if (strEqual(suffix, ".direction"))
12460 result = (strEqual(value, "left") ? MV_LEFT :
12461 strEqual(value, "right") ? MV_RIGHT :
12462 strEqual(value, "up") ? MV_UP :
12463 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12465 else if (strEqual(suffix, ".position"))
12467 result = (strEqual(value, "left") ? POS_LEFT :
12468 strEqual(value, "right") ? POS_RIGHT :
12469 strEqual(value, "top") ? POS_TOP :
12470 strEqual(value, "upper") ? POS_UPPER :
12471 strEqual(value, "middle") ? POS_MIDDLE :
12472 strEqual(value, "lower") ? POS_LOWER :
12473 strEqual(value, "bottom") ? POS_BOTTOM :
12474 strEqual(value, "any") ? POS_ANY :
12475 strEqual(value, "ce") ? POS_CE :
12476 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12477 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12479 else if (strEqual(suffix, ".align"))
12481 result = (strEqual(value, "left") ? ALIGN_LEFT :
12482 strEqual(value, "right") ? ALIGN_RIGHT :
12483 strEqual(value, "center") ? ALIGN_CENTER :
12484 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12486 else if (strEqual(suffix, ".valign"))
12488 result = (strEqual(value, "top") ? VALIGN_TOP :
12489 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12490 strEqual(value, "middle") ? VALIGN_MIDDLE :
12491 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12493 else if (strEqual(suffix, ".anim_mode"))
12495 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12496 string_has_parameter(value, "loop") ? ANIM_LOOP :
12497 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12498 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12499 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12500 string_has_parameter(value, "random") ? ANIM_RANDOM :
12501 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12502 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12503 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12504 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12505 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12506 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12507 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12508 string_has_parameter(value, "all") ? ANIM_ALL :
12509 string_has_parameter(value, "tiled") ? ANIM_TILED :
12510 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12513 if (string_has_parameter(value, "once"))
12514 result |= ANIM_ONCE;
12516 if (string_has_parameter(value, "reverse"))
12517 result |= ANIM_REVERSE;
12519 if (string_has_parameter(value, "opaque_player"))
12520 result |= ANIM_OPAQUE_PLAYER;
12522 if (string_has_parameter(value, "static_panel"))
12523 result |= ANIM_STATIC_PANEL;
12525 else if (strEqual(suffix, ".init_event") ||
12526 strEqual(suffix, ".anim_event"))
12528 result = get_anim_parameter_values(value);
12530 else if (strEqual(suffix, ".init_delay_action") ||
12531 strEqual(suffix, ".anim_delay_action") ||
12532 strEqual(suffix, ".post_delay_action") ||
12533 strEqual(suffix, ".init_event_action") ||
12534 strEqual(suffix, ".anim_event_action"))
12536 result = get_anim_action_parameter_value(value_raw);
12538 else if (strEqual(suffix, ".class"))
12540 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12541 get_hash_from_string(value));
12543 else if (strEqual(suffix, ".style"))
12545 result = STYLE_DEFAULT;
12547 if (string_has_parameter(value, "accurate_borders"))
12548 result |= STYLE_ACCURATE_BORDERS;
12550 if (string_has_parameter(value, "inner_corners"))
12551 result |= STYLE_INNER_CORNERS;
12553 if (string_has_parameter(value, "reverse"))
12554 result |= STYLE_REVERSE;
12556 if (string_has_parameter(value, "leftmost_position"))
12557 result |= STYLE_LEFTMOST_POSITION;
12559 if (string_has_parameter(value, "block_clicks"))
12560 result |= STYLE_BLOCK;
12562 if (string_has_parameter(value, "passthrough_clicks"))
12563 result |= STYLE_PASSTHROUGH;
12565 if (string_has_parameter(value, "multiple_actions"))
12566 result |= STYLE_MULTIPLE_ACTIONS;
12568 if (string_has_parameter(value, "consume_ce_event"))
12569 result |= STYLE_CONSUME_CE_EVENT;
12571 else if (strEqual(suffix, ".fade_mode"))
12573 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12574 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12575 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12576 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12577 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12578 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12579 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12580 FADE_MODE_DEFAULT);
12582 else if (strEqual(suffix, ".auto_delay_unit"))
12584 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12585 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12586 AUTO_DELAY_UNIT_DEFAULT);
12588 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12590 result = gfx.get_font_from_token_function(value);
12592 else // generic parameter of type integer or boolean
12594 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12595 type == TYPE_INTEGER ? get_integer_from_string(value) :
12596 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12597 ARG_UNDEFINED_VALUE);
12605 static int get_token_parameter_value(char *token, char *value_raw)
12609 if (token == NULL || value_raw == NULL)
12610 return ARG_UNDEFINED_VALUE;
12612 suffix = strrchr(token, '.');
12613 if (suffix == NULL)
12616 if (strEqual(suffix, ".element"))
12617 return getElementFromToken(value_raw);
12619 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12620 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12623 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12624 boolean ignore_defaults)
12628 for (i = 0; image_config_vars[i].token != NULL; i++)
12630 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12632 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12633 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12637 *image_config_vars[i].value =
12638 get_token_parameter_value(image_config_vars[i].token, value);
12642 void InitMenuDesignSettings_Static(void)
12644 // always start with reliable default values from static default config
12645 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12648 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12652 // the following initializes hierarchical values from static configuration
12654 // special case: initialize "ARG_DEFAULT" values in static default config
12655 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12656 titlescreen_initial_first_default.fade_mode =
12657 title_initial_first_default.fade_mode;
12658 titlescreen_initial_first_default.fade_delay =
12659 title_initial_first_default.fade_delay;
12660 titlescreen_initial_first_default.post_delay =
12661 title_initial_first_default.post_delay;
12662 titlescreen_initial_first_default.auto_delay =
12663 title_initial_first_default.auto_delay;
12664 titlescreen_initial_first_default.auto_delay_unit =
12665 title_initial_first_default.auto_delay_unit;
12666 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12667 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12668 titlescreen_first_default.post_delay = title_first_default.post_delay;
12669 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12670 titlescreen_first_default.auto_delay_unit =
12671 title_first_default.auto_delay_unit;
12672 titlemessage_initial_first_default.fade_mode =
12673 title_initial_first_default.fade_mode;
12674 titlemessage_initial_first_default.fade_delay =
12675 title_initial_first_default.fade_delay;
12676 titlemessage_initial_first_default.post_delay =
12677 title_initial_first_default.post_delay;
12678 titlemessage_initial_first_default.auto_delay =
12679 title_initial_first_default.auto_delay;
12680 titlemessage_initial_first_default.auto_delay_unit =
12681 title_initial_first_default.auto_delay_unit;
12682 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12683 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12684 titlemessage_first_default.post_delay = title_first_default.post_delay;
12685 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12686 titlemessage_first_default.auto_delay_unit =
12687 title_first_default.auto_delay_unit;
12689 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12690 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12691 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12692 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12693 titlescreen_initial_default.auto_delay_unit =
12694 title_initial_default.auto_delay_unit;
12695 titlescreen_default.fade_mode = title_default.fade_mode;
12696 titlescreen_default.fade_delay = title_default.fade_delay;
12697 titlescreen_default.post_delay = title_default.post_delay;
12698 titlescreen_default.auto_delay = title_default.auto_delay;
12699 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12700 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12701 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12702 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12703 titlemessage_initial_default.auto_delay_unit =
12704 title_initial_default.auto_delay_unit;
12705 titlemessage_default.fade_mode = title_default.fade_mode;
12706 titlemessage_default.fade_delay = title_default.fade_delay;
12707 titlemessage_default.post_delay = title_default.post_delay;
12708 titlemessage_default.auto_delay = title_default.auto_delay;
12709 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12711 // special case: initialize "ARG_DEFAULT" values in static default config
12712 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12713 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12715 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12716 titlescreen_first[i] = titlescreen_first_default;
12717 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12718 titlemessage_first[i] = titlemessage_first_default;
12720 titlescreen_initial[i] = titlescreen_initial_default;
12721 titlescreen[i] = titlescreen_default;
12722 titlemessage_initial[i] = titlemessage_initial_default;
12723 titlemessage[i] = titlemessage_default;
12726 // special case: initialize "ARG_DEFAULT" values in static default config
12727 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12728 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12730 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12733 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12734 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12735 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12738 // special case: initialize "ARG_DEFAULT" values in static default config
12739 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12740 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12742 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12743 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12744 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12746 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12749 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12753 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12757 struct XY *dst, *src;
12759 game_buttons_xy[] =
12761 { &game.button.save, &game.button.stop },
12762 { &game.button.pause2, &game.button.pause },
12763 { &game.button.load, &game.button.play },
12764 { &game.button.undo, &game.button.stop },
12765 { &game.button.redo, &game.button.play },
12771 // special case: initialize later added SETUP list size from LEVELS value
12772 if (menu.list_size[GAME_MODE_SETUP] == -1)
12773 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12775 // set default position for snapshot buttons to stop/pause/play buttons
12776 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12777 if ((*game_buttons_xy[i].dst).x == -1 &&
12778 (*game_buttons_xy[i].dst).y == -1)
12779 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12781 // --------------------------------------------------------------------------
12782 // dynamic viewports (including playfield margins, borders and alignments)
12783 // --------------------------------------------------------------------------
12785 // dynamic viewports currently only supported for landscape mode
12786 int display_width = MAX(video.display_width, video.display_height);
12787 int display_height = MIN(video.display_width, video.display_height);
12789 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12791 struct RectWithBorder *vp_window = &viewport.window[i];
12792 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12793 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12794 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12795 boolean dynamic_window_width = (vp_window->min_width != -1);
12796 boolean dynamic_window_height = (vp_window->min_height != -1);
12797 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12798 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12800 // adjust window size if min/max width/height is specified
12802 if (vp_window->min_width != -1)
12804 int window_width = display_width;
12806 // when using static window height, use aspect ratio of display
12807 if (vp_window->min_height == -1)
12808 window_width = vp_window->height * display_width / display_height;
12810 vp_window->width = MAX(vp_window->min_width, window_width);
12813 if (vp_window->min_height != -1)
12815 int window_height = display_height;
12817 // when using static window width, use aspect ratio of display
12818 if (vp_window->min_width == -1)
12819 window_height = vp_window->width * display_height / display_width;
12821 vp_window->height = MAX(vp_window->min_height, window_height);
12824 if (vp_window->max_width != -1)
12825 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12827 if (vp_window->max_height != -1)
12828 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12830 int playfield_width = vp_window->width;
12831 int playfield_height = vp_window->height;
12833 // adjust playfield size and position according to specified margins
12835 playfield_width -= vp_playfield->margin_left;
12836 playfield_width -= vp_playfield->margin_right;
12838 playfield_height -= vp_playfield->margin_top;
12839 playfield_height -= vp_playfield->margin_bottom;
12841 // adjust playfield size if min/max width/height is specified
12843 if (vp_playfield->min_width != -1)
12844 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12846 if (vp_playfield->min_height != -1)
12847 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12849 if (vp_playfield->max_width != -1)
12850 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12852 if (vp_playfield->max_height != -1)
12853 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12855 // adjust playfield position according to specified alignment
12857 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12858 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12859 else if (vp_playfield->align == ALIGN_CENTER)
12860 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12861 else if (vp_playfield->align == ALIGN_RIGHT)
12862 vp_playfield->x += playfield_width - vp_playfield->width;
12864 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12865 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12866 else if (vp_playfield->valign == VALIGN_MIDDLE)
12867 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12868 else if (vp_playfield->valign == VALIGN_BOTTOM)
12869 vp_playfield->y += playfield_height - vp_playfield->height;
12871 vp_playfield->x += vp_playfield->margin_left;
12872 vp_playfield->y += vp_playfield->margin_top;
12874 // adjust individual playfield borders if only default border is specified
12876 if (vp_playfield->border_left == -1)
12877 vp_playfield->border_left = vp_playfield->border_size;
12878 if (vp_playfield->border_right == -1)
12879 vp_playfield->border_right = vp_playfield->border_size;
12880 if (vp_playfield->border_top == -1)
12881 vp_playfield->border_top = vp_playfield->border_size;
12882 if (vp_playfield->border_bottom == -1)
12883 vp_playfield->border_bottom = vp_playfield->border_size;
12885 // set dynamic playfield borders if borders are specified as undefined
12886 // (but only if window size was dynamic and playfield size was static)
12888 if (dynamic_window_width && !dynamic_playfield_width)
12890 if (vp_playfield->border_left == -1)
12892 vp_playfield->border_left = (vp_playfield->x -
12893 vp_playfield->margin_left);
12894 vp_playfield->x -= vp_playfield->border_left;
12895 vp_playfield->width += vp_playfield->border_left;
12898 if (vp_playfield->border_right == -1)
12900 vp_playfield->border_right = (vp_window->width -
12902 vp_playfield->width -
12903 vp_playfield->margin_right);
12904 vp_playfield->width += vp_playfield->border_right;
12908 if (dynamic_window_height && !dynamic_playfield_height)
12910 if (vp_playfield->border_top == -1)
12912 vp_playfield->border_top = (vp_playfield->y -
12913 vp_playfield->margin_top);
12914 vp_playfield->y -= vp_playfield->border_top;
12915 vp_playfield->height += vp_playfield->border_top;
12918 if (vp_playfield->border_bottom == -1)
12920 vp_playfield->border_bottom = (vp_window->height -
12922 vp_playfield->height -
12923 vp_playfield->margin_bottom);
12924 vp_playfield->height += vp_playfield->border_bottom;
12928 // adjust playfield size to be a multiple of a defined alignment tile size
12930 int align_size = vp_playfield->align_size;
12931 int playfield_xtiles = vp_playfield->width / align_size;
12932 int playfield_ytiles = vp_playfield->height / align_size;
12933 int playfield_width_corrected = playfield_xtiles * align_size;
12934 int playfield_height_corrected = playfield_ytiles * align_size;
12935 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12936 i == GFX_SPECIAL_ARG_EDITOR);
12938 if (is_playfield_mode &&
12939 dynamic_playfield_width &&
12940 vp_playfield->width != playfield_width_corrected)
12942 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12944 vp_playfield->width = playfield_width_corrected;
12946 if (vp_playfield->align == ALIGN_LEFT)
12948 vp_playfield->border_left += playfield_xdiff;
12950 else if (vp_playfield->align == ALIGN_RIGHT)
12952 vp_playfield->border_right += playfield_xdiff;
12954 else if (vp_playfield->align == ALIGN_CENTER)
12956 int border_left_diff = playfield_xdiff / 2;
12957 int border_right_diff = playfield_xdiff - border_left_diff;
12959 vp_playfield->border_left += border_left_diff;
12960 vp_playfield->border_right += border_right_diff;
12964 if (is_playfield_mode &&
12965 dynamic_playfield_height &&
12966 vp_playfield->height != playfield_height_corrected)
12968 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12970 vp_playfield->height = playfield_height_corrected;
12972 if (vp_playfield->valign == VALIGN_TOP)
12974 vp_playfield->border_top += playfield_ydiff;
12976 else if (vp_playfield->align == VALIGN_BOTTOM)
12978 vp_playfield->border_right += playfield_ydiff;
12980 else if (vp_playfield->align == VALIGN_MIDDLE)
12982 int border_top_diff = playfield_ydiff / 2;
12983 int border_bottom_diff = playfield_ydiff - border_top_diff;
12985 vp_playfield->border_top += border_top_diff;
12986 vp_playfield->border_bottom += border_bottom_diff;
12990 // adjust door positions according to specified alignment
12992 for (j = 0; j < 2; j++)
12994 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12996 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12997 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12998 else if (vp_door->align == ALIGN_CENTER)
12999 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13000 else if (vp_door->align == ALIGN_RIGHT)
13001 vp_door->x += vp_window->width - vp_door->width;
13003 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13004 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13005 else if (vp_door->valign == VALIGN_MIDDLE)
13006 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13007 else if (vp_door->valign == VALIGN_BOTTOM)
13008 vp_door->y += vp_window->height - vp_door->height;
13013 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13017 struct XYTileSize *dst, *src;
13020 editor_buttons_xy[] =
13023 &editor.button.element_left, &editor.palette.element_left,
13024 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13027 &editor.button.element_middle, &editor.palette.element_middle,
13028 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13031 &editor.button.element_right, &editor.palette.element_right,
13032 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13039 // set default position for element buttons to element graphics
13040 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13042 if ((*editor_buttons_xy[i].dst).x == -1 &&
13043 (*editor_buttons_xy[i].dst).y == -1)
13045 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13047 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13049 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13053 // adjust editor palette rows and columns if specified to be dynamic
13055 if (editor.palette.cols == -1)
13057 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13058 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13059 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13061 editor.palette.cols = (vp_width - sc_width) / bt_width;
13063 if (editor.palette.x == -1)
13065 int palette_width = editor.palette.cols * bt_width + sc_width;
13067 editor.palette.x = (vp_width - palette_width) / 2;
13071 if (editor.palette.rows == -1)
13073 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13074 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13075 int tx_height = getFontHeight(FONT_TEXT_2);
13077 editor.palette.rows = (vp_height - tx_height) / bt_height;
13079 if (editor.palette.y == -1)
13081 int palette_height = editor.palette.rows * bt_height + tx_height;
13083 editor.palette.y = (vp_height - palette_height) / 2;
13088 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13089 boolean initialize)
13091 // special case: check if network and preview player positions are redefined,
13092 // to compare this later against the main menu level preview being redefined
13093 struct TokenIntPtrInfo menu_config_players[] =
13095 { "main.network_players.x", &menu.main.network_players.redefined },
13096 { "main.network_players.y", &menu.main.network_players.redefined },
13097 { "main.preview_players.x", &menu.main.preview_players.redefined },
13098 { "main.preview_players.y", &menu.main.preview_players.redefined },
13099 { "preview.x", &preview.redefined },
13100 { "preview.y", &preview.redefined }
13106 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13107 *menu_config_players[i].value = FALSE;
13111 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13112 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13113 *menu_config_players[i].value = TRUE;
13117 static void InitMenuDesignSettings_PreviewPlayers(void)
13119 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13122 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13124 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13127 static void LoadMenuDesignSettingsFromFilename(char *filename)
13129 static struct TitleFadingInfo tfi;
13130 static struct TitleMessageInfo tmi;
13131 static struct TokenInfo title_tokens[] =
13133 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13134 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13135 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13136 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13137 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13141 static struct TokenInfo titlemessage_tokens[] =
13143 { TYPE_INTEGER, &tmi.x, ".x" },
13144 { TYPE_INTEGER, &tmi.y, ".y" },
13145 { TYPE_INTEGER, &tmi.width, ".width" },
13146 { TYPE_INTEGER, &tmi.height, ".height" },
13147 { TYPE_INTEGER, &tmi.chars, ".chars" },
13148 { TYPE_INTEGER, &tmi.lines, ".lines" },
13149 { TYPE_INTEGER, &tmi.align, ".align" },
13150 { TYPE_INTEGER, &tmi.valign, ".valign" },
13151 { TYPE_INTEGER, &tmi.font, ".font" },
13152 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13153 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13154 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13155 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13156 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13157 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13158 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13159 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13160 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13166 struct TitleFadingInfo *info;
13171 // initialize first titles from "enter screen" definitions, if defined
13172 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13173 { &title_first_default, "menu.enter_screen.TITLE" },
13175 // initialize title screens from "next screen" definitions, if defined
13176 { &title_initial_default, "menu.next_screen.TITLE" },
13177 { &title_default, "menu.next_screen.TITLE" },
13183 struct TitleMessageInfo *array;
13186 titlemessage_arrays[] =
13188 // initialize first titles from "enter screen" definitions, if defined
13189 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13190 { titlescreen_first, "menu.enter_screen.TITLE" },
13191 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13192 { titlemessage_first, "menu.enter_screen.TITLE" },
13194 // initialize titles from "next screen" definitions, if defined
13195 { titlescreen_initial, "menu.next_screen.TITLE" },
13196 { titlescreen, "menu.next_screen.TITLE" },
13197 { titlemessage_initial, "menu.next_screen.TITLE" },
13198 { titlemessage, "menu.next_screen.TITLE" },
13200 // overwrite titles with title definitions, if defined
13201 { titlescreen_initial_first, "[title_initial]" },
13202 { titlescreen_first, "[title]" },
13203 { titlemessage_initial_first, "[title_initial]" },
13204 { titlemessage_first, "[title]" },
13206 { titlescreen_initial, "[title_initial]" },
13207 { titlescreen, "[title]" },
13208 { titlemessage_initial, "[title_initial]" },
13209 { titlemessage, "[title]" },
13211 // overwrite titles with title screen/message definitions, if defined
13212 { titlescreen_initial_first, "[titlescreen_initial]" },
13213 { titlescreen_first, "[titlescreen]" },
13214 { titlemessage_initial_first, "[titlemessage_initial]" },
13215 { titlemessage_first, "[titlemessage]" },
13217 { titlescreen_initial, "[titlescreen_initial]" },
13218 { titlescreen, "[titlescreen]" },
13219 { titlemessage_initial, "[titlemessage_initial]" },
13220 { titlemessage, "[titlemessage]" },
13224 SetupFileHash *setup_file_hash;
13227 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13230 // the following initializes hierarchical values from dynamic configuration
13232 // special case: initialize with default values that may be overwritten
13233 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13234 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13236 struct TokenIntPtrInfo menu_config[] =
13238 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13239 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13240 { "menu.list_size", &menu.list_size[i] }
13243 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13245 char *token = menu_config[j].token;
13246 char *value = getHashEntry(setup_file_hash, token);
13249 *menu_config[j].value = get_integer_from_string(value);
13253 // special case: initialize with default values that may be overwritten
13254 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13255 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13257 struct TokenIntPtrInfo menu_config[] =
13259 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13260 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13261 { "menu.list_size.INFO", &menu.list_size_info[i] },
13262 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13263 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13266 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13268 char *token = menu_config[j].token;
13269 char *value = getHashEntry(setup_file_hash, token);
13272 *menu_config[j].value = get_integer_from_string(value);
13276 // special case: initialize with default values that may be overwritten
13277 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13278 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13280 struct TokenIntPtrInfo menu_config[] =
13282 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13283 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13286 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13288 char *token = menu_config[j].token;
13289 char *value = getHashEntry(setup_file_hash, token);
13292 *menu_config[j].value = get_integer_from_string(value);
13296 // special case: initialize with default values that may be overwritten
13297 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13298 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13300 struct TokenIntPtrInfo menu_config[] =
13302 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13303 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13304 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13305 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13306 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13307 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13308 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13309 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13310 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13311 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13314 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13316 char *token = menu_config[j].token;
13317 char *value = getHashEntry(setup_file_hash, token);
13320 *menu_config[j].value = get_integer_from_string(value);
13324 // special case: initialize with default values that may be overwritten
13325 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13326 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13328 struct TokenIntPtrInfo menu_config[] =
13330 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13331 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13332 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13333 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13334 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13335 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13336 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13337 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13338 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13341 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13343 char *token = menu_config[j].token;
13344 char *value = getHashEntry(setup_file_hash, token);
13347 *menu_config[j].value = get_token_parameter_value(token, value);
13351 // special case: initialize with default values that may be overwritten
13352 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13353 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13357 char *token_prefix;
13358 struct RectWithBorder *struct_ptr;
13362 { "viewport.window", &viewport.window[i] },
13363 { "viewport.playfield", &viewport.playfield[i] },
13364 { "viewport.door_1", &viewport.door_1[i] },
13365 { "viewport.door_2", &viewport.door_2[i] }
13368 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13370 struct TokenIntPtrInfo vp_config[] =
13372 { ".x", &vp_struct[j].struct_ptr->x },
13373 { ".y", &vp_struct[j].struct_ptr->y },
13374 { ".width", &vp_struct[j].struct_ptr->width },
13375 { ".height", &vp_struct[j].struct_ptr->height },
13376 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13377 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13378 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13379 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13380 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13381 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13382 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13383 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13384 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13385 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13386 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13387 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13388 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13389 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13390 { ".align", &vp_struct[j].struct_ptr->align },
13391 { ".valign", &vp_struct[j].struct_ptr->valign }
13394 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13396 char *token = getStringCat2(vp_struct[j].token_prefix,
13397 vp_config[k].token);
13398 char *value = getHashEntry(setup_file_hash, token);
13401 *vp_config[k].value = get_token_parameter_value(token, value);
13408 // special case: initialize with default values that may be overwritten
13409 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13410 for (i = 0; title_info[i].info != NULL; i++)
13412 struct TitleFadingInfo *info = title_info[i].info;
13413 char *base_token = title_info[i].text;
13415 for (j = 0; title_tokens[j].type != -1; j++)
13417 char *token = getStringCat2(base_token, title_tokens[j].text);
13418 char *value = getHashEntry(setup_file_hash, token);
13422 int parameter_value = get_token_parameter_value(token, value);
13426 *(int *)title_tokens[j].value = (int)parameter_value;
13435 // special case: initialize with default values that may be overwritten
13436 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13437 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13439 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13440 char *base_token = titlemessage_arrays[i].text;
13442 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13444 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13445 char *value = getHashEntry(setup_file_hash, token);
13449 int parameter_value = get_token_parameter_value(token, value);
13451 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13455 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13456 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13458 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13468 // read (and overwrite with) values that may be specified in config file
13469 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13471 // special case: check if network and preview player positions are redefined
13472 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13474 freeSetupFileHash(setup_file_hash);
13477 void LoadMenuDesignSettings(void)
13479 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13481 InitMenuDesignSettings_Static();
13482 InitMenuDesignSettings_SpecialPreProcessing();
13483 InitMenuDesignSettings_PreviewPlayers();
13485 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13487 // first look for special settings configured in level series config
13488 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13490 if (fileExists(filename_base))
13491 LoadMenuDesignSettingsFromFilename(filename_base);
13494 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13496 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13497 LoadMenuDesignSettingsFromFilename(filename_local);
13499 InitMenuDesignSettings_SpecialPostProcessing();
13502 void LoadMenuDesignSettings_AfterGraphics(void)
13504 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13507 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13508 boolean ignore_defaults)
13512 for (i = 0; sound_config_vars[i].token != NULL; i++)
13514 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13516 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13517 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13521 *sound_config_vars[i].value =
13522 get_token_parameter_value(sound_config_vars[i].token, value);
13526 void InitSoundSettings_Static(void)
13528 // always start with reliable default values from static default config
13529 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13532 static void LoadSoundSettingsFromFilename(char *filename)
13534 SetupFileHash *setup_file_hash;
13536 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13539 // read (and overwrite with) values that may be specified in config file
13540 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13542 freeSetupFileHash(setup_file_hash);
13545 void LoadSoundSettings(void)
13547 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13549 InitSoundSettings_Static();
13551 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13553 // first look for special settings configured in level series config
13554 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13556 if (fileExists(filename_base))
13557 LoadSoundSettingsFromFilename(filename_base);
13560 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13562 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13563 LoadSoundSettingsFromFilename(filename_local);
13566 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13568 char *filename = getEditorSetupFilename();
13569 SetupFileList *setup_file_list, *list;
13570 SetupFileHash *element_hash;
13571 int num_unknown_tokens = 0;
13574 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13577 element_hash = newSetupFileHash();
13579 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13580 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13582 // determined size may be larger than needed (due to unknown elements)
13584 for (list = setup_file_list; list != NULL; list = list->next)
13587 // add space for up to 3 more elements for padding that may be needed
13588 *num_elements += 3;
13590 // free memory for old list of elements, if needed
13591 checked_free(*elements);
13593 // allocate memory for new list of elements
13594 *elements = checked_malloc(*num_elements * sizeof(int));
13597 for (list = setup_file_list; list != NULL; list = list->next)
13599 char *value = getHashEntry(element_hash, list->token);
13601 if (value == NULL) // try to find obsolete token mapping
13603 char *mapped_token = get_mapped_token(list->token);
13605 if (mapped_token != NULL)
13607 value = getHashEntry(element_hash, mapped_token);
13609 free(mapped_token);
13615 (*elements)[(*num_elements)++] = atoi(value);
13619 if (num_unknown_tokens == 0)
13622 Warn("unknown token(s) found in config file:");
13623 Warn("- config file: '%s'", filename);
13625 num_unknown_tokens++;
13628 Warn("- token: '%s'", list->token);
13632 if (num_unknown_tokens > 0)
13635 while (*num_elements % 4) // pad with empty elements, if needed
13636 (*elements)[(*num_elements)++] = EL_EMPTY;
13638 freeSetupFileList(setup_file_list);
13639 freeSetupFileHash(element_hash);
13642 for (i = 0; i < *num_elements; i++)
13643 Debug("editor", "element '%s' [%d]\n",
13644 element_info[(*elements)[i]].token_name, (*elements)[i]);
13648 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13651 SetupFileHash *setup_file_hash = NULL;
13652 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13653 char *filename_music, *filename_prefix, *filename_info;
13659 token_to_value_ptr[] =
13661 { "title_header", &tmp_music_file_info.title_header },
13662 { "artist_header", &tmp_music_file_info.artist_header },
13663 { "album_header", &tmp_music_file_info.album_header },
13664 { "year_header", &tmp_music_file_info.year_header },
13665 { "played_header", &tmp_music_file_info.played_header },
13667 { "title", &tmp_music_file_info.title },
13668 { "artist", &tmp_music_file_info.artist },
13669 { "album", &tmp_music_file_info.album },
13670 { "year", &tmp_music_file_info.year },
13671 { "played", &tmp_music_file_info.played },
13677 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13678 getCustomMusicFilename(basename));
13680 if (filename_music == NULL)
13683 // ---------- try to replace file extension ----------
13685 filename_prefix = getStringCopy(filename_music);
13686 if (strrchr(filename_prefix, '.') != NULL)
13687 *strrchr(filename_prefix, '.') = '\0';
13688 filename_info = getStringCat2(filename_prefix, ".txt");
13690 if (fileExists(filename_info))
13691 setup_file_hash = loadSetupFileHash(filename_info);
13693 free(filename_prefix);
13694 free(filename_info);
13696 if (setup_file_hash == NULL)
13698 // ---------- try to add file extension ----------
13700 filename_prefix = getStringCopy(filename_music);
13701 filename_info = getStringCat2(filename_prefix, ".txt");
13703 if (fileExists(filename_info))
13704 setup_file_hash = loadSetupFileHash(filename_info);
13706 free(filename_prefix);
13707 free(filename_info);
13710 if (setup_file_hash == NULL)
13713 // ---------- music file info found ----------
13715 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13717 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13719 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13721 *token_to_value_ptr[i].value_ptr =
13722 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13725 tmp_music_file_info.basename = getStringCopy(basename);
13726 tmp_music_file_info.music = music;
13727 tmp_music_file_info.is_sound = is_sound;
13729 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13730 *new_music_file_info = tmp_music_file_info;
13732 return new_music_file_info;
13735 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13737 return get_music_file_info_ext(basename, music, FALSE);
13740 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13742 return get_music_file_info_ext(basename, sound, TRUE);
13745 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13746 char *basename, boolean is_sound)
13748 for (; list != NULL; list = list->next)
13749 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13755 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13757 return music_info_listed_ext(list, basename, FALSE);
13760 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13762 return music_info_listed_ext(list, basename, TRUE);
13765 void LoadMusicInfo(void)
13767 int num_music_noconf = getMusicListSize_NoConf();
13768 int num_music = getMusicListSize();
13769 int num_sounds = getSoundListSize();
13770 struct FileInfo *music, *sound;
13771 struct MusicFileInfo *next, **new;
13775 while (music_file_info != NULL)
13777 next = music_file_info->next;
13779 checked_free(music_file_info->basename);
13781 checked_free(music_file_info->title_header);
13782 checked_free(music_file_info->artist_header);
13783 checked_free(music_file_info->album_header);
13784 checked_free(music_file_info->year_header);
13785 checked_free(music_file_info->played_header);
13787 checked_free(music_file_info->title);
13788 checked_free(music_file_info->artist);
13789 checked_free(music_file_info->album);
13790 checked_free(music_file_info->year);
13791 checked_free(music_file_info->played);
13793 free(music_file_info);
13795 music_file_info = next;
13798 new = &music_file_info;
13800 // get (configured or unconfigured) music file info for all levels
13801 for (i = leveldir_current->first_level;
13802 i <= leveldir_current->last_level; i++)
13806 if (levelset.music[i] != MUS_UNDEFINED)
13808 // get music file info for configured level music
13809 music_nr = levelset.music[i];
13811 else if (num_music_noconf > 0)
13813 // get music file info for unconfigured level music
13814 int level_pos = i - leveldir_current->first_level;
13816 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13823 char *basename = getMusicInfoEntryFilename(music_nr);
13825 if (basename == NULL)
13828 if (!music_info_listed(music_file_info, basename))
13830 *new = get_music_file_info(basename, music_nr);
13833 new = &(*new)->next;
13837 // get music file info for all remaining configured music files
13838 for (i = 0; i < num_music; i++)
13840 music = getMusicListEntry(i);
13842 if (music->filename == NULL)
13845 if (strEqual(music->filename, UNDEFINED_FILENAME))
13848 // a configured file may be not recognized as music
13849 if (!FileIsMusic(music->filename))
13852 if (!music_info_listed(music_file_info, music->filename))
13854 *new = get_music_file_info(music->filename, i);
13857 new = &(*new)->next;
13861 // get sound file info for all configured sound files
13862 for (i = 0; i < num_sounds; i++)
13864 sound = getSoundListEntry(i);
13866 if (sound->filename == NULL)
13869 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13872 // a configured file may be not recognized as sound
13873 if (!FileIsSound(sound->filename))
13876 if (!sound_info_listed(music_file_info, sound->filename))
13878 *new = get_sound_file_info(sound->filename, i);
13880 new = &(*new)->next;
13884 // add pointers to previous list nodes
13886 struct MusicFileInfo *node = music_file_info;
13888 while (node != NULL)
13891 node->next->prev = node;
13897 static void add_helpanim_entry(int element, int action, int direction,
13898 int delay, int *num_list_entries)
13900 struct HelpAnimInfo *new_list_entry;
13901 (*num_list_entries)++;
13904 checked_realloc(helpanim_info,
13905 *num_list_entries * sizeof(struct HelpAnimInfo));
13906 new_list_entry = &helpanim_info[*num_list_entries - 1];
13908 new_list_entry->element = element;
13909 new_list_entry->action = action;
13910 new_list_entry->direction = direction;
13911 new_list_entry->delay = delay;
13914 static void print_unknown_token(char *filename, char *token, int token_nr)
13919 Warn("unknown token(s) found in config file:");
13920 Warn("- config file: '%s'", filename);
13923 Warn("- token: '%s'", token);
13926 static void print_unknown_token_end(int token_nr)
13932 void LoadHelpAnimInfo(void)
13934 char *filename = getHelpAnimFilename();
13935 SetupFileList *setup_file_list = NULL, *list;
13936 SetupFileHash *element_hash, *action_hash, *direction_hash;
13937 int num_list_entries = 0;
13938 int num_unknown_tokens = 0;
13941 if (fileExists(filename))
13942 setup_file_list = loadSetupFileList(filename);
13944 if (setup_file_list == NULL)
13946 // use reliable default values from static configuration
13947 SetupFileList *insert_ptr;
13949 insert_ptr = setup_file_list =
13950 newSetupFileList(helpanim_config[0].token,
13951 helpanim_config[0].value);
13953 for (i = 1; helpanim_config[i].token; i++)
13954 insert_ptr = addListEntry(insert_ptr,
13955 helpanim_config[i].token,
13956 helpanim_config[i].value);
13959 element_hash = newSetupFileHash();
13960 action_hash = newSetupFileHash();
13961 direction_hash = newSetupFileHash();
13963 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13964 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13966 for (i = 0; i < NUM_ACTIONS; i++)
13967 setHashEntry(action_hash, element_action_info[i].suffix,
13968 i_to_a(element_action_info[i].value));
13970 // do not store direction index (bit) here, but direction value!
13971 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13972 setHashEntry(direction_hash, element_direction_info[i].suffix,
13973 i_to_a(1 << element_direction_info[i].value));
13975 for (list = setup_file_list; list != NULL; list = list->next)
13977 char *element_token, *action_token, *direction_token;
13978 char *element_value, *action_value, *direction_value;
13979 int delay = atoi(list->value);
13981 if (strEqual(list->token, "end"))
13983 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13988 /* first try to break element into element/action/direction parts;
13989 if this does not work, also accept combined "element[.act][.dir]"
13990 elements (like "dynamite.active"), which are unique elements */
13992 if (strchr(list->token, '.') == NULL) // token contains no '.'
13994 element_value = getHashEntry(element_hash, list->token);
13995 if (element_value != NULL) // element found
13996 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13997 &num_list_entries);
14000 // no further suffixes found -- this is not an element
14001 print_unknown_token(filename, list->token, num_unknown_tokens++);
14007 // token has format "<prefix>.<something>"
14009 action_token = strchr(list->token, '.'); // suffix may be action ...
14010 direction_token = action_token; // ... or direction
14012 element_token = getStringCopy(list->token);
14013 *strchr(element_token, '.') = '\0';
14015 element_value = getHashEntry(element_hash, element_token);
14017 if (element_value == NULL) // this is no element
14019 element_value = getHashEntry(element_hash, list->token);
14020 if (element_value != NULL) // combined element found
14021 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14022 &num_list_entries);
14024 print_unknown_token(filename, list->token, num_unknown_tokens++);
14026 free(element_token);
14031 action_value = getHashEntry(action_hash, action_token);
14033 if (action_value != NULL) // action found
14035 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14036 &num_list_entries);
14038 free(element_token);
14043 direction_value = getHashEntry(direction_hash, direction_token);
14045 if (direction_value != NULL) // direction found
14047 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14048 &num_list_entries);
14050 free(element_token);
14055 if (strchr(action_token + 1, '.') == NULL)
14057 // no further suffixes found -- this is not an action nor direction
14059 element_value = getHashEntry(element_hash, list->token);
14060 if (element_value != NULL) // combined element found
14061 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14062 &num_list_entries);
14064 print_unknown_token(filename, list->token, num_unknown_tokens++);
14066 free(element_token);
14071 // token has format "<prefix>.<suffix>.<something>"
14073 direction_token = strchr(action_token + 1, '.');
14075 action_token = getStringCopy(action_token);
14076 *strchr(action_token + 1, '.') = '\0';
14078 action_value = getHashEntry(action_hash, action_token);
14080 if (action_value == NULL) // this is no action
14082 element_value = getHashEntry(element_hash, list->token);
14083 if (element_value != NULL) // combined element found
14084 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14085 &num_list_entries);
14087 print_unknown_token(filename, list->token, num_unknown_tokens++);
14089 free(element_token);
14090 free(action_token);
14095 direction_value = getHashEntry(direction_hash, direction_token);
14097 if (direction_value != NULL) // direction found
14099 add_helpanim_entry(atoi(element_value), atoi(action_value),
14100 atoi(direction_value), delay, &num_list_entries);
14102 free(element_token);
14103 free(action_token);
14108 // this is no direction
14110 element_value = getHashEntry(element_hash, list->token);
14111 if (element_value != NULL) // combined element found
14112 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14113 &num_list_entries);
14115 print_unknown_token(filename, list->token, num_unknown_tokens++);
14117 free(element_token);
14118 free(action_token);
14121 print_unknown_token_end(num_unknown_tokens);
14123 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14124 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14126 freeSetupFileList(setup_file_list);
14127 freeSetupFileHash(element_hash);
14128 freeSetupFileHash(action_hash);
14129 freeSetupFileHash(direction_hash);
14132 for (i = 0; i < num_list_entries; i++)
14133 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14134 EL_NAME(helpanim_info[i].element),
14135 helpanim_info[i].element,
14136 helpanim_info[i].action,
14137 helpanim_info[i].direction,
14138 helpanim_info[i].delay);
14142 void LoadHelpTextInfo(void)
14144 char *filename = getHelpTextFilename();
14147 if (helptext_info != NULL)
14149 freeSetupFileHash(helptext_info);
14150 helptext_info = NULL;
14153 if (fileExists(filename))
14154 helptext_info = loadSetupFileHash(filename);
14156 if (helptext_info == NULL)
14158 // use reliable default values from static configuration
14159 helptext_info = newSetupFileHash();
14161 for (i = 0; helptext_config[i].token; i++)
14162 setHashEntry(helptext_info,
14163 helptext_config[i].token,
14164 helptext_config[i].value);
14168 BEGIN_HASH_ITERATION(helptext_info, itr)
14170 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14171 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14173 END_HASH_ITERATION(hash, itr)
14178 // ----------------------------------------------------------------------------
14180 // ----------------------------------------------------------------------------
14182 #define MAX_NUM_CONVERT_LEVELS 1000
14184 void ConvertLevels(void)
14186 static LevelDirTree *convert_leveldir = NULL;
14187 static int convert_level_nr = -1;
14188 static int num_levels_handled = 0;
14189 static int num_levels_converted = 0;
14190 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14193 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14194 global.convert_leveldir);
14196 if (convert_leveldir == NULL)
14197 Fail("no such level identifier: '%s'", global.convert_leveldir);
14199 leveldir_current = convert_leveldir;
14201 if (global.convert_level_nr != -1)
14203 convert_leveldir->first_level = global.convert_level_nr;
14204 convert_leveldir->last_level = global.convert_level_nr;
14207 convert_level_nr = convert_leveldir->first_level;
14209 PrintLine("=", 79);
14210 Print("Converting levels\n");
14211 PrintLine("-", 79);
14212 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14213 Print("Level series name: '%s'\n", convert_leveldir->name);
14214 Print("Level series author: '%s'\n", convert_leveldir->author);
14215 Print("Number of levels: %d\n", convert_leveldir->levels);
14216 PrintLine("=", 79);
14219 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14220 levels_failed[i] = FALSE;
14222 while (convert_level_nr <= convert_leveldir->last_level)
14224 char *level_filename;
14227 level_nr = convert_level_nr++;
14229 Print("Level %03d: ", level_nr);
14231 LoadLevel(level_nr);
14232 if (level.no_level_file || level.no_valid_file)
14234 Print("(no level)\n");
14238 Print("converting level ... ");
14241 // special case: conversion of some EMC levels as requested by ACME
14242 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14245 level_filename = getDefaultLevelFilename(level_nr);
14246 new_level = !fileExists(level_filename);
14250 SaveLevel(level_nr);
14252 num_levels_converted++;
14254 Print("converted.\n");
14258 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14259 levels_failed[level_nr] = TRUE;
14261 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14264 num_levels_handled++;
14268 PrintLine("=", 79);
14269 Print("Number of levels handled: %d\n", num_levels_handled);
14270 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14271 (num_levels_handled ?
14272 num_levels_converted * 100 / num_levels_handled : 0));
14273 PrintLine("-", 79);
14274 Print("Summary (for automatic parsing by scripts):\n");
14275 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14276 convert_leveldir->identifier, num_levels_converted,
14277 num_levels_handled,
14278 (num_levels_handled ?
14279 num_levels_converted * 100 / num_levels_handled : 0));
14281 if (num_levels_handled != num_levels_converted)
14283 Print(", FAILED:");
14284 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14285 if (levels_failed[i])
14290 PrintLine("=", 79);
14292 CloseAllAndExit(0);
14296 // ----------------------------------------------------------------------------
14297 // create and save images for use in level sketches (raw BMP format)
14298 // ----------------------------------------------------------------------------
14300 void CreateLevelSketchImages(void)
14306 InitElementPropertiesGfxElement();
14308 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14309 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14311 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14313 int element = getMappedElement(i);
14314 char basename1[16];
14315 char basename2[16];
14319 sprintf(basename1, "%04d.bmp", i);
14320 sprintf(basename2, "%04ds.bmp", i);
14322 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14323 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14325 DrawSizedElement(0, 0, element, TILESIZE);
14326 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14328 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14329 Fail("cannot save level sketch image file '%s'", filename1);
14331 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14332 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14334 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14335 Fail("cannot save level sketch image file '%s'", filename2);
14340 // create corresponding SQL statements (for normal and small images)
14343 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14344 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14347 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14348 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14350 // optional: create content for forum level sketch demonstration post
14352 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14355 FreeBitmap(bitmap1);
14356 FreeBitmap(bitmap2);
14359 fprintf(stderr, "\n");
14361 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14363 CloseAllAndExit(0);
14367 // ----------------------------------------------------------------------------
14368 // create and save images for element collecting animations (raw BMP format)
14369 // ----------------------------------------------------------------------------
14371 static boolean createCollectImage(int element)
14373 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14376 void CreateCollectElementImages(void)
14380 int anim_frames = num_steps - 1;
14381 int tile_size = TILESIZE;
14382 int anim_width = tile_size * anim_frames;
14383 int anim_height = tile_size;
14384 int num_collect_images = 0;
14385 int pos_collect_images = 0;
14387 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14388 if (createCollectImage(i))
14389 num_collect_images++;
14391 Info("Creating %d element collecting animation images ...",
14392 num_collect_images);
14394 int dst_width = anim_width * 2;
14395 int dst_height = anim_height * num_collect_images / 2;
14396 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14397 char *basename_bmp = "RocksCollect.bmp";
14398 char *basename_png = "RocksCollect.png";
14399 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14400 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14401 int len_filename_bmp = strlen(filename_bmp);
14402 int len_filename_png = strlen(filename_png);
14403 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14404 char cmd_convert[max_command_len];
14406 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14410 // force using RGBA surface for destination bitmap
14411 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14412 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14414 dst_bitmap->surface =
14415 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14417 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14419 if (!createCollectImage(i))
14422 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14423 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14424 int graphic = el2img(i);
14425 char *token_name = element_info[i].token_name;
14426 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14427 Bitmap *src_bitmap;
14430 Info("- creating collecting image for '%s' ...", token_name);
14432 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14434 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14435 tile_size, tile_size, 0, 0);
14437 // force using RGBA surface for temporary bitmap (using transparent black)
14438 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14439 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14441 tmp_bitmap->surface =
14442 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14444 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14446 for (j = 0; j < anim_frames; j++)
14448 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14449 int frame_size = frame_size_final * num_steps;
14450 int offset = (tile_size - frame_size_final) / 2;
14451 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14453 while (frame_size > frame_size_final)
14457 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14459 FreeBitmap(frame_bitmap);
14461 frame_bitmap = half_bitmap;
14464 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14465 frame_size_final, frame_size_final,
14466 dst_x + j * tile_size + offset, dst_y + offset);
14468 FreeBitmap(frame_bitmap);
14471 tmp_bitmap->surface_masked = NULL;
14473 FreeBitmap(tmp_bitmap);
14475 pos_collect_images++;
14478 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14479 Fail("cannot save element collecting image file '%s'", filename_bmp);
14481 FreeBitmap(dst_bitmap);
14483 Info("Converting image file from BMP to PNG ...");
14485 if (system(cmd_convert) != 0)
14486 Fail("converting image file failed");
14488 unlink(filename_bmp);
14492 CloseAllAndExit(0);
14496 // ----------------------------------------------------------------------------
14497 // create and save images for custom and group elements (raw BMP format)
14498 // ----------------------------------------------------------------------------
14500 void CreateCustomElementImages(char *directory)
14502 char *src_basename = "RocksCE-template.ilbm";
14503 char *dst_basename = "RocksCE.bmp";
14504 char *src_filename = getPath2(directory, src_basename);
14505 char *dst_filename = getPath2(directory, dst_basename);
14506 Bitmap *src_bitmap;
14508 int yoffset_ce = 0;
14509 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14512 InitVideoDefaults();
14514 ReCreateBitmap(&backbuffer, video.width, video.height);
14516 src_bitmap = LoadImage(src_filename);
14518 bitmap = CreateBitmap(TILEX * 16 * 2,
14519 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14522 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14529 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14530 TILEX * x, TILEY * y + yoffset_ce);
14532 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14534 TILEX * x + TILEX * 16,
14535 TILEY * y + yoffset_ce);
14537 for (j = 2; j >= 0; j--)
14541 BlitBitmap(src_bitmap, bitmap,
14542 TILEX + c * 7, 0, 6, 10,
14543 TILEX * x + 6 + j * 7,
14544 TILEY * y + 11 + yoffset_ce);
14546 BlitBitmap(src_bitmap, bitmap,
14547 TILEX + c * 8, TILEY, 6, 10,
14548 TILEX * 16 + TILEX * x + 6 + j * 8,
14549 TILEY * y + 10 + yoffset_ce);
14555 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14562 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14563 TILEX * x, TILEY * y + yoffset_ge);
14565 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14567 TILEX * x + TILEX * 16,
14568 TILEY * y + yoffset_ge);
14570 for (j = 1; j >= 0; j--)
14574 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14575 TILEX * x + 6 + j * 10,
14576 TILEY * y + 11 + yoffset_ge);
14578 BlitBitmap(src_bitmap, bitmap,
14579 TILEX + c * 8, TILEY + 12, 6, 10,
14580 TILEX * 16 + TILEX * x + 10 + j * 8,
14581 TILEY * y + 10 + yoffset_ge);
14587 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14588 Fail("cannot save CE graphics file '%s'", dst_filename);
14590 FreeBitmap(bitmap);
14592 CloseAllAndExit(0);