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
728 // (the following values are related to various game elements)
732 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
733 &li.score[SC_EMERALD], 10
738 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
739 &li.score[SC_DIAMOND], 10
744 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
745 &li.score[SC_BUG], 10
750 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
751 &li.score[SC_SPACESHIP], 10
756 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
757 &li.score[SC_PACMAN], 10
762 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
763 &li.score[SC_NUT], 10
768 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
769 &li.score[SC_DYNAMITE], 10
774 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
775 &li.score[SC_KEY], 10
780 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
781 &li.score[SC_PEARL], 10
786 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
787 &li.score[SC_CRYSTAL], 10
790 // (amoeba values used by R'n'D game engine only)
793 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
794 &li.amoeba_content, EL_DIAMOND
798 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
803 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
804 &li.grow_into_diggable, TRUE
806 // (amoeba values used by BD game engine only)
809 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
810 &li.bd_amoeba_wait_for_hatching, FALSE
814 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
815 &li.bd_amoeba_start_immediately, TRUE
819 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
820 &li.bd_amoeba_2_explode_by_amoeba, TRUE
824 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
825 &li.bd_amoeba_threshold_too_big, 200
829 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
830 &li.bd_amoeba_slow_growth_time, 200
834 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
835 &li.bd_amoeba_slow_growth_rate, 3
839 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
840 &li.bd_amoeba_fast_growth_rate, 25
844 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
845 &li.bd_amoeba_content_too_big, EL_BD_ROCK
849 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
850 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
855 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
856 &li.bd_amoeba_2_threshold_too_big, 200
860 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
861 &li.bd_amoeba_2_slow_growth_time, 200
865 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
866 &li.bd_amoeba_2_slow_growth_rate, 3
870 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
871 &li.bd_amoeba_2_fast_growth_rate, 25
875 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
876 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
880 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
881 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
885 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
886 &li.bd_amoeba_2_content_exploding, EL_EMPTY
890 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
891 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
896 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
897 &li.yamyam_content, EL_ROCK, NULL,
898 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
902 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
903 &li.score[SC_YAMYAM], 10
908 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
909 &li.score[SC_ROBOT], 10
913 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
919 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
925 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
926 &li.time_magic_wall, 10
931 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
932 &li.game_of_life[0], 2
936 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
937 &li.game_of_life[1], 3
941 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
942 &li.game_of_life[2], 3
946 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
947 &li.game_of_life[3], 3
951 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
952 &li.use_life_bugs, FALSE
957 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
962 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
967 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
972 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
977 EL_TIMEGATE_SWITCH, -1,
978 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
979 &li.time_timegate, 10
983 EL_LIGHT_SWITCH_ACTIVE, -1,
984 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
989 EL_SHIELD_NORMAL, -1,
990 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
991 &li.shield_normal_time, 10
994 EL_SHIELD_NORMAL, -1,
995 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
996 &li.score[SC_SHIELD], 10
1000 EL_SHIELD_DEADLY, -1,
1001 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1002 &li.shield_deadly_time, 10
1005 EL_SHIELD_DEADLY, -1,
1006 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1007 &li.score[SC_SHIELD], 10
1012 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1017 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1018 &li.extra_time_score, 10
1022 EL_TIME_ORB_FULL, -1,
1023 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1024 &li.time_orb_time, 10
1027 EL_TIME_ORB_FULL, -1,
1028 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1029 &li.use_time_orb_bug, FALSE
1034 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1035 &li.use_spring_bug, FALSE
1040 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1041 &li.android_move_time, 10
1045 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1046 &li.android_clone_time, 10
1049 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1050 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1051 &li.android_clone_element[0], EL_EMPTY, NULL,
1052 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1056 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1057 &li.android_clone_element[0], EL_EMPTY, NULL,
1058 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1064 &li.lenses_score, 10
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1073 EL_EMC_MAGNIFIER, -1,
1074 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1075 &li.magnify_score, 10
1078 EL_EMC_MAGNIFIER, -1,
1079 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1080 &li.magnify_time, 10
1084 EL_EMC_MAGIC_BALL, -1,
1085 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1089 EL_EMC_MAGIC_BALL, -1,
1090 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1091 &li.ball_random, FALSE
1094 EL_EMC_MAGIC_BALL, -1,
1095 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1096 &li.ball_active_initial, FALSE
1099 EL_EMC_MAGIC_BALL, -1,
1100 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1101 &li.ball_content, EL_EMPTY, NULL,
1102 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1106 EL_SOKOBAN_FIELD_EMPTY, -1,
1107 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1108 &li.sb_fields_needed, TRUE
1112 EL_SOKOBAN_OBJECT, -1,
1113 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1114 &li.sb_objects_needed, TRUE
1119 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1120 &li.mm_laser_red, FALSE
1124 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1125 &li.mm_laser_green, FALSE
1129 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1130 &li.mm_laser_blue, TRUE
1135 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1136 &li.df_laser_red, TRUE
1140 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1141 &li.df_laser_green, TRUE
1145 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1146 &li.df_laser_blue, FALSE
1150 EL_MM_FUSE_ACTIVE, -1,
1151 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1152 &li.mm_time_fuse, 25
1156 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1157 &li.mm_time_bomb, 75
1161 EL_MM_GRAY_BALL, -1,
1162 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1163 &li.mm_time_ball, 75
1166 EL_MM_GRAY_BALL, -1,
1167 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1168 &li.mm_ball_choice_mode, ANIM_RANDOM
1171 EL_MM_GRAY_BALL, -1,
1172 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1173 &li.mm_ball_content, EL_EMPTY, NULL,
1174 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1177 EL_MM_GRAY_BALL, -1,
1178 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1179 &li.rotate_mm_ball_content, TRUE
1182 EL_MM_GRAY_BALL, -1,
1183 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1184 &li.explode_mm_ball, FALSE
1188 EL_MM_STEEL_BLOCK, -1,
1189 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1190 &li.mm_time_block, 75
1193 EL_MM_LIGHTBALL, -1,
1194 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1195 &li.score[SC_ELEM_BONUS], 10
1205 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1209 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1210 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1214 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1215 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1220 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1221 &xx_envelope.autowrap, FALSE
1225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1226 &xx_envelope.centered, FALSE
1231 TYPE_STRING, CONF_VALUE_BYTES(1),
1232 &xx_envelope.text, -1, NULL,
1233 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1234 &xx_default_string_empty[0]
1244 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1248 TYPE_STRING, CONF_VALUE_BYTES(1),
1249 &xx_ei.description[0], -1,
1250 &yy_ei.description[0],
1251 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1252 &xx_default_description[0]
1257 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1258 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1259 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1261 #if ENABLE_RESERVED_CODE
1262 // (reserved for later use)
1265 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1266 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1267 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1273 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1274 &xx_ei.use_gfx_element, FALSE,
1275 &yy_ei.use_gfx_element
1279 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1280 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1281 &yy_ei.gfx_element_initial
1286 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1287 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1288 &yy_ei.access_direction
1293 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1294 &xx_ei.collect_score_initial, 10,
1295 &yy_ei.collect_score_initial
1299 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1300 &xx_ei.collect_count_initial, 1,
1301 &yy_ei.collect_count_initial
1306 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1307 &xx_ei.ce_value_fixed_initial, 0,
1308 &yy_ei.ce_value_fixed_initial
1312 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1313 &xx_ei.ce_value_random_initial, 0,
1314 &yy_ei.ce_value_random_initial
1318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1319 &xx_ei.use_last_ce_value, FALSE,
1320 &yy_ei.use_last_ce_value
1325 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1326 &xx_ei.push_delay_fixed, 8,
1327 &yy_ei.push_delay_fixed
1331 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1332 &xx_ei.push_delay_random, 8,
1333 &yy_ei.push_delay_random
1337 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1338 &xx_ei.drop_delay_fixed, 0,
1339 &yy_ei.drop_delay_fixed
1343 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1344 &xx_ei.drop_delay_random, 0,
1345 &yy_ei.drop_delay_random
1349 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1350 &xx_ei.move_delay_fixed, 0,
1351 &yy_ei.move_delay_fixed
1355 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1356 &xx_ei.move_delay_random, 0,
1357 &yy_ei.move_delay_random
1361 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1362 &xx_ei.step_delay_fixed, 0,
1363 &yy_ei.step_delay_fixed
1367 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1368 &xx_ei.step_delay_random, 0,
1369 &yy_ei.step_delay_random
1374 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1375 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1380 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1381 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1382 &yy_ei.move_direction_initial
1386 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1387 &xx_ei.move_stepsize, TILEX / 8,
1388 &yy_ei.move_stepsize
1393 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1394 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1395 &yy_ei.move_enter_element
1399 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1400 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1401 &yy_ei.move_leave_element
1405 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1406 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1407 &yy_ei.move_leave_type
1412 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1413 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1414 &yy_ei.slippery_type
1419 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1420 &xx_ei.explosion_type, EXPLODES_3X3,
1421 &yy_ei.explosion_type
1425 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1426 &xx_ei.explosion_delay, 16,
1427 &yy_ei.explosion_delay
1431 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1432 &xx_ei.ignition_delay, 8,
1433 &yy_ei.ignition_delay
1438 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1439 &xx_ei.content, EL_EMPTY_SPACE,
1441 &xx_num_contents, 1, 1
1444 // ---------- "num_change_pages" must be the last entry ---------------------
1447 -1, SAVE_CONF_ALWAYS,
1448 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1449 &xx_ei.num_change_pages, 1,
1450 &yy_ei.num_change_pages
1461 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1463 // ---------- "current_change_page" must be the first entry -----------------
1466 -1, SAVE_CONF_ALWAYS,
1467 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1468 &xx_current_change_page, -1
1471 // ---------- (the remaining entries can be in any order) -------------------
1475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1476 &xx_change.can_change, FALSE
1481 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1482 &xx_event_bits[0], 0
1486 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1487 &xx_event_bits[1], 0
1492 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1493 &xx_change.trigger_player, CH_PLAYER_ANY
1497 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1498 &xx_change.trigger_side, CH_SIDE_ANY
1502 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1503 &xx_change.trigger_page, CH_PAGE_ANY
1508 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1509 &xx_change.target_element, EL_EMPTY_SPACE
1514 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1515 &xx_change.delay_fixed, 0
1519 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1520 &xx_change.delay_random, 0
1524 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1525 &xx_change.delay_frames, FRAMES_PER_SECOND
1530 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1531 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1536 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1537 &xx_change.explode, FALSE
1541 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1542 &xx_change.use_target_content, FALSE
1546 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1547 &xx_change.only_if_complete, FALSE
1551 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1552 &xx_change.use_random_replace, FALSE
1556 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1557 &xx_change.random_percentage, 100
1561 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1562 &xx_change.replace_when, CP_WHEN_EMPTY
1567 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1568 &xx_change.has_action, FALSE
1572 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1573 &xx_change.action_type, CA_NO_ACTION
1577 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1578 &xx_change.action_mode, CA_MODE_UNDEFINED
1582 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1583 &xx_change.action_arg, CA_ARG_UNDEFINED
1588 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1589 &xx_change.action_element, EL_EMPTY_SPACE
1594 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1595 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1596 &xx_num_contents, 1, 1
1606 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1610 TYPE_STRING, CONF_VALUE_BYTES(1),
1611 &xx_ei.description[0], -1, NULL,
1612 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1613 &xx_default_description[0]
1618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1619 &xx_ei.use_gfx_element, FALSE
1623 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1624 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1629 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1630 &xx_group.choice_mode, ANIM_RANDOM
1635 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1636 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1637 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1647 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1651 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1652 &xx_ei.use_gfx_element, FALSE
1656 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1657 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1667 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1671 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1672 &li.block_snap_field, TRUE
1676 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1677 &li.continuous_snapping, TRUE
1681 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1682 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1686 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1687 &li.use_start_element[0], FALSE
1691 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1692 &li.start_element[0], EL_PLAYER_1
1696 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1697 &li.use_artwork_element[0], FALSE
1701 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1702 &li.artwork_element[0], EL_PLAYER_1
1706 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1707 &li.use_explosion_element[0], FALSE
1711 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1712 &li.explosion_element[0], EL_PLAYER_1
1727 filetype_id_list[] =
1729 { LEVEL_FILE_TYPE_RND, "RND" },
1730 { LEVEL_FILE_TYPE_BD, "BD" },
1731 { LEVEL_FILE_TYPE_EM, "EM" },
1732 { LEVEL_FILE_TYPE_SP, "SP" },
1733 { LEVEL_FILE_TYPE_DX, "DX" },
1734 { LEVEL_FILE_TYPE_SB, "SB" },
1735 { LEVEL_FILE_TYPE_DC, "DC" },
1736 { LEVEL_FILE_TYPE_MM, "MM" },
1737 { LEVEL_FILE_TYPE_MM, "DF" },
1742 // ============================================================================
1743 // level file functions
1744 // ============================================================================
1746 static boolean check_special_flags(char *flag)
1748 if (strEqual(options.special_flags, flag) ||
1749 strEqual(leveldir_current->special_flags, flag))
1755 static struct DateInfo getCurrentDate(void)
1757 time_t epoch_seconds = time(NULL);
1758 struct tm *now = localtime(&epoch_seconds);
1759 struct DateInfo date;
1761 date.year = now->tm_year + 1900;
1762 date.month = now->tm_mon + 1;
1763 date.day = now->tm_mday;
1765 date.src = DATE_SRC_CLOCK;
1770 static void resetEventFlags(struct ElementChangeInfo *change)
1774 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1775 change->has_event[i] = FALSE;
1778 static void resetEventBits(void)
1782 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1783 xx_event_bits[i] = 0;
1786 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1790 /* important: only change event flag if corresponding event bit is set
1791 (this is because all xx_event_bits[] values are loaded separately,
1792 and all xx_event_bits[] values are set back to zero before loading
1793 another value xx_event_bits[x] (each value representing 32 flags)) */
1795 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1796 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1797 change->has_event[i] = TRUE;
1800 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1804 /* in contrast to the above function setEventFlagsFromEventBits(), it
1805 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1806 depending on the corresponding change->has_event[i] values here, as
1807 all xx_event_bits[] values are reset in resetEventBits() before */
1809 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1810 if (change->has_event[i])
1811 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1814 static char *getDefaultElementDescription(struct ElementInfo *ei)
1816 static char description[MAX_ELEMENT_NAME_LEN + 1];
1817 char *default_description = (ei->custom_description != NULL ?
1818 ei->custom_description :
1819 ei->editor_description);
1822 // always start with reliable default values
1823 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1824 description[i] = '\0';
1826 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1827 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1829 return &description[0];
1832 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1834 char *default_description = getDefaultElementDescription(ei);
1837 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1838 ei->description[i] = default_description[i];
1841 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1845 for (i = 0; conf[i].data_type != -1; i++)
1847 int default_value = conf[i].default_value;
1848 int data_type = conf[i].data_type;
1849 int conf_type = conf[i].conf_type;
1850 int byte_mask = conf_type & CONF_MASK_BYTES;
1852 if (byte_mask == CONF_MASK_MULTI_BYTES)
1854 int default_num_entities = conf[i].default_num_entities;
1855 int max_num_entities = conf[i].max_num_entities;
1857 *(int *)(conf[i].num_entities) = default_num_entities;
1859 if (data_type == TYPE_STRING)
1861 char *default_string = conf[i].default_string;
1862 char *string = (char *)(conf[i].value);
1864 strncpy(string, default_string, max_num_entities);
1866 else if (data_type == TYPE_ELEMENT_LIST)
1868 int *element_array = (int *)(conf[i].value);
1871 for (j = 0; j < max_num_entities; j++)
1872 element_array[j] = default_value;
1874 else if (data_type == TYPE_CONTENT_LIST)
1876 struct Content *content = (struct Content *)(conf[i].value);
1879 for (c = 0; c < max_num_entities; c++)
1880 for (y = 0; y < 3; y++)
1881 for (x = 0; x < 3; x++)
1882 content[c].e[x][y] = default_value;
1885 else // constant size configuration data (1, 2 or 4 bytes)
1887 if (data_type == TYPE_BOOLEAN)
1888 *(boolean *)(conf[i].value) = default_value;
1890 *(int *) (conf[i].value) = default_value;
1895 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1899 for (i = 0; conf[i].data_type != -1; i++)
1901 int data_type = conf[i].data_type;
1902 int conf_type = conf[i].conf_type;
1903 int byte_mask = conf_type & CONF_MASK_BYTES;
1905 if (byte_mask == CONF_MASK_MULTI_BYTES)
1907 int max_num_entities = conf[i].max_num_entities;
1909 if (data_type == TYPE_STRING)
1911 char *string = (char *)(conf[i].value);
1912 char *string_copy = (char *)(conf[i].value_copy);
1914 strncpy(string_copy, string, max_num_entities);
1916 else if (data_type == TYPE_ELEMENT_LIST)
1918 int *element_array = (int *)(conf[i].value);
1919 int *element_array_copy = (int *)(conf[i].value_copy);
1922 for (j = 0; j < max_num_entities; j++)
1923 element_array_copy[j] = element_array[j];
1925 else if (data_type == TYPE_CONTENT_LIST)
1927 struct Content *content = (struct Content *)(conf[i].value);
1928 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1931 for (c = 0; c < max_num_entities; c++)
1932 for (y = 0; y < 3; y++)
1933 for (x = 0; x < 3; x++)
1934 content_copy[c].e[x][y] = content[c].e[x][y];
1937 else // constant size configuration data (1, 2 or 4 bytes)
1939 if (data_type == TYPE_BOOLEAN)
1940 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1942 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1947 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1951 xx_ei = *ei_from; // copy element data into temporary buffer
1952 yy_ei = *ei_to; // copy element data into temporary buffer
1954 copyConfigFromConfigList(chunk_config_CUSX_base);
1959 // ---------- reinitialize and copy change pages ----------
1961 ei_to->num_change_pages = ei_from->num_change_pages;
1962 ei_to->current_change_page = ei_from->current_change_page;
1964 setElementChangePages(ei_to, ei_to->num_change_pages);
1966 for (i = 0; i < ei_to->num_change_pages; i++)
1967 ei_to->change_page[i] = ei_from->change_page[i];
1969 // ---------- copy group element info ----------
1970 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1971 *ei_to->group = *ei_from->group;
1973 // mark this custom element as modified
1974 ei_to->modified_settings = TRUE;
1977 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1979 int change_page_size = sizeof(struct ElementChangeInfo);
1981 ei->num_change_pages = MAX(1, change_pages);
1984 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1986 if (ei->current_change_page >= ei->num_change_pages)
1987 ei->current_change_page = ei->num_change_pages - 1;
1989 ei->change = &ei->change_page[ei->current_change_page];
1992 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1994 xx_change = *change; // copy change data into temporary buffer
1996 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1998 *change = xx_change;
2000 resetEventFlags(change);
2002 change->direct_action = 0;
2003 change->other_action = 0;
2005 change->pre_change_function = NULL;
2006 change->change_function = NULL;
2007 change->post_change_function = NULL;
2010 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2014 li = *level; // copy level data into temporary buffer
2015 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2016 *level = li; // copy temporary buffer back to level data
2018 setLevelInfoToDefaults_BD();
2019 setLevelInfoToDefaults_EM();
2020 setLevelInfoToDefaults_SP();
2021 setLevelInfoToDefaults_MM();
2023 level->native_bd_level = &native_bd_level;
2024 level->native_em_level = &native_em_level;
2025 level->native_sp_level = &native_sp_level;
2026 level->native_mm_level = &native_mm_level;
2028 level->file_version = FILE_VERSION_ACTUAL;
2029 level->game_version = GAME_VERSION_ACTUAL;
2031 level->creation_date = getCurrentDate();
2033 level->encoding_16bit_field = TRUE;
2034 level->encoding_16bit_yamyam = TRUE;
2035 level->encoding_16bit_amoeba = TRUE;
2037 // clear level name and level author string buffers
2038 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2039 level->name[i] = '\0';
2040 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2041 level->author[i] = '\0';
2043 // set level name and level author to default values
2044 strcpy(level->name, NAMELESS_LEVEL_NAME);
2045 strcpy(level->author, ANONYMOUS_NAME);
2047 // set level playfield to playable default level with player and exit
2048 for (x = 0; x < MAX_LEV_FIELDX; x++)
2049 for (y = 0; y < MAX_LEV_FIELDY; y++)
2050 level->field[x][y] = EL_SAND;
2052 level->field[0][0] = EL_PLAYER_1;
2053 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2055 BorderElement = EL_STEELWALL;
2057 // detect custom elements when loading them
2058 level->file_has_custom_elements = FALSE;
2060 // set all bug compatibility flags to "false" => do not emulate this bug
2061 level->use_action_after_change_bug = FALSE;
2063 if (leveldir_current)
2065 // try to determine better author name than 'anonymous'
2066 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2068 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2069 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2073 switch (LEVELCLASS(leveldir_current))
2075 case LEVELCLASS_TUTORIAL:
2076 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2079 case LEVELCLASS_CONTRIB:
2080 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2081 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2084 case LEVELCLASS_PRIVATE:
2085 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2086 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2090 // keep default value
2097 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2099 static boolean clipboard_elements_initialized = FALSE;
2102 InitElementPropertiesStatic();
2104 li = *level; // copy level data into temporary buffer
2105 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2106 *level = li; // copy temporary buffer back to level data
2108 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2111 struct ElementInfo *ei = &element_info[element];
2113 if (element == EL_MM_GRAY_BALL)
2115 struct LevelInfo_MM *level_mm = level->native_mm_level;
2118 for (j = 0; j < level->num_mm_ball_contents; j++)
2119 level->mm_ball_content[j] =
2120 map_element_MM_to_RND(level_mm->ball_content[j]);
2123 // never initialize clipboard elements after the very first time
2124 // (to be able to use clipboard elements between several levels)
2125 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2128 if (IS_ENVELOPE(element))
2130 int envelope_nr = element - EL_ENVELOPE_1;
2132 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2134 level->envelope[envelope_nr] = xx_envelope;
2137 if (IS_CUSTOM_ELEMENT(element) ||
2138 IS_GROUP_ELEMENT(element) ||
2139 IS_INTERNAL_ELEMENT(element))
2141 xx_ei = *ei; // copy element data into temporary buffer
2143 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2148 setElementChangePages(ei, 1);
2149 setElementChangeInfoToDefaults(ei->change);
2151 if (IS_CUSTOM_ELEMENT(element) ||
2152 IS_GROUP_ELEMENT(element))
2154 setElementDescriptionToDefault(ei);
2156 ei->modified_settings = FALSE;
2159 if (IS_CUSTOM_ELEMENT(element) ||
2160 IS_INTERNAL_ELEMENT(element))
2162 // internal values used in level editor
2164 ei->access_type = 0;
2165 ei->access_layer = 0;
2166 ei->access_protected = 0;
2167 ei->walk_to_action = 0;
2168 ei->smash_targets = 0;
2171 ei->can_explode_by_fire = FALSE;
2172 ei->can_explode_smashed = FALSE;
2173 ei->can_explode_impact = FALSE;
2175 ei->current_change_page = 0;
2178 if (IS_GROUP_ELEMENT(element) ||
2179 IS_INTERNAL_ELEMENT(element))
2181 struct ElementGroupInfo *group;
2183 // initialize memory for list of elements in group
2184 if (ei->group == NULL)
2185 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2189 xx_group = *group; // copy group data into temporary buffer
2191 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2196 if (IS_EMPTY_ELEMENT(element) ||
2197 IS_INTERNAL_ELEMENT(element))
2199 xx_ei = *ei; // copy element data into temporary buffer
2201 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2207 clipboard_elements_initialized = TRUE;
2210 static void setLevelInfoToDefaults(struct LevelInfo *level,
2211 boolean level_info_only,
2212 boolean reset_file_status)
2214 setLevelInfoToDefaults_Level(level);
2216 if (!level_info_only)
2217 setLevelInfoToDefaults_Elements(level);
2219 if (reset_file_status)
2221 level->no_valid_file = FALSE;
2222 level->no_level_file = FALSE;
2225 level->changed = FALSE;
2228 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2230 level_file_info->nr = 0;
2231 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2232 level_file_info->packed = FALSE;
2234 setString(&level_file_info->basename, NULL);
2235 setString(&level_file_info->filename, NULL);
2238 int getMappedElement_SB(int, boolean);
2240 static void ActivateLevelTemplate(void)
2244 if (check_special_flags("load_xsb_to_ces"))
2246 // fill smaller playfields with padding "beyond border wall" elements
2247 if (level.fieldx < level_template.fieldx ||
2248 level.fieldy < level_template.fieldy)
2250 short field[level.fieldx][level.fieldy];
2251 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2252 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2253 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2254 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2256 // copy old playfield (which is smaller than the visible area)
2257 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2258 field[x][y] = level.field[x][y];
2260 // fill new, larger playfield with "beyond border wall" elements
2261 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2262 level.field[x][y] = getMappedElement_SB('_', TRUE);
2264 // copy the old playfield to the middle of the new playfield
2265 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2266 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2268 level.fieldx = new_fieldx;
2269 level.fieldy = new_fieldy;
2273 // Currently there is no special action needed to activate the template
2274 // data, because 'element_info' property settings overwrite the original
2275 // level data, while all other variables do not change.
2277 // Exception: 'from_level_template' elements in the original level playfield
2278 // are overwritten with the corresponding elements at the same position in
2279 // playfield from the level template.
2281 for (x = 0; x < level.fieldx; x++)
2282 for (y = 0; y < level.fieldy; y++)
2283 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2284 level.field[x][y] = level_template.field[x][y];
2286 if (check_special_flags("load_xsb_to_ces"))
2288 struct LevelInfo level_backup = level;
2290 // overwrite all individual level settings from template level settings
2291 level = level_template;
2293 // restore level file info
2294 level.file_info = level_backup.file_info;
2296 // restore playfield size
2297 level.fieldx = level_backup.fieldx;
2298 level.fieldy = level_backup.fieldy;
2300 // restore playfield content
2301 for (x = 0; x < level.fieldx; x++)
2302 for (y = 0; y < level.fieldy; y++)
2303 level.field[x][y] = level_backup.field[x][y];
2305 // restore name and author from individual level
2306 strcpy(level.name, level_backup.name);
2307 strcpy(level.author, level_backup.author);
2309 // restore flag "use_custom_template"
2310 level.use_custom_template = level_backup.use_custom_template;
2314 static boolean checkForPackageFromBasename_BD(char *basename)
2316 // check for native BD level file extensions
2317 if (!strSuffixLower(basename, ".bd") &&
2318 !strSuffixLower(basename, ".bdr") &&
2319 !strSuffixLower(basename, ".brc") &&
2320 !strSuffixLower(basename, ".gds"))
2323 // check for standard single-level BD files (like "001.bd")
2324 if (strSuffixLower(basename, ".bd") &&
2325 strlen(basename) == 6 &&
2326 basename[0] >= '0' && basename[0] <= '9' &&
2327 basename[1] >= '0' && basename[1] <= '9' &&
2328 basename[2] >= '0' && basename[2] <= '9')
2331 // this is a level package in native BD file format
2335 static char *getLevelFilenameFromBasename(char *basename)
2337 static char *filename = NULL;
2339 checked_free(filename);
2341 filename = getPath2(getCurrentLevelDir(), basename);
2346 static int getFileTypeFromBasename(char *basename)
2348 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2350 static char *filename = NULL;
2351 struct stat file_status;
2353 // ---------- try to determine file type from filename ----------
2355 // check for typical filename of a Supaplex level package file
2356 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2357 return LEVEL_FILE_TYPE_SP;
2359 // check for typical filename of a Diamond Caves II level package file
2360 if (strSuffixLower(basename, ".dc") ||
2361 strSuffixLower(basename, ".dc2"))
2362 return LEVEL_FILE_TYPE_DC;
2364 // check for typical filename of a Sokoban level package file
2365 if (strSuffixLower(basename, ".xsb") &&
2366 strchr(basename, '%') == NULL)
2367 return LEVEL_FILE_TYPE_SB;
2369 // check for typical filename of a Boulder Dash (GDash) level package file
2370 if (checkForPackageFromBasename_BD(basename))
2371 return LEVEL_FILE_TYPE_BD;
2373 // ---------- try to determine file type from filesize ----------
2375 checked_free(filename);
2376 filename = getPath2(getCurrentLevelDir(), basename);
2378 if (stat(filename, &file_status) == 0)
2380 // check for typical filesize of a Supaplex level package file
2381 if (file_status.st_size == 170496)
2382 return LEVEL_FILE_TYPE_SP;
2385 return LEVEL_FILE_TYPE_UNKNOWN;
2388 static int getFileTypeFromMagicBytes(char *filename, int type)
2392 if ((file = openFile(filename, MODE_READ)))
2394 char chunk_name[CHUNK_ID_LEN + 1];
2396 getFileChunkBE(file, chunk_name, NULL);
2398 if (strEqual(chunk_name, "MMII") ||
2399 strEqual(chunk_name, "MIRR"))
2400 type = LEVEL_FILE_TYPE_MM;
2408 static boolean checkForPackageFromBasename(char *basename)
2410 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2411 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2413 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2416 static char *getSingleLevelBasenameExt(int nr, char *extension)
2418 static char basename[MAX_FILENAME_LEN];
2421 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2423 sprintf(basename, "%03d.%s", nr, extension);
2428 static char *getSingleLevelBasename(int nr)
2430 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2433 static char *getPackedLevelBasename(int type)
2435 static char basename[MAX_FILENAME_LEN];
2436 char *directory = getCurrentLevelDir();
2438 DirectoryEntry *dir_entry;
2440 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2442 if ((dir = openDirectory(directory)) == NULL)
2444 Warn("cannot read current level directory '%s'", directory);
2449 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2451 char *entry_basename = dir_entry->basename;
2452 int entry_type = getFileTypeFromBasename(entry_basename);
2454 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2456 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2459 strcpy(basename, entry_basename);
2466 closeDirectory(dir);
2471 static char *getSingleLevelFilename(int nr)
2473 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2476 #if ENABLE_UNUSED_CODE
2477 static char *getPackedLevelFilename(int type)
2479 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2483 char *getDefaultLevelFilename(int nr)
2485 return getSingleLevelFilename(nr);
2488 #if ENABLE_UNUSED_CODE
2489 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2493 lfi->packed = FALSE;
2495 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2496 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2500 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2501 int type, char *format, ...)
2503 static char basename[MAX_FILENAME_LEN];
2506 va_start(ap, format);
2507 vsprintf(basename, format, ap);
2511 lfi->packed = FALSE;
2513 setString(&lfi->basename, basename);
2514 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2517 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2523 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2524 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2527 static int getFiletypeFromID(char *filetype_id)
2529 char *filetype_id_lower;
2530 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2533 if (filetype_id == NULL)
2534 return LEVEL_FILE_TYPE_UNKNOWN;
2536 filetype_id_lower = getStringToLower(filetype_id);
2538 for (i = 0; filetype_id_list[i].id != NULL; i++)
2540 char *id_lower = getStringToLower(filetype_id_list[i].id);
2542 if (strEqual(filetype_id_lower, id_lower))
2543 filetype = filetype_id_list[i].filetype;
2547 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2551 free(filetype_id_lower);
2556 char *getLocalLevelTemplateFilename(void)
2558 return getDefaultLevelFilename(-1);
2561 char *getGlobalLevelTemplateFilename(void)
2563 // global variable "leveldir_current" must be modified in the loop below
2564 LevelDirTree *leveldir_current_last = leveldir_current;
2565 char *filename = NULL;
2567 // check for template level in path from current to topmost tree node
2569 while (leveldir_current != NULL)
2571 filename = getDefaultLevelFilename(-1);
2573 if (fileExists(filename))
2576 leveldir_current = leveldir_current->node_parent;
2579 // restore global variable "leveldir_current" modified in above loop
2580 leveldir_current = leveldir_current_last;
2585 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2589 // special case: level number is negative => check for level template file
2592 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2593 getSingleLevelBasename(-1));
2595 // replace local level template filename with global template filename
2596 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2598 // no fallback if template file not existing
2602 // special case: check for file name/pattern specified in "levelinfo.conf"
2603 if (leveldir_current->level_filename != NULL)
2605 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2607 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2608 leveldir_current->level_filename, nr);
2610 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2612 if (fileExists(lfi->filename))
2615 else if (leveldir_current->level_filetype != NULL)
2617 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2619 // check for specified native level file with standard file name
2620 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2621 "%03d.%s", nr, LEVELFILE_EXTENSION);
2622 if (fileExists(lfi->filename))
2626 // check for native Rocks'n'Diamonds level file
2627 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2628 "%03d.%s", nr, LEVELFILE_EXTENSION);
2629 if (fileExists(lfi->filename))
2632 // check for native Boulder Dash level file
2633 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2634 if (fileExists(lfi->filename))
2637 // check for Emerald Mine level file (V1)
2638 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2639 'a' + (nr / 10) % 26, '0' + nr % 10);
2640 if (fileExists(lfi->filename))
2642 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2643 'A' + (nr / 10) % 26, '0' + nr % 10);
2644 if (fileExists(lfi->filename))
2647 // check for Emerald Mine level file (V2 to V5)
2648 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2649 if (fileExists(lfi->filename))
2652 // check for Emerald Mine level file (V6 / single mode)
2653 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2654 if (fileExists(lfi->filename))
2656 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2657 if (fileExists(lfi->filename))
2660 // check for Emerald Mine level file (V6 / teamwork mode)
2661 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2662 if (fileExists(lfi->filename))
2664 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2665 if (fileExists(lfi->filename))
2668 // check for various packed level file formats
2669 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2670 if (fileExists(lfi->filename))
2673 // no known level file found -- use default values (and fail later)
2674 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2675 "%03d.%s", nr, LEVELFILE_EXTENSION);
2678 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2680 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2681 lfi->type = getFileTypeFromBasename(lfi->basename);
2683 if (lfi->type == LEVEL_FILE_TYPE_RND)
2684 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2687 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2689 // always start with reliable default values
2690 setFileInfoToDefaults(level_file_info);
2692 level_file_info->nr = nr; // set requested level number
2694 determineLevelFileInfo_Filename(level_file_info);
2695 determineLevelFileInfo_Filetype(level_file_info);
2698 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2699 struct LevelFileInfo *lfi_to)
2701 lfi_to->nr = lfi_from->nr;
2702 lfi_to->type = lfi_from->type;
2703 lfi_to->packed = lfi_from->packed;
2705 setString(&lfi_to->basename, lfi_from->basename);
2706 setString(&lfi_to->filename, lfi_from->filename);
2709 // ----------------------------------------------------------------------------
2710 // functions for loading R'n'D level
2711 // ----------------------------------------------------------------------------
2713 int getMappedElement(int element)
2715 // remap some (historic, now obsolete) elements
2719 case EL_PLAYER_OBSOLETE:
2720 element = EL_PLAYER_1;
2723 case EL_KEY_OBSOLETE:
2727 case EL_EM_KEY_1_FILE_OBSOLETE:
2728 element = EL_EM_KEY_1;
2731 case EL_EM_KEY_2_FILE_OBSOLETE:
2732 element = EL_EM_KEY_2;
2735 case EL_EM_KEY_3_FILE_OBSOLETE:
2736 element = EL_EM_KEY_3;
2739 case EL_EM_KEY_4_FILE_OBSOLETE:
2740 element = EL_EM_KEY_4;
2743 case EL_ENVELOPE_OBSOLETE:
2744 element = EL_ENVELOPE_1;
2752 if (element >= NUM_FILE_ELEMENTS)
2754 Warn("invalid level element %d", element);
2756 element = EL_UNKNOWN;
2764 static int getMappedElementByVersion(int element, int game_version)
2766 // remap some elements due to certain game version
2768 if (game_version <= VERSION_IDENT(2,2,0,0))
2770 // map game font elements
2771 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2772 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2773 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2774 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2777 if (game_version < VERSION_IDENT(3,0,0,0))
2779 // map Supaplex gravity tube elements
2780 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2781 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2782 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2783 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2790 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2792 level->file_version = getFileVersion(file);
2793 level->game_version = getFileVersion(file);
2798 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2800 level->creation_date.year = getFile16BitBE(file);
2801 level->creation_date.month = getFile8Bit(file);
2802 level->creation_date.day = getFile8Bit(file);
2804 level->creation_date.src = DATE_SRC_LEVELFILE;
2809 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2811 int initial_player_stepsize;
2812 int initial_player_gravity;
2815 level->fieldx = getFile8Bit(file);
2816 level->fieldy = getFile8Bit(file);
2818 level->time = getFile16BitBE(file);
2819 level->gems_needed = getFile16BitBE(file);
2821 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2822 level->name[i] = getFile8Bit(file);
2823 level->name[MAX_LEVEL_NAME_LEN] = 0;
2825 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2826 level->score[i] = getFile8Bit(file);
2828 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2829 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2830 for (y = 0; y < 3; y++)
2831 for (x = 0; x < 3; x++)
2832 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2834 level->amoeba_speed = getFile8Bit(file);
2835 level->time_magic_wall = getFile8Bit(file);
2836 level->time_wheel = getFile8Bit(file);
2837 level->amoeba_content = getMappedElement(getFile8Bit(file));
2839 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2842 for (i = 0; i < MAX_PLAYERS; i++)
2843 level->initial_player_stepsize[i] = initial_player_stepsize;
2845 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2847 for (i = 0; i < MAX_PLAYERS; i++)
2848 level->initial_player_gravity[i] = initial_player_gravity;
2850 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2851 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2853 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2855 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2856 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2857 level->can_move_into_acid_bits = getFile32BitBE(file);
2858 level->dont_collide_with_bits = getFile8Bit(file);
2860 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2861 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2863 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2864 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2865 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2867 level->game_engine_type = getFile8Bit(file);
2869 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2874 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2878 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2879 level->name[i] = getFile8Bit(file);
2880 level->name[MAX_LEVEL_NAME_LEN] = 0;
2885 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2889 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2890 level->author[i] = getFile8Bit(file);
2891 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2896 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2899 int chunk_size_expected = level->fieldx * level->fieldy;
2901 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2902 stored with 16-bit encoding (and should be twice as big then).
2903 Even worse, playfield data was stored 16-bit when only yamyam content
2904 contained 16-bit elements and vice versa. */
2906 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2907 chunk_size_expected *= 2;
2909 if (chunk_size_expected != chunk_size)
2911 ReadUnusedBytesFromFile(file, chunk_size);
2912 return chunk_size_expected;
2915 for (y = 0; y < level->fieldy; y++)
2916 for (x = 0; x < level->fieldx; x++)
2917 level->field[x][y] =
2918 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2923 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2926 int header_size = 4;
2927 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2928 int chunk_size_expected = header_size + content_size;
2930 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2931 stored with 16-bit encoding (and should be twice as big then).
2932 Even worse, playfield data was stored 16-bit when only yamyam content
2933 contained 16-bit elements and vice versa. */
2935 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2936 chunk_size_expected += content_size;
2938 if (chunk_size_expected != chunk_size)
2940 ReadUnusedBytesFromFile(file, chunk_size);
2941 return chunk_size_expected;
2945 level->num_yamyam_contents = getFile8Bit(file);
2949 // correct invalid number of content fields -- should never happen
2950 if (level->num_yamyam_contents < 1 ||
2951 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2952 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2954 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2955 for (y = 0; y < 3; y++)
2956 for (x = 0; x < 3; x++)
2957 level->yamyam_content[i].e[x][y] =
2958 getMappedElement(level->encoding_16bit_field ?
2959 getFile16BitBE(file) : getFile8Bit(file));
2963 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2968 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2970 element = getMappedElement(getFile16BitBE(file));
2971 num_contents = getFile8Bit(file);
2973 getFile8Bit(file); // content x size (unused)
2974 getFile8Bit(file); // content y size (unused)
2976 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2978 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2979 for (y = 0; y < 3; y++)
2980 for (x = 0; x < 3; x++)
2981 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2983 // correct invalid number of content fields -- should never happen
2984 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2985 num_contents = STD_ELEMENT_CONTENTS;
2987 if (element == EL_YAMYAM)
2989 level->num_yamyam_contents = num_contents;
2991 for (i = 0; i < num_contents; i++)
2992 for (y = 0; y < 3; y++)
2993 for (x = 0; x < 3; x++)
2994 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2996 else if (element == EL_BD_AMOEBA)
2998 level->amoeba_content = content_array[0][0][0];
3002 Warn("cannot load content for element '%d'", element);
3008 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3014 int chunk_size_expected;
3016 element = getMappedElement(getFile16BitBE(file));
3017 if (!IS_ENVELOPE(element))
3018 element = EL_ENVELOPE_1;
3020 envelope_nr = element - EL_ENVELOPE_1;
3022 envelope_len = getFile16BitBE(file);
3024 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3025 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3027 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3029 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3030 if (chunk_size_expected != chunk_size)
3032 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3033 return chunk_size_expected;
3036 for (i = 0; i < envelope_len; i++)
3037 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3042 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3044 int num_changed_custom_elements = getFile16BitBE(file);
3045 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3048 if (chunk_size_expected != chunk_size)
3050 ReadUnusedBytesFromFile(file, chunk_size - 2);
3051 return chunk_size_expected;
3054 for (i = 0; i < num_changed_custom_elements; i++)
3056 int element = getMappedElement(getFile16BitBE(file));
3057 int properties = getFile32BitBE(file);
3059 if (IS_CUSTOM_ELEMENT(element))
3060 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3062 Warn("invalid custom element number %d", element);
3064 // older game versions that wrote level files with CUS1 chunks used
3065 // different default push delay values (not yet stored in level file)
3066 element_info[element].push_delay_fixed = 2;
3067 element_info[element].push_delay_random = 8;
3070 level->file_has_custom_elements = TRUE;
3075 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3077 int num_changed_custom_elements = getFile16BitBE(file);
3078 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3081 if (chunk_size_expected != chunk_size)
3083 ReadUnusedBytesFromFile(file, chunk_size - 2);
3084 return chunk_size_expected;
3087 for (i = 0; i < num_changed_custom_elements; i++)
3089 int element = getMappedElement(getFile16BitBE(file));
3090 int custom_target_element = getMappedElement(getFile16BitBE(file));
3092 if (IS_CUSTOM_ELEMENT(element))
3093 element_info[element].change->target_element = custom_target_element;
3095 Warn("invalid custom element number %d", element);
3098 level->file_has_custom_elements = TRUE;
3103 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3105 int num_changed_custom_elements = getFile16BitBE(file);
3106 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3109 if (chunk_size_expected != chunk_size)
3111 ReadUnusedBytesFromFile(file, chunk_size - 2);
3112 return chunk_size_expected;
3115 for (i = 0; i < num_changed_custom_elements; i++)
3117 int element = getMappedElement(getFile16BitBE(file));
3118 struct ElementInfo *ei = &element_info[element];
3119 unsigned int event_bits;
3121 if (!IS_CUSTOM_ELEMENT(element))
3123 Warn("invalid custom element number %d", element);
3125 element = EL_INTERNAL_DUMMY;
3128 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3129 ei->description[j] = getFile8Bit(file);
3130 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3132 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3134 // some free bytes for future properties and padding
3135 ReadUnusedBytesFromFile(file, 7);
3137 ei->use_gfx_element = getFile8Bit(file);
3138 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3140 ei->collect_score_initial = getFile8Bit(file);
3141 ei->collect_count_initial = getFile8Bit(file);
3143 ei->push_delay_fixed = getFile16BitBE(file);
3144 ei->push_delay_random = getFile16BitBE(file);
3145 ei->move_delay_fixed = getFile16BitBE(file);
3146 ei->move_delay_random = getFile16BitBE(file);
3148 ei->move_pattern = getFile16BitBE(file);
3149 ei->move_direction_initial = getFile8Bit(file);
3150 ei->move_stepsize = getFile8Bit(file);
3152 for (y = 0; y < 3; y++)
3153 for (x = 0; x < 3; x++)
3154 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3156 // bits 0 - 31 of "has_event[]"
3157 event_bits = getFile32BitBE(file);
3158 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3159 if (event_bits & (1u << j))
3160 ei->change->has_event[j] = TRUE;
3162 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3164 ei->change->delay_fixed = getFile16BitBE(file);
3165 ei->change->delay_random = getFile16BitBE(file);
3166 ei->change->delay_frames = getFile16BitBE(file);
3168 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3170 ei->change->explode = getFile8Bit(file);
3171 ei->change->use_target_content = getFile8Bit(file);
3172 ei->change->only_if_complete = getFile8Bit(file);
3173 ei->change->use_random_replace = getFile8Bit(file);
3175 ei->change->random_percentage = getFile8Bit(file);
3176 ei->change->replace_when = getFile8Bit(file);
3178 for (y = 0; y < 3; y++)
3179 for (x = 0; x < 3; x++)
3180 ei->change->target_content.e[x][y] =
3181 getMappedElement(getFile16BitBE(file));
3183 ei->slippery_type = getFile8Bit(file);
3185 // some free bytes for future properties and padding
3186 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3188 // mark that this custom element has been modified
3189 ei->modified_settings = TRUE;
3192 level->file_has_custom_elements = TRUE;
3197 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3199 struct ElementInfo *ei;
3200 int chunk_size_expected;
3204 // ---------- custom element base property values (96 bytes) ----------------
3206 element = getMappedElement(getFile16BitBE(file));
3208 if (!IS_CUSTOM_ELEMENT(element))
3210 Warn("invalid custom element number %d", element);
3212 ReadUnusedBytesFromFile(file, chunk_size - 2);
3217 ei = &element_info[element];
3219 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3220 ei->description[i] = getFile8Bit(file);
3221 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3223 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3225 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3227 ei->num_change_pages = getFile8Bit(file);
3229 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3230 if (chunk_size_expected != chunk_size)
3232 ReadUnusedBytesFromFile(file, chunk_size - 43);
3233 return chunk_size_expected;
3236 ei->ce_value_fixed_initial = getFile16BitBE(file);
3237 ei->ce_value_random_initial = getFile16BitBE(file);
3238 ei->use_last_ce_value = getFile8Bit(file);
3240 ei->use_gfx_element = getFile8Bit(file);
3241 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3243 ei->collect_score_initial = getFile8Bit(file);
3244 ei->collect_count_initial = getFile8Bit(file);
3246 ei->drop_delay_fixed = getFile8Bit(file);
3247 ei->push_delay_fixed = getFile8Bit(file);
3248 ei->drop_delay_random = getFile8Bit(file);
3249 ei->push_delay_random = getFile8Bit(file);
3250 ei->move_delay_fixed = getFile16BitBE(file);
3251 ei->move_delay_random = getFile16BitBE(file);
3253 // bits 0 - 15 of "move_pattern" ...
3254 ei->move_pattern = getFile16BitBE(file);
3255 ei->move_direction_initial = getFile8Bit(file);
3256 ei->move_stepsize = getFile8Bit(file);
3258 ei->slippery_type = getFile8Bit(file);
3260 for (y = 0; y < 3; y++)
3261 for (x = 0; x < 3; x++)
3262 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3264 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3265 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3266 ei->move_leave_type = getFile8Bit(file);
3268 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3269 ei->move_pattern |= (getFile16BitBE(file) << 16);
3271 ei->access_direction = getFile8Bit(file);
3273 ei->explosion_delay = getFile8Bit(file);
3274 ei->ignition_delay = getFile8Bit(file);
3275 ei->explosion_type = getFile8Bit(file);
3277 // some free bytes for future custom property values and padding
3278 ReadUnusedBytesFromFile(file, 1);
3280 // ---------- change page property values (48 bytes) ------------------------
3282 setElementChangePages(ei, ei->num_change_pages);
3284 for (i = 0; i < ei->num_change_pages; i++)
3286 struct ElementChangeInfo *change = &ei->change_page[i];
3287 unsigned int event_bits;
3289 // always start with reliable default values
3290 setElementChangeInfoToDefaults(change);
3292 // bits 0 - 31 of "has_event[]" ...
3293 event_bits = getFile32BitBE(file);
3294 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3295 if (event_bits & (1u << j))
3296 change->has_event[j] = TRUE;
3298 change->target_element = getMappedElement(getFile16BitBE(file));
3300 change->delay_fixed = getFile16BitBE(file);
3301 change->delay_random = getFile16BitBE(file);
3302 change->delay_frames = getFile16BitBE(file);
3304 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3306 change->explode = getFile8Bit(file);
3307 change->use_target_content = getFile8Bit(file);
3308 change->only_if_complete = getFile8Bit(file);
3309 change->use_random_replace = getFile8Bit(file);
3311 change->random_percentage = getFile8Bit(file);
3312 change->replace_when = getFile8Bit(file);
3314 for (y = 0; y < 3; y++)
3315 for (x = 0; x < 3; x++)
3316 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3318 change->can_change = getFile8Bit(file);
3320 change->trigger_side = getFile8Bit(file);
3322 change->trigger_player = getFile8Bit(file);
3323 change->trigger_page = getFile8Bit(file);
3325 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3326 CH_PAGE_ANY : (1 << change->trigger_page));
3328 change->has_action = getFile8Bit(file);
3329 change->action_type = getFile8Bit(file);
3330 change->action_mode = getFile8Bit(file);
3331 change->action_arg = getFile16BitBE(file);
3333 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3334 event_bits = getFile8Bit(file);
3335 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3336 if (event_bits & (1u << (j - 32)))
3337 change->has_event[j] = TRUE;
3340 // mark this custom element as modified
3341 ei->modified_settings = TRUE;
3343 level->file_has_custom_elements = TRUE;
3348 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3350 struct ElementInfo *ei;
3351 struct ElementGroupInfo *group;
3355 element = getMappedElement(getFile16BitBE(file));
3357 if (!IS_GROUP_ELEMENT(element))
3359 Warn("invalid group element number %d", element);
3361 ReadUnusedBytesFromFile(file, chunk_size - 2);
3366 ei = &element_info[element];
3368 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3369 ei->description[i] = getFile8Bit(file);
3370 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3372 group = element_info[element].group;
3374 group->num_elements = getFile8Bit(file);
3376 ei->use_gfx_element = getFile8Bit(file);
3377 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3379 group->choice_mode = getFile8Bit(file);
3381 // some free bytes for future values and padding
3382 ReadUnusedBytesFromFile(file, 3);
3384 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3385 group->element[i] = getMappedElement(getFile16BitBE(file));
3387 // mark this group element as modified
3388 element_info[element].modified_settings = TRUE;
3390 level->file_has_custom_elements = TRUE;
3395 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3396 int element, int real_element)
3398 int micro_chunk_size = 0;
3399 int conf_type = getFile8Bit(file);
3400 int byte_mask = conf_type & CONF_MASK_BYTES;
3401 boolean element_found = FALSE;
3404 micro_chunk_size += 1;
3406 if (byte_mask == CONF_MASK_MULTI_BYTES)
3408 int num_bytes = getFile16BitBE(file);
3409 byte *buffer = checked_malloc(num_bytes);
3411 ReadBytesFromFile(file, buffer, num_bytes);
3413 for (i = 0; conf[i].data_type != -1; i++)
3415 if (conf[i].element == element &&
3416 conf[i].conf_type == conf_type)
3418 int data_type = conf[i].data_type;
3419 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3420 int max_num_entities = conf[i].max_num_entities;
3422 if (num_entities > max_num_entities)
3424 Warn("truncating number of entities for element %d from %d to %d",
3425 element, num_entities, max_num_entities);
3427 num_entities = max_num_entities;
3430 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3431 data_type == TYPE_CONTENT_LIST))
3433 // for element and content lists, zero entities are not allowed
3434 Warn("found empty list of entities for element %d", element);
3436 // do not set "num_entities" here to prevent reading behind buffer
3438 *(int *)(conf[i].num_entities) = 1; // at least one is required
3442 *(int *)(conf[i].num_entities) = num_entities;
3445 element_found = TRUE;
3447 if (data_type == TYPE_STRING)
3449 char *string = (char *)(conf[i].value);
3452 for (j = 0; j < max_num_entities; j++)
3453 string[j] = (j < num_entities ? buffer[j] : '\0');
3455 else if (data_type == TYPE_ELEMENT_LIST)
3457 int *element_array = (int *)(conf[i].value);
3460 for (j = 0; j < num_entities; j++)
3462 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3464 else if (data_type == TYPE_CONTENT_LIST)
3466 struct Content *content= (struct Content *)(conf[i].value);
3469 for (c = 0; c < num_entities; c++)
3470 for (y = 0; y < 3; y++)
3471 for (x = 0; x < 3; x++)
3472 content[c].e[x][y] =
3473 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3476 element_found = FALSE;
3482 checked_free(buffer);
3484 micro_chunk_size += 2 + num_bytes;
3486 else // constant size configuration data (1, 2 or 4 bytes)
3488 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3489 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3490 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3492 for (i = 0; conf[i].data_type != -1; i++)
3494 if (conf[i].element == element &&
3495 conf[i].conf_type == conf_type)
3497 int data_type = conf[i].data_type;
3499 if (data_type == TYPE_ELEMENT)
3500 value = getMappedElement(value);
3502 if (data_type == TYPE_BOOLEAN)
3503 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3505 *(int *) (conf[i].value) = value;
3507 element_found = TRUE;
3513 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3518 char *error_conf_chunk_bytes =
3519 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3520 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3521 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3522 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3523 int error_element = real_element;
3525 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3526 error_conf_chunk_bytes, error_conf_chunk_token,
3527 error_element, EL_NAME(error_element));
3530 return micro_chunk_size;
3533 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3535 int real_chunk_size = 0;
3537 li = *level; // copy level data into temporary buffer
3539 while (!checkEndOfFile(file))
3541 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3543 if (real_chunk_size >= chunk_size)
3547 *level = li; // copy temporary buffer back to level data
3549 return real_chunk_size;
3552 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3554 int real_chunk_size = 0;
3556 li = *level; // copy level data into temporary buffer
3558 while (!checkEndOfFile(file))
3560 int element = getMappedElement(getFile16BitBE(file));
3562 real_chunk_size += 2;
3563 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3565 if (real_chunk_size >= chunk_size)
3569 *level = li; // copy temporary buffer back to level data
3571 return real_chunk_size;
3574 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3576 int real_chunk_size = 0;
3578 li = *level; // copy level data into temporary buffer
3580 while (!checkEndOfFile(file))
3582 int element = getMappedElement(getFile16BitBE(file));
3584 real_chunk_size += 2;
3585 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3587 if (real_chunk_size >= chunk_size)
3591 *level = li; // copy temporary buffer back to level data
3593 return real_chunk_size;
3596 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3598 int element = getMappedElement(getFile16BitBE(file));
3599 int envelope_nr = element - EL_ENVELOPE_1;
3600 int real_chunk_size = 2;
3602 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3604 while (!checkEndOfFile(file))
3606 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3609 if (real_chunk_size >= chunk_size)
3613 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3615 return real_chunk_size;
3618 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3620 int element = getMappedElement(getFile16BitBE(file));
3621 int real_chunk_size = 2;
3622 struct ElementInfo *ei = &element_info[element];
3625 xx_ei = *ei; // copy element data into temporary buffer
3627 xx_ei.num_change_pages = -1;
3629 while (!checkEndOfFile(file))
3631 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3633 if (xx_ei.num_change_pages != -1)
3636 if (real_chunk_size >= chunk_size)
3642 if (ei->num_change_pages == -1)
3644 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3647 ei->num_change_pages = 1;
3649 setElementChangePages(ei, 1);
3650 setElementChangeInfoToDefaults(ei->change);
3652 return real_chunk_size;
3655 // initialize number of change pages stored for this custom element
3656 setElementChangePages(ei, ei->num_change_pages);
3657 for (i = 0; i < ei->num_change_pages; i++)
3658 setElementChangeInfoToDefaults(&ei->change_page[i]);
3660 // start with reading properties for the first change page
3661 xx_current_change_page = 0;
3663 while (!checkEndOfFile(file))
3665 // level file might contain invalid change page number
3666 if (xx_current_change_page >= ei->num_change_pages)
3669 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3671 xx_change = *change; // copy change data into temporary buffer
3673 resetEventBits(); // reset bits; change page might have changed
3675 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3678 *change = xx_change;
3680 setEventFlagsFromEventBits(change);
3682 if (real_chunk_size >= chunk_size)
3686 level->file_has_custom_elements = TRUE;
3688 return real_chunk_size;
3691 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3693 int element = getMappedElement(getFile16BitBE(file));
3694 int real_chunk_size = 2;
3695 struct ElementInfo *ei = &element_info[element];
3696 struct ElementGroupInfo *group = ei->group;
3701 xx_ei = *ei; // copy element data into temporary buffer
3702 xx_group = *group; // copy group data into temporary buffer
3704 while (!checkEndOfFile(file))
3706 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3709 if (real_chunk_size >= chunk_size)
3716 level->file_has_custom_elements = TRUE;
3718 return real_chunk_size;
3721 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3723 int element = getMappedElement(getFile16BitBE(file));
3724 int real_chunk_size = 2;
3725 struct ElementInfo *ei = &element_info[element];
3727 xx_ei = *ei; // copy element data into temporary buffer
3729 while (!checkEndOfFile(file))
3731 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3734 if (real_chunk_size >= chunk_size)
3740 level->file_has_custom_elements = TRUE;
3742 return real_chunk_size;
3745 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3746 struct LevelFileInfo *level_file_info,
3747 boolean level_info_only)
3749 char *filename = level_file_info->filename;
3750 char cookie[MAX_LINE_LEN];
3751 char chunk_name[CHUNK_ID_LEN + 1];
3755 if (!(file = openFile(filename, MODE_READ)))
3757 level->no_valid_file = TRUE;
3758 level->no_level_file = TRUE;
3760 if (level_info_only)
3763 Warn("cannot read level '%s' -- using empty level", filename);
3765 if (!setup.editor.use_template_for_new_levels)
3768 // if level file not found, try to initialize level data from template
3769 filename = getGlobalLevelTemplateFilename();
3771 if (!(file = openFile(filename, MODE_READ)))
3774 // default: for empty levels, use level template for custom elements
3775 level->use_custom_template = TRUE;
3777 level->no_valid_file = FALSE;
3780 getFileChunkBE(file, chunk_name, NULL);
3781 if (strEqual(chunk_name, "RND1"))
3783 getFile32BitBE(file); // not used
3785 getFileChunkBE(file, chunk_name, NULL);
3786 if (!strEqual(chunk_name, "CAVE"))
3788 level->no_valid_file = TRUE;
3790 Warn("unknown format of level file '%s'", filename);
3797 else // check for pre-2.0 file format with cookie string
3799 strcpy(cookie, chunk_name);
3800 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3802 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3803 cookie[strlen(cookie) - 1] = '\0';
3805 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3807 level->no_valid_file = TRUE;
3809 Warn("unknown format of level file '%s'", filename);
3816 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3818 level->no_valid_file = TRUE;
3820 Warn("unsupported version of level file '%s'", filename);
3827 // pre-2.0 level files have no game version, so use file version here
3828 level->game_version = level->file_version;
3831 if (level->file_version < FILE_VERSION_1_2)
3833 // level files from versions before 1.2.0 without chunk structure
3834 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3835 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3843 int (*loader)(File *, int, struct LevelInfo *);
3847 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3848 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3849 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3850 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3851 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3852 { "INFO", -1, LoadLevel_INFO },
3853 { "BODY", -1, LoadLevel_BODY },
3854 { "CONT", -1, LoadLevel_CONT },
3855 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3856 { "CNT3", -1, LoadLevel_CNT3 },
3857 { "CUS1", -1, LoadLevel_CUS1 },
3858 { "CUS2", -1, LoadLevel_CUS2 },
3859 { "CUS3", -1, LoadLevel_CUS3 },
3860 { "CUS4", -1, LoadLevel_CUS4 },
3861 { "GRP1", -1, LoadLevel_GRP1 },
3862 { "CONF", -1, LoadLevel_CONF },
3863 { "ELEM", -1, LoadLevel_ELEM },
3864 { "NOTE", -1, LoadLevel_NOTE },
3865 { "CUSX", -1, LoadLevel_CUSX },
3866 { "GRPX", -1, LoadLevel_GRPX },
3867 { "EMPX", -1, LoadLevel_EMPX },
3872 while (getFileChunkBE(file, chunk_name, &chunk_size))
3876 while (chunk_info[i].name != NULL &&
3877 !strEqual(chunk_name, chunk_info[i].name))
3880 if (chunk_info[i].name == NULL)
3882 Warn("unknown chunk '%s' in level file '%s'",
3883 chunk_name, filename);
3885 ReadUnusedBytesFromFile(file, chunk_size);
3887 else if (chunk_info[i].size != -1 &&
3888 chunk_info[i].size != chunk_size)
3890 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3891 chunk_size, chunk_name, filename);
3893 ReadUnusedBytesFromFile(file, chunk_size);
3897 // call function to load this level chunk
3898 int chunk_size_expected =
3899 (chunk_info[i].loader)(file, chunk_size, level);
3901 if (chunk_size_expected < 0)
3903 Warn("error reading chunk '%s' in level file '%s'",
3904 chunk_name, filename);
3909 // the size of some chunks cannot be checked before reading other
3910 // chunks first (like "HEAD" and "BODY") that contain some header
3911 // information, so check them here
3912 if (chunk_size_expected != chunk_size)
3914 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3915 chunk_size, chunk_name, filename);
3927 // ----------------------------------------------------------------------------
3928 // functions for loading BD level
3929 // ----------------------------------------------------------------------------
3931 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3933 struct LevelInfo_BD *level_bd = level->native_bd_level;
3934 GdCave *cave = NULL; // will be changed below
3935 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3936 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3939 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3941 // cave and map newly allocated when set to defaults above
3942 cave = level_bd->cave;
3945 cave->intermission = level->bd_intermission;
3948 cave->level_time[0] = level->time;
3949 cave->level_diamonds[0] = level->gems_needed;
3952 cave->scheduling = level->bd_scheduling_type;
3953 cave->pal_timing = level->bd_pal_timing;
3954 cave->level_speed[0] = level->bd_cycle_delay_ms;
3955 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
3956 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
3957 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
3960 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
3961 cave->diamond_value = level->score[SC_EMERALD];
3962 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3964 // compatibility settings
3965 cave->lineshift = level->bd_line_shifting_borders;
3966 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3967 cave->short_explosions = level->bd_short_explosions;
3968 cave->gravity_affects_all = level->bd_gravity_affects_all;
3970 // player properties
3971 cave->diagonal_movements = level->bd_diagonal_movements;
3972 cave->active_is_first_found = level->bd_topmost_player_active;
3973 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
3974 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
3975 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
3976 cave->snap_element = map_element_RND_to_BD(level->bd_snap_element);
3978 // element properties
3979 cave->level_bonus_time[0] = level->bd_clock_extra_time;
3980 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
3981 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
3982 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
3983 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
3984 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
3985 cave->level_magic_wall_time[0] = level->time_magic_wall;
3986 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
3987 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
3988 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
3989 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
3990 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
3991 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
3992 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
3993 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
3994 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
3995 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
3996 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
3997 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
3998 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4000 cave->amoeba_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
4001 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
4002 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
4003 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
4004 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
4005 cave->amoeba_2_looks_like = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
4007 cave->slime_predictable = level->bd_slime_is_predictable;
4008 cave->slime_correct_random = level->bd_slime_correct_random;
4009 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4010 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4011 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4012 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4014 cave->acid_eats_this = map_element_RND_to_BD(level->bd_acid_eats_element);
4015 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4016 cave->acid_turns_to = map_element_RND_to_BD(level->bd_acid_turns_to_element);
4019 strncpy(cave->name, level->name, sizeof(GdString));
4020 cave->name[sizeof(GdString) - 1] = '\0';
4022 // playfield elements
4023 for (x = 0; x < cave->w; x++)
4024 for (y = 0; y < cave->h; y++)
4025 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
4028 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4030 struct LevelInfo_BD *level_bd = level->native_bd_level;
4031 GdCave *cave = level_bd->cave;
4032 int bd_level_nr = level_bd->level_nr;
4035 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4036 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4039 level->bd_intermission = cave->intermission;
4042 level->time = cave->level_time[bd_level_nr];
4043 level->gems_needed = cave->level_diamonds[bd_level_nr];
4046 level->bd_scheduling_type = cave->scheduling;
4047 level->bd_pal_timing = cave->pal_timing;
4048 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4049 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4050 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4051 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4054 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4055 level->score[SC_EMERALD] = cave->diamond_value;
4056 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4058 // compatibility settings
4059 level->bd_line_shifting_borders = cave->lineshift;
4060 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4061 level->bd_short_explosions = cave->short_explosions;
4062 level->bd_gravity_affects_all = cave->gravity_affects_all;
4064 // player properties
4065 level->bd_diagonal_movements = cave->diagonal_movements;
4066 level->bd_topmost_player_active = cave->active_is_first_found;
4067 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4068 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4069 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4070 level->bd_snap_element = map_element_BD_to_RND(cave->snap_element);
4072 // element properties
4073 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4074 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4075 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4076 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4077 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4078 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4079 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4080 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4081 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4082 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4083 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4084 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4085 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4086 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4087 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4088 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4089 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4090 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4091 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4092 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4094 level->bd_amoeba_content_too_big = map_element_BD_to_RND(cave->amoeba_too_big_effect);
4095 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4096 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4097 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4098 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4099 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4101 level->bd_slime_is_predictable = cave->slime_predictable;
4102 level->bd_slime_correct_random = cave->slime_correct_random;
4103 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4104 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4105 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4106 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4108 level->bd_acid_eats_element = map_element_BD_to_RND(cave->acid_eats_this);
4109 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4110 level->bd_acid_turns_to_element = map_element_BD_to_RND(cave->acid_turns_to);
4113 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4115 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4116 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4118 // playfield elements
4119 for (x = 0; x < level->fieldx; x++)
4120 for (y = 0; y < level->fieldy; y++)
4121 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4123 checked_free(cave_name);
4126 static void setTapeInfoToDefaults(void);
4128 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4130 struct LevelInfo_BD *level_bd = level->native_bd_level;
4131 GdCave *cave = level_bd->cave;
4132 GdReplay *replay = level_bd->replay;
4138 // always start with reliable default values
4139 setTapeInfoToDefaults();
4141 tape.level_nr = level_nr; // (currently not used)
4142 tape.random_seed = replay->seed;
4144 TapeSetDateFromIsoDateString(replay->date);
4147 tape.pos[tape.counter].delay = 0;
4149 tape.bd_replay = TRUE;
4151 // all time calculations only used to display approximate tape time
4152 int cave_speed = cave->speed;
4153 int milliseconds_game = 0;
4154 int milliseconds_elapsed = 20;
4156 for (i = 0; i < replay->movements->len; i++)
4158 int replay_action = replay->movements->data[i];
4159 int tape_action = map_action_BD_to_RND(replay_action);
4160 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4161 boolean success = 0;
4165 success = TapeAddAction(action);
4167 milliseconds_game += milliseconds_elapsed;
4169 if (milliseconds_game >= cave_speed)
4171 milliseconds_game -= cave_speed;
4178 tape.pos[tape.counter].delay = 0;
4179 tape.pos[tape.counter].action[0] = 0;
4183 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4189 TapeHaltRecording();
4193 // ----------------------------------------------------------------------------
4194 // functions for loading EM level
4195 // ----------------------------------------------------------------------------
4197 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4199 static int ball_xy[8][2] =
4210 struct LevelInfo_EM *level_em = level->native_em_level;
4211 struct CAVE *cav = level_em->cav;
4214 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4215 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4217 cav->time_seconds = level->time;
4218 cav->gems_needed = level->gems_needed;
4220 cav->emerald_score = level->score[SC_EMERALD];
4221 cav->diamond_score = level->score[SC_DIAMOND];
4222 cav->alien_score = level->score[SC_ROBOT];
4223 cav->tank_score = level->score[SC_SPACESHIP];
4224 cav->bug_score = level->score[SC_BUG];
4225 cav->eater_score = level->score[SC_YAMYAM];
4226 cav->nut_score = level->score[SC_NUT];
4227 cav->dynamite_score = level->score[SC_DYNAMITE];
4228 cav->key_score = level->score[SC_KEY];
4229 cav->exit_score = level->score[SC_TIME_BONUS];
4231 cav->num_eater_arrays = level->num_yamyam_contents;
4233 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4234 for (y = 0; y < 3; y++)
4235 for (x = 0; x < 3; x++)
4236 cav->eater_array[i][y * 3 + x] =
4237 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4239 cav->amoeba_time = level->amoeba_speed;
4240 cav->wonderwall_time = level->time_magic_wall;
4241 cav->wheel_time = level->time_wheel;
4243 cav->android_move_time = level->android_move_time;
4244 cav->android_clone_time = level->android_clone_time;
4245 cav->ball_random = level->ball_random;
4246 cav->ball_active = level->ball_active_initial;
4247 cav->ball_time = level->ball_time;
4248 cav->num_ball_arrays = level->num_ball_contents;
4250 cav->lenses_score = level->lenses_score;
4251 cav->magnify_score = level->magnify_score;
4252 cav->slurp_score = level->slurp_score;
4254 cav->lenses_time = level->lenses_time;
4255 cav->magnify_time = level->magnify_time;
4257 cav->wind_time = 9999;
4258 cav->wind_direction =
4259 map_direction_RND_to_EM(level->wind_direction_initial);
4261 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4262 for (j = 0; j < 8; j++)
4263 cav->ball_array[i][j] =
4264 map_element_RND_to_EM_cave(level->ball_content[i].
4265 e[ball_xy[j][0]][ball_xy[j][1]]);
4267 map_android_clone_elements_RND_to_EM(level);
4269 // first fill the complete playfield with the empty space element
4270 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4271 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4272 cav->cave[x][y] = Cblank;
4274 // then copy the real level contents from level file into the playfield
4275 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4277 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4279 if (level->field[x][y] == EL_AMOEBA_DEAD)
4280 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4282 cav->cave[x][y] = new_element;
4285 for (i = 0; i < MAX_PLAYERS; i++)
4287 cav->player_x[i] = -1;
4288 cav->player_y[i] = -1;
4291 // initialize player positions and delete players from the playfield
4292 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4294 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4296 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4298 cav->player_x[player_nr] = x;
4299 cav->player_y[player_nr] = y;
4301 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4306 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4308 static int ball_xy[8][2] =
4319 struct LevelInfo_EM *level_em = level->native_em_level;
4320 struct CAVE *cav = level_em->cav;
4323 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4324 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4326 level->time = cav->time_seconds;
4327 level->gems_needed = cav->gems_needed;
4329 sprintf(level->name, "Level %d", level->file_info.nr);
4331 level->score[SC_EMERALD] = cav->emerald_score;
4332 level->score[SC_DIAMOND] = cav->diamond_score;
4333 level->score[SC_ROBOT] = cav->alien_score;
4334 level->score[SC_SPACESHIP] = cav->tank_score;
4335 level->score[SC_BUG] = cav->bug_score;
4336 level->score[SC_YAMYAM] = cav->eater_score;
4337 level->score[SC_NUT] = cav->nut_score;
4338 level->score[SC_DYNAMITE] = cav->dynamite_score;
4339 level->score[SC_KEY] = cav->key_score;
4340 level->score[SC_TIME_BONUS] = cav->exit_score;
4342 level->num_yamyam_contents = cav->num_eater_arrays;
4344 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4345 for (y = 0; y < 3; y++)
4346 for (x = 0; x < 3; x++)
4347 level->yamyam_content[i].e[x][y] =
4348 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4350 level->amoeba_speed = cav->amoeba_time;
4351 level->time_magic_wall = cav->wonderwall_time;
4352 level->time_wheel = cav->wheel_time;
4354 level->android_move_time = cav->android_move_time;
4355 level->android_clone_time = cav->android_clone_time;
4356 level->ball_random = cav->ball_random;
4357 level->ball_active_initial = cav->ball_active;
4358 level->ball_time = cav->ball_time;
4359 level->num_ball_contents = cav->num_ball_arrays;
4361 level->lenses_score = cav->lenses_score;
4362 level->magnify_score = cav->magnify_score;
4363 level->slurp_score = cav->slurp_score;
4365 level->lenses_time = cav->lenses_time;
4366 level->magnify_time = cav->magnify_time;
4368 level->wind_direction_initial =
4369 map_direction_EM_to_RND(cav->wind_direction);
4371 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4372 for (j = 0; j < 8; j++)
4373 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4374 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4376 map_android_clone_elements_EM_to_RND(level);
4378 // convert the playfield (some elements need special treatment)
4379 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4381 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4383 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4384 new_element = EL_AMOEBA_DEAD;
4386 level->field[x][y] = new_element;
4389 for (i = 0; i < MAX_PLAYERS; i++)
4391 // in case of all players set to the same field, use the first player
4392 int nr = MAX_PLAYERS - i - 1;
4393 int jx = cav->player_x[nr];
4394 int jy = cav->player_y[nr];
4396 if (jx != -1 && jy != -1)
4397 level->field[jx][jy] = EL_PLAYER_1 + nr;
4400 // time score is counted for each 10 seconds left in Emerald Mine levels
4401 level->time_score_base = 10;
4405 // ----------------------------------------------------------------------------
4406 // functions for loading SP level
4407 // ----------------------------------------------------------------------------
4409 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4411 struct LevelInfo_SP *level_sp = level->native_sp_level;
4412 LevelInfoType *header = &level_sp->header;
4415 level_sp->width = level->fieldx;
4416 level_sp->height = level->fieldy;
4418 for (x = 0; x < level->fieldx; x++)
4419 for (y = 0; y < level->fieldy; y++)
4420 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4422 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4424 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4425 header->LevelTitle[i] = level->name[i];
4426 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4428 header->InfotronsNeeded = level->gems_needed;
4430 header->SpecialPortCount = 0;
4432 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4434 boolean gravity_port_found = FALSE;
4435 boolean gravity_port_valid = FALSE;
4436 int gravity_port_flag;
4437 int gravity_port_base_element;
4438 int element = level->field[x][y];
4440 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4441 element <= EL_SP_GRAVITY_ON_PORT_UP)
4443 gravity_port_found = TRUE;
4444 gravity_port_valid = TRUE;
4445 gravity_port_flag = 1;
4446 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4448 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4449 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4451 gravity_port_found = TRUE;
4452 gravity_port_valid = TRUE;
4453 gravity_port_flag = 0;
4454 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4456 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4457 element <= EL_SP_GRAVITY_PORT_UP)
4459 // change R'n'D style gravity inverting special port to normal port
4460 // (there are no gravity inverting ports in native Supaplex engine)
4462 gravity_port_found = TRUE;
4463 gravity_port_valid = FALSE;
4464 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4467 if (gravity_port_found)
4469 if (gravity_port_valid &&
4470 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4472 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4474 port->PortLocation = (y * level->fieldx + x) * 2;
4475 port->Gravity = gravity_port_flag;
4477 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4479 header->SpecialPortCount++;
4483 // change special gravity port to normal port
4485 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4488 level_sp->playfield[x][y] = element - EL_SP_START;
4493 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4495 struct LevelInfo_SP *level_sp = level->native_sp_level;
4496 LevelInfoType *header = &level_sp->header;
4497 boolean num_invalid_elements = 0;
4500 level->fieldx = level_sp->width;
4501 level->fieldy = level_sp->height;
4503 for (x = 0; x < level->fieldx; x++)
4505 for (y = 0; y < level->fieldy; y++)
4507 int element_old = level_sp->playfield[x][y];
4508 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4510 if (element_new == EL_UNKNOWN)
4512 num_invalid_elements++;
4514 Debug("level:native:SP", "invalid element %d at position %d, %d",
4518 level->field[x][y] = element_new;
4522 if (num_invalid_elements > 0)
4523 Warn("found %d invalid elements%s", num_invalid_elements,
4524 (!options.debug ? " (use '--debug' for more details)" : ""));
4526 for (i = 0; i < MAX_PLAYERS; i++)
4527 level->initial_player_gravity[i] =
4528 (header->InitialGravity == 1 ? TRUE : FALSE);
4530 // skip leading spaces
4531 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4532 if (header->LevelTitle[i] != ' ')
4536 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4537 level->name[j] = header->LevelTitle[i];
4538 level->name[j] = '\0';
4540 // cut trailing spaces
4542 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4543 level->name[j - 1] = '\0';
4545 level->gems_needed = header->InfotronsNeeded;
4547 for (i = 0; i < header->SpecialPortCount; i++)
4549 SpecialPortType *port = &header->SpecialPort[i];
4550 int port_location = port->PortLocation;
4551 int gravity = port->Gravity;
4552 int port_x, port_y, port_element;
4554 port_x = (port_location / 2) % level->fieldx;
4555 port_y = (port_location / 2) / level->fieldx;
4557 if (port_x < 0 || port_x >= level->fieldx ||
4558 port_y < 0 || port_y >= level->fieldy)
4560 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4565 port_element = level->field[port_x][port_y];
4567 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4568 port_element > EL_SP_GRAVITY_PORT_UP)
4570 Warn("no special port at position (%d, %d)", port_x, port_y);
4575 // change previous (wrong) gravity inverting special port to either
4576 // gravity enabling special port or gravity disabling special port
4577 level->field[port_x][port_y] +=
4578 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4579 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4582 // change special gravity ports without database entries to normal ports
4583 for (x = 0; x < level->fieldx; x++)
4584 for (y = 0; y < level->fieldy; y++)
4585 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4586 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4587 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4589 level->time = 0; // no time limit
4590 level->amoeba_speed = 0;
4591 level->time_magic_wall = 0;
4592 level->time_wheel = 0;
4593 level->amoeba_content = EL_EMPTY;
4595 // original Supaplex does not use score values -- rate by playing time
4596 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4597 level->score[i] = 0;
4599 level->rate_time_over_score = TRUE;
4601 // there are no yamyams in supaplex levels
4602 for (i = 0; i < level->num_yamyam_contents; i++)
4603 for (x = 0; x < 3; x++)
4604 for (y = 0; y < 3; y++)
4605 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4608 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4610 struct LevelInfo_SP *level_sp = level->native_sp_level;
4611 struct DemoInfo_SP *demo = &level_sp->demo;
4614 // always start with reliable default values
4615 demo->is_available = FALSE;
4618 if (TAPE_IS_EMPTY(tape))
4621 demo->level_nr = tape.level_nr; // (currently not used)
4623 level_sp->header.DemoRandomSeed = tape.random_seed;
4627 for (i = 0; i < tape.length; i++)
4629 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4630 int demo_repeat = tape.pos[i].delay;
4631 int demo_entries = (demo_repeat + 15) / 16;
4633 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4635 Warn("tape truncated: size exceeds maximum SP demo size %d",
4641 for (j = 0; j < demo_repeat / 16; j++)
4642 demo->data[demo->length++] = 0xf0 | demo_action;
4644 if (demo_repeat % 16)
4645 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4648 demo->is_available = TRUE;
4651 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4653 struct LevelInfo_SP *level_sp = level->native_sp_level;
4654 struct DemoInfo_SP *demo = &level_sp->demo;
4655 char *filename = level->file_info.filename;
4658 // always start with reliable default values
4659 setTapeInfoToDefaults();
4661 if (!demo->is_available)
4664 tape.level_nr = demo->level_nr; // (currently not used)
4665 tape.random_seed = level_sp->header.DemoRandomSeed;
4667 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4670 tape.pos[tape.counter].delay = 0;
4672 for (i = 0; i < demo->length; i++)
4674 int demo_action = demo->data[i] & 0x0f;
4675 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4676 int tape_action = map_key_SP_to_RND(demo_action);
4677 int tape_repeat = demo_repeat + 1;
4678 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4679 boolean success = 0;
4682 for (j = 0; j < tape_repeat; j++)
4683 success = TapeAddAction(action);
4687 Warn("SP demo truncated: size exceeds maximum tape size %d",
4694 TapeHaltRecording();
4698 // ----------------------------------------------------------------------------
4699 // functions for loading MM level
4700 // ----------------------------------------------------------------------------
4702 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4704 struct LevelInfo_MM *level_mm = level->native_mm_level;
4707 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4708 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4710 level_mm->time = level->time;
4711 level_mm->kettles_needed = level->gems_needed;
4712 level_mm->auto_count_kettles = level->auto_count_gems;
4714 level_mm->mm_laser_red = level->mm_laser_red;
4715 level_mm->mm_laser_green = level->mm_laser_green;
4716 level_mm->mm_laser_blue = level->mm_laser_blue;
4718 level_mm->df_laser_red = level->df_laser_red;
4719 level_mm->df_laser_green = level->df_laser_green;
4720 level_mm->df_laser_blue = level->df_laser_blue;
4722 strcpy(level_mm->name, level->name);
4723 strcpy(level_mm->author, level->author);
4725 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4726 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4727 level_mm->score[SC_KEY] = level->score[SC_KEY];
4728 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4729 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4731 level_mm->amoeba_speed = level->amoeba_speed;
4732 level_mm->time_fuse = level->mm_time_fuse;
4733 level_mm->time_bomb = level->mm_time_bomb;
4734 level_mm->time_ball = level->mm_time_ball;
4735 level_mm->time_block = level->mm_time_block;
4737 level_mm->num_ball_contents = level->num_mm_ball_contents;
4738 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4739 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4740 level_mm->explode_ball = level->explode_mm_ball;
4742 for (i = 0; i < level->num_mm_ball_contents; i++)
4743 level_mm->ball_content[i] =
4744 map_element_RND_to_MM(level->mm_ball_content[i]);
4746 for (x = 0; x < level->fieldx; x++)
4747 for (y = 0; y < level->fieldy; y++)
4749 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4752 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4754 struct LevelInfo_MM *level_mm = level->native_mm_level;
4757 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4758 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4760 level->time = level_mm->time;
4761 level->gems_needed = level_mm->kettles_needed;
4762 level->auto_count_gems = level_mm->auto_count_kettles;
4764 level->mm_laser_red = level_mm->mm_laser_red;
4765 level->mm_laser_green = level_mm->mm_laser_green;
4766 level->mm_laser_blue = level_mm->mm_laser_blue;
4768 level->df_laser_red = level_mm->df_laser_red;
4769 level->df_laser_green = level_mm->df_laser_green;
4770 level->df_laser_blue = level_mm->df_laser_blue;
4772 strcpy(level->name, level_mm->name);
4774 // only overwrite author from 'levelinfo.conf' if author defined in level
4775 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4776 strcpy(level->author, level_mm->author);
4778 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4779 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4780 level->score[SC_KEY] = level_mm->score[SC_KEY];
4781 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4782 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4784 level->amoeba_speed = level_mm->amoeba_speed;
4785 level->mm_time_fuse = level_mm->time_fuse;
4786 level->mm_time_bomb = level_mm->time_bomb;
4787 level->mm_time_ball = level_mm->time_ball;
4788 level->mm_time_block = level_mm->time_block;
4790 level->num_mm_ball_contents = level_mm->num_ball_contents;
4791 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4792 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4793 level->explode_mm_ball = level_mm->explode_ball;
4795 for (i = 0; i < level->num_mm_ball_contents; i++)
4796 level->mm_ball_content[i] =
4797 map_element_MM_to_RND(level_mm->ball_content[i]);
4799 for (x = 0; x < level->fieldx; x++)
4800 for (y = 0; y < level->fieldy; y++)
4801 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4805 // ----------------------------------------------------------------------------
4806 // functions for loading DC level
4807 // ----------------------------------------------------------------------------
4809 #define DC_LEVEL_HEADER_SIZE 344
4811 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4814 static int last_data_encoded;
4818 int diff_hi, diff_lo;
4819 int data_hi, data_lo;
4820 unsigned short data_decoded;
4824 last_data_encoded = 0;
4831 diff = data_encoded - last_data_encoded;
4832 diff_hi = diff & ~0xff;
4833 diff_lo = diff & 0xff;
4837 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4838 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4839 data_hi = data_hi & 0xff00;
4841 data_decoded = data_hi | data_lo;
4843 last_data_encoded = data_encoded;
4845 offset1 = (offset1 + 1) % 31;
4846 offset2 = offset2 & 0xff;
4848 return data_decoded;
4851 static int getMappedElement_DC(int element)
4859 // 0x0117 - 0x036e: (?)
4862 // 0x042d - 0x0684: (?)
4878 element = EL_CRYSTAL;
4881 case 0x0e77: // quicksand (boulder)
4882 element = EL_QUICKSAND_FAST_FULL;
4885 case 0x0e99: // slow quicksand (boulder)
4886 element = EL_QUICKSAND_FULL;
4890 element = EL_EM_EXIT_OPEN;
4894 element = EL_EM_EXIT_CLOSED;
4898 element = EL_EM_STEEL_EXIT_OPEN;
4902 element = EL_EM_STEEL_EXIT_CLOSED;
4905 case 0x0f4f: // dynamite (lit 1)
4906 element = EL_EM_DYNAMITE_ACTIVE;
4909 case 0x0f57: // dynamite (lit 2)
4910 element = EL_EM_DYNAMITE_ACTIVE;
4913 case 0x0f5f: // dynamite (lit 3)
4914 element = EL_EM_DYNAMITE_ACTIVE;
4917 case 0x0f67: // dynamite (lit 4)
4918 element = EL_EM_DYNAMITE_ACTIVE;
4925 element = EL_AMOEBA_WET;
4929 element = EL_AMOEBA_DROP;
4933 element = EL_DC_MAGIC_WALL;
4937 element = EL_SPACESHIP_UP;
4941 element = EL_SPACESHIP_DOWN;
4945 element = EL_SPACESHIP_LEFT;
4949 element = EL_SPACESHIP_RIGHT;
4953 element = EL_BUG_UP;
4957 element = EL_BUG_DOWN;
4961 element = EL_BUG_LEFT;
4965 element = EL_BUG_RIGHT;
4969 element = EL_MOLE_UP;
4973 element = EL_MOLE_DOWN;
4977 element = EL_MOLE_LEFT;
4981 element = EL_MOLE_RIGHT;
4989 element = EL_YAMYAM_UP;
4993 element = EL_SWITCHGATE_OPEN;
4997 element = EL_SWITCHGATE_CLOSED;
5001 element = EL_DC_SWITCHGATE_SWITCH_UP;
5005 element = EL_TIMEGATE_CLOSED;
5008 case 0x144c: // conveyor belt switch (green)
5009 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5012 case 0x144f: // conveyor belt switch (red)
5013 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5016 case 0x1452: // conveyor belt switch (blue)
5017 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5021 element = EL_CONVEYOR_BELT_3_MIDDLE;
5025 element = EL_CONVEYOR_BELT_3_LEFT;
5029 element = EL_CONVEYOR_BELT_3_RIGHT;
5033 element = EL_CONVEYOR_BELT_1_MIDDLE;
5037 element = EL_CONVEYOR_BELT_1_LEFT;
5041 element = EL_CONVEYOR_BELT_1_RIGHT;
5045 element = EL_CONVEYOR_BELT_4_MIDDLE;
5049 element = EL_CONVEYOR_BELT_4_LEFT;
5053 element = EL_CONVEYOR_BELT_4_RIGHT;
5057 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5061 element = EL_EXPANDABLE_WALL_VERTICAL;
5065 element = EL_EXPANDABLE_WALL_ANY;
5068 case 0x14ce: // growing steel wall (left/right)
5069 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5072 case 0x14df: // growing steel wall (up/down)
5073 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5076 case 0x14e8: // growing steel wall (up/down/left/right)
5077 element = EL_EXPANDABLE_STEELWALL_ANY;
5081 element = EL_SHIELD_DEADLY;
5085 element = EL_EXTRA_TIME;
5093 element = EL_EMPTY_SPACE;
5096 case 0x1578: // quicksand (empty)
5097 element = EL_QUICKSAND_FAST_EMPTY;
5100 case 0x1579: // slow quicksand (empty)
5101 element = EL_QUICKSAND_EMPTY;
5111 element = EL_EM_DYNAMITE;
5114 case 0x15a1: // key (red)
5115 element = EL_EM_KEY_1;
5118 case 0x15a2: // key (yellow)
5119 element = EL_EM_KEY_2;
5122 case 0x15a3: // key (blue)
5123 element = EL_EM_KEY_4;
5126 case 0x15a4: // key (green)
5127 element = EL_EM_KEY_3;
5130 case 0x15a5: // key (white)
5131 element = EL_DC_KEY_WHITE;
5135 element = EL_WALL_SLIPPERY;
5142 case 0x15a8: // wall (not round)
5146 case 0x15a9: // (blue)
5147 element = EL_CHAR_A;
5150 case 0x15aa: // (blue)
5151 element = EL_CHAR_B;
5154 case 0x15ab: // (blue)
5155 element = EL_CHAR_C;
5158 case 0x15ac: // (blue)
5159 element = EL_CHAR_D;
5162 case 0x15ad: // (blue)
5163 element = EL_CHAR_E;
5166 case 0x15ae: // (blue)
5167 element = EL_CHAR_F;
5170 case 0x15af: // (blue)
5171 element = EL_CHAR_G;
5174 case 0x15b0: // (blue)
5175 element = EL_CHAR_H;
5178 case 0x15b1: // (blue)
5179 element = EL_CHAR_I;
5182 case 0x15b2: // (blue)
5183 element = EL_CHAR_J;
5186 case 0x15b3: // (blue)
5187 element = EL_CHAR_K;
5190 case 0x15b4: // (blue)
5191 element = EL_CHAR_L;
5194 case 0x15b5: // (blue)
5195 element = EL_CHAR_M;
5198 case 0x15b6: // (blue)
5199 element = EL_CHAR_N;
5202 case 0x15b7: // (blue)
5203 element = EL_CHAR_O;
5206 case 0x15b8: // (blue)
5207 element = EL_CHAR_P;
5210 case 0x15b9: // (blue)
5211 element = EL_CHAR_Q;
5214 case 0x15ba: // (blue)
5215 element = EL_CHAR_R;
5218 case 0x15bb: // (blue)
5219 element = EL_CHAR_S;
5222 case 0x15bc: // (blue)
5223 element = EL_CHAR_T;
5226 case 0x15bd: // (blue)
5227 element = EL_CHAR_U;
5230 case 0x15be: // (blue)
5231 element = EL_CHAR_V;
5234 case 0x15bf: // (blue)
5235 element = EL_CHAR_W;
5238 case 0x15c0: // (blue)
5239 element = EL_CHAR_X;
5242 case 0x15c1: // (blue)
5243 element = EL_CHAR_Y;
5246 case 0x15c2: // (blue)
5247 element = EL_CHAR_Z;
5250 case 0x15c3: // (blue)
5251 element = EL_CHAR_AUMLAUT;
5254 case 0x15c4: // (blue)
5255 element = EL_CHAR_OUMLAUT;
5258 case 0x15c5: // (blue)
5259 element = EL_CHAR_UUMLAUT;
5262 case 0x15c6: // (blue)
5263 element = EL_CHAR_0;
5266 case 0x15c7: // (blue)
5267 element = EL_CHAR_1;
5270 case 0x15c8: // (blue)
5271 element = EL_CHAR_2;
5274 case 0x15c9: // (blue)
5275 element = EL_CHAR_3;
5278 case 0x15ca: // (blue)
5279 element = EL_CHAR_4;
5282 case 0x15cb: // (blue)
5283 element = EL_CHAR_5;
5286 case 0x15cc: // (blue)
5287 element = EL_CHAR_6;
5290 case 0x15cd: // (blue)
5291 element = EL_CHAR_7;
5294 case 0x15ce: // (blue)
5295 element = EL_CHAR_8;
5298 case 0x15cf: // (blue)
5299 element = EL_CHAR_9;
5302 case 0x15d0: // (blue)
5303 element = EL_CHAR_PERIOD;
5306 case 0x15d1: // (blue)
5307 element = EL_CHAR_EXCLAM;
5310 case 0x15d2: // (blue)
5311 element = EL_CHAR_COLON;
5314 case 0x15d3: // (blue)
5315 element = EL_CHAR_LESS;
5318 case 0x15d4: // (blue)
5319 element = EL_CHAR_GREATER;
5322 case 0x15d5: // (blue)
5323 element = EL_CHAR_QUESTION;
5326 case 0x15d6: // (blue)
5327 element = EL_CHAR_COPYRIGHT;
5330 case 0x15d7: // (blue)
5331 element = EL_CHAR_UP;
5334 case 0x15d8: // (blue)
5335 element = EL_CHAR_DOWN;
5338 case 0x15d9: // (blue)
5339 element = EL_CHAR_BUTTON;
5342 case 0x15da: // (blue)
5343 element = EL_CHAR_PLUS;
5346 case 0x15db: // (blue)
5347 element = EL_CHAR_MINUS;
5350 case 0x15dc: // (blue)
5351 element = EL_CHAR_APOSTROPHE;
5354 case 0x15dd: // (blue)
5355 element = EL_CHAR_PARENLEFT;
5358 case 0x15de: // (blue)
5359 element = EL_CHAR_PARENRIGHT;
5362 case 0x15df: // (green)
5363 element = EL_CHAR_A;
5366 case 0x15e0: // (green)
5367 element = EL_CHAR_B;
5370 case 0x15e1: // (green)
5371 element = EL_CHAR_C;
5374 case 0x15e2: // (green)
5375 element = EL_CHAR_D;
5378 case 0x15e3: // (green)
5379 element = EL_CHAR_E;
5382 case 0x15e4: // (green)
5383 element = EL_CHAR_F;
5386 case 0x15e5: // (green)
5387 element = EL_CHAR_G;
5390 case 0x15e6: // (green)
5391 element = EL_CHAR_H;
5394 case 0x15e7: // (green)
5395 element = EL_CHAR_I;
5398 case 0x15e8: // (green)
5399 element = EL_CHAR_J;
5402 case 0x15e9: // (green)
5403 element = EL_CHAR_K;
5406 case 0x15ea: // (green)
5407 element = EL_CHAR_L;
5410 case 0x15eb: // (green)
5411 element = EL_CHAR_M;
5414 case 0x15ec: // (green)
5415 element = EL_CHAR_N;
5418 case 0x15ed: // (green)
5419 element = EL_CHAR_O;
5422 case 0x15ee: // (green)
5423 element = EL_CHAR_P;
5426 case 0x15ef: // (green)
5427 element = EL_CHAR_Q;
5430 case 0x15f0: // (green)
5431 element = EL_CHAR_R;
5434 case 0x15f1: // (green)
5435 element = EL_CHAR_S;
5438 case 0x15f2: // (green)
5439 element = EL_CHAR_T;
5442 case 0x15f3: // (green)
5443 element = EL_CHAR_U;
5446 case 0x15f4: // (green)
5447 element = EL_CHAR_V;
5450 case 0x15f5: // (green)
5451 element = EL_CHAR_W;
5454 case 0x15f6: // (green)
5455 element = EL_CHAR_X;
5458 case 0x15f7: // (green)
5459 element = EL_CHAR_Y;
5462 case 0x15f8: // (green)
5463 element = EL_CHAR_Z;
5466 case 0x15f9: // (green)
5467 element = EL_CHAR_AUMLAUT;
5470 case 0x15fa: // (green)
5471 element = EL_CHAR_OUMLAUT;
5474 case 0x15fb: // (green)
5475 element = EL_CHAR_UUMLAUT;
5478 case 0x15fc: // (green)
5479 element = EL_CHAR_0;
5482 case 0x15fd: // (green)
5483 element = EL_CHAR_1;
5486 case 0x15fe: // (green)
5487 element = EL_CHAR_2;
5490 case 0x15ff: // (green)
5491 element = EL_CHAR_3;
5494 case 0x1600: // (green)
5495 element = EL_CHAR_4;
5498 case 0x1601: // (green)
5499 element = EL_CHAR_5;
5502 case 0x1602: // (green)
5503 element = EL_CHAR_6;
5506 case 0x1603: // (green)
5507 element = EL_CHAR_7;
5510 case 0x1604: // (green)
5511 element = EL_CHAR_8;
5514 case 0x1605: // (green)
5515 element = EL_CHAR_9;
5518 case 0x1606: // (green)
5519 element = EL_CHAR_PERIOD;
5522 case 0x1607: // (green)
5523 element = EL_CHAR_EXCLAM;
5526 case 0x1608: // (green)
5527 element = EL_CHAR_COLON;
5530 case 0x1609: // (green)
5531 element = EL_CHAR_LESS;
5534 case 0x160a: // (green)
5535 element = EL_CHAR_GREATER;
5538 case 0x160b: // (green)
5539 element = EL_CHAR_QUESTION;
5542 case 0x160c: // (green)
5543 element = EL_CHAR_COPYRIGHT;
5546 case 0x160d: // (green)
5547 element = EL_CHAR_UP;
5550 case 0x160e: // (green)
5551 element = EL_CHAR_DOWN;
5554 case 0x160f: // (green)
5555 element = EL_CHAR_BUTTON;
5558 case 0x1610: // (green)
5559 element = EL_CHAR_PLUS;
5562 case 0x1611: // (green)
5563 element = EL_CHAR_MINUS;
5566 case 0x1612: // (green)
5567 element = EL_CHAR_APOSTROPHE;
5570 case 0x1613: // (green)
5571 element = EL_CHAR_PARENLEFT;
5574 case 0x1614: // (green)
5575 element = EL_CHAR_PARENRIGHT;
5578 case 0x1615: // (blue steel)
5579 element = EL_STEEL_CHAR_A;
5582 case 0x1616: // (blue steel)
5583 element = EL_STEEL_CHAR_B;
5586 case 0x1617: // (blue steel)
5587 element = EL_STEEL_CHAR_C;
5590 case 0x1618: // (blue steel)
5591 element = EL_STEEL_CHAR_D;
5594 case 0x1619: // (blue steel)
5595 element = EL_STEEL_CHAR_E;
5598 case 0x161a: // (blue steel)
5599 element = EL_STEEL_CHAR_F;
5602 case 0x161b: // (blue steel)
5603 element = EL_STEEL_CHAR_G;
5606 case 0x161c: // (blue steel)
5607 element = EL_STEEL_CHAR_H;
5610 case 0x161d: // (blue steel)
5611 element = EL_STEEL_CHAR_I;
5614 case 0x161e: // (blue steel)
5615 element = EL_STEEL_CHAR_J;
5618 case 0x161f: // (blue steel)
5619 element = EL_STEEL_CHAR_K;
5622 case 0x1620: // (blue steel)
5623 element = EL_STEEL_CHAR_L;
5626 case 0x1621: // (blue steel)
5627 element = EL_STEEL_CHAR_M;
5630 case 0x1622: // (blue steel)
5631 element = EL_STEEL_CHAR_N;
5634 case 0x1623: // (blue steel)
5635 element = EL_STEEL_CHAR_O;
5638 case 0x1624: // (blue steel)
5639 element = EL_STEEL_CHAR_P;
5642 case 0x1625: // (blue steel)
5643 element = EL_STEEL_CHAR_Q;
5646 case 0x1626: // (blue steel)
5647 element = EL_STEEL_CHAR_R;
5650 case 0x1627: // (blue steel)
5651 element = EL_STEEL_CHAR_S;
5654 case 0x1628: // (blue steel)
5655 element = EL_STEEL_CHAR_T;
5658 case 0x1629: // (blue steel)
5659 element = EL_STEEL_CHAR_U;
5662 case 0x162a: // (blue steel)
5663 element = EL_STEEL_CHAR_V;
5666 case 0x162b: // (blue steel)
5667 element = EL_STEEL_CHAR_W;
5670 case 0x162c: // (blue steel)
5671 element = EL_STEEL_CHAR_X;
5674 case 0x162d: // (blue steel)
5675 element = EL_STEEL_CHAR_Y;
5678 case 0x162e: // (blue steel)
5679 element = EL_STEEL_CHAR_Z;
5682 case 0x162f: // (blue steel)
5683 element = EL_STEEL_CHAR_AUMLAUT;
5686 case 0x1630: // (blue steel)
5687 element = EL_STEEL_CHAR_OUMLAUT;
5690 case 0x1631: // (blue steel)
5691 element = EL_STEEL_CHAR_UUMLAUT;
5694 case 0x1632: // (blue steel)
5695 element = EL_STEEL_CHAR_0;
5698 case 0x1633: // (blue steel)
5699 element = EL_STEEL_CHAR_1;
5702 case 0x1634: // (blue steel)
5703 element = EL_STEEL_CHAR_2;
5706 case 0x1635: // (blue steel)
5707 element = EL_STEEL_CHAR_3;
5710 case 0x1636: // (blue steel)
5711 element = EL_STEEL_CHAR_4;
5714 case 0x1637: // (blue steel)
5715 element = EL_STEEL_CHAR_5;
5718 case 0x1638: // (blue steel)
5719 element = EL_STEEL_CHAR_6;
5722 case 0x1639: // (blue steel)
5723 element = EL_STEEL_CHAR_7;
5726 case 0x163a: // (blue steel)
5727 element = EL_STEEL_CHAR_8;
5730 case 0x163b: // (blue steel)
5731 element = EL_STEEL_CHAR_9;
5734 case 0x163c: // (blue steel)
5735 element = EL_STEEL_CHAR_PERIOD;
5738 case 0x163d: // (blue steel)
5739 element = EL_STEEL_CHAR_EXCLAM;
5742 case 0x163e: // (blue steel)
5743 element = EL_STEEL_CHAR_COLON;
5746 case 0x163f: // (blue steel)
5747 element = EL_STEEL_CHAR_LESS;
5750 case 0x1640: // (blue steel)
5751 element = EL_STEEL_CHAR_GREATER;
5754 case 0x1641: // (blue steel)
5755 element = EL_STEEL_CHAR_QUESTION;
5758 case 0x1642: // (blue steel)
5759 element = EL_STEEL_CHAR_COPYRIGHT;
5762 case 0x1643: // (blue steel)
5763 element = EL_STEEL_CHAR_UP;
5766 case 0x1644: // (blue steel)
5767 element = EL_STEEL_CHAR_DOWN;
5770 case 0x1645: // (blue steel)
5771 element = EL_STEEL_CHAR_BUTTON;
5774 case 0x1646: // (blue steel)
5775 element = EL_STEEL_CHAR_PLUS;
5778 case 0x1647: // (blue steel)
5779 element = EL_STEEL_CHAR_MINUS;
5782 case 0x1648: // (blue steel)
5783 element = EL_STEEL_CHAR_APOSTROPHE;
5786 case 0x1649: // (blue steel)
5787 element = EL_STEEL_CHAR_PARENLEFT;
5790 case 0x164a: // (blue steel)
5791 element = EL_STEEL_CHAR_PARENRIGHT;
5794 case 0x164b: // (green steel)
5795 element = EL_STEEL_CHAR_A;
5798 case 0x164c: // (green steel)
5799 element = EL_STEEL_CHAR_B;
5802 case 0x164d: // (green steel)
5803 element = EL_STEEL_CHAR_C;
5806 case 0x164e: // (green steel)
5807 element = EL_STEEL_CHAR_D;
5810 case 0x164f: // (green steel)
5811 element = EL_STEEL_CHAR_E;
5814 case 0x1650: // (green steel)
5815 element = EL_STEEL_CHAR_F;
5818 case 0x1651: // (green steel)
5819 element = EL_STEEL_CHAR_G;
5822 case 0x1652: // (green steel)
5823 element = EL_STEEL_CHAR_H;
5826 case 0x1653: // (green steel)
5827 element = EL_STEEL_CHAR_I;
5830 case 0x1654: // (green steel)
5831 element = EL_STEEL_CHAR_J;
5834 case 0x1655: // (green steel)
5835 element = EL_STEEL_CHAR_K;
5838 case 0x1656: // (green steel)
5839 element = EL_STEEL_CHAR_L;
5842 case 0x1657: // (green steel)
5843 element = EL_STEEL_CHAR_M;
5846 case 0x1658: // (green steel)
5847 element = EL_STEEL_CHAR_N;
5850 case 0x1659: // (green steel)
5851 element = EL_STEEL_CHAR_O;
5854 case 0x165a: // (green steel)
5855 element = EL_STEEL_CHAR_P;
5858 case 0x165b: // (green steel)
5859 element = EL_STEEL_CHAR_Q;
5862 case 0x165c: // (green steel)
5863 element = EL_STEEL_CHAR_R;
5866 case 0x165d: // (green steel)
5867 element = EL_STEEL_CHAR_S;
5870 case 0x165e: // (green steel)
5871 element = EL_STEEL_CHAR_T;
5874 case 0x165f: // (green steel)
5875 element = EL_STEEL_CHAR_U;
5878 case 0x1660: // (green steel)
5879 element = EL_STEEL_CHAR_V;
5882 case 0x1661: // (green steel)
5883 element = EL_STEEL_CHAR_W;
5886 case 0x1662: // (green steel)
5887 element = EL_STEEL_CHAR_X;
5890 case 0x1663: // (green steel)
5891 element = EL_STEEL_CHAR_Y;
5894 case 0x1664: // (green steel)
5895 element = EL_STEEL_CHAR_Z;
5898 case 0x1665: // (green steel)
5899 element = EL_STEEL_CHAR_AUMLAUT;
5902 case 0x1666: // (green steel)
5903 element = EL_STEEL_CHAR_OUMLAUT;
5906 case 0x1667: // (green steel)
5907 element = EL_STEEL_CHAR_UUMLAUT;
5910 case 0x1668: // (green steel)
5911 element = EL_STEEL_CHAR_0;
5914 case 0x1669: // (green steel)
5915 element = EL_STEEL_CHAR_1;
5918 case 0x166a: // (green steel)
5919 element = EL_STEEL_CHAR_2;
5922 case 0x166b: // (green steel)
5923 element = EL_STEEL_CHAR_3;
5926 case 0x166c: // (green steel)
5927 element = EL_STEEL_CHAR_4;
5930 case 0x166d: // (green steel)
5931 element = EL_STEEL_CHAR_5;
5934 case 0x166e: // (green steel)
5935 element = EL_STEEL_CHAR_6;
5938 case 0x166f: // (green steel)
5939 element = EL_STEEL_CHAR_7;
5942 case 0x1670: // (green steel)
5943 element = EL_STEEL_CHAR_8;
5946 case 0x1671: // (green steel)
5947 element = EL_STEEL_CHAR_9;
5950 case 0x1672: // (green steel)
5951 element = EL_STEEL_CHAR_PERIOD;
5954 case 0x1673: // (green steel)
5955 element = EL_STEEL_CHAR_EXCLAM;
5958 case 0x1674: // (green steel)
5959 element = EL_STEEL_CHAR_COLON;
5962 case 0x1675: // (green steel)
5963 element = EL_STEEL_CHAR_LESS;
5966 case 0x1676: // (green steel)
5967 element = EL_STEEL_CHAR_GREATER;
5970 case 0x1677: // (green steel)
5971 element = EL_STEEL_CHAR_QUESTION;
5974 case 0x1678: // (green steel)
5975 element = EL_STEEL_CHAR_COPYRIGHT;
5978 case 0x1679: // (green steel)
5979 element = EL_STEEL_CHAR_UP;
5982 case 0x167a: // (green steel)
5983 element = EL_STEEL_CHAR_DOWN;
5986 case 0x167b: // (green steel)
5987 element = EL_STEEL_CHAR_BUTTON;
5990 case 0x167c: // (green steel)
5991 element = EL_STEEL_CHAR_PLUS;
5994 case 0x167d: // (green steel)
5995 element = EL_STEEL_CHAR_MINUS;
5998 case 0x167e: // (green steel)
5999 element = EL_STEEL_CHAR_APOSTROPHE;
6002 case 0x167f: // (green steel)
6003 element = EL_STEEL_CHAR_PARENLEFT;
6006 case 0x1680: // (green steel)
6007 element = EL_STEEL_CHAR_PARENRIGHT;
6010 case 0x1681: // gate (red)
6011 element = EL_EM_GATE_1;
6014 case 0x1682: // secret gate (red)
6015 element = EL_EM_GATE_1_GRAY;
6018 case 0x1683: // gate (yellow)
6019 element = EL_EM_GATE_2;
6022 case 0x1684: // secret gate (yellow)
6023 element = EL_EM_GATE_2_GRAY;
6026 case 0x1685: // gate (blue)
6027 element = EL_EM_GATE_4;
6030 case 0x1686: // secret gate (blue)
6031 element = EL_EM_GATE_4_GRAY;
6034 case 0x1687: // gate (green)
6035 element = EL_EM_GATE_3;
6038 case 0x1688: // secret gate (green)
6039 element = EL_EM_GATE_3_GRAY;
6042 case 0x1689: // gate (white)
6043 element = EL_DC_GATE_WHITE;
6046 case 0x168a: // secret gate (white)
6047 element = EL_DC_GATE_WHITE_GRAY;
6050 case 0x168b: // secret gate (no key)
6051 element = EL_DC_GATE_FAKE_GRAY;
6055 element = EL_ROBOT_WHEEL;
6059 element = EL_DC_TIMEGATE_SWITCH;
6063 element = EL_ACID_POOL_BOTTOM;
6067 element = EL_ACID_POOL_TOPLEFT;
6071 element = EL_ACID_POOL_TOPRIGHT;
6075 element = EL_ACID_POOL_BOTTOMLEFT;
6079 element = EL_ACID_POOL_BOTTOMRIGHT;
6083 element = EL_STEELWALL;
6087 element = EL_STEELWALL_SLIPPERY;
6090 case 0x1695: // steel wall (not round)
6091 element = EL_STEELWALL;
6094 case 0x1696: // steel wall (left)
6095 element = EL_DC_STEELWALL_1_LEFT;
6098 case 0x1697: // steel wall (bottom)
6099 element = EL_DC_STEELWALL_1_BOTTOM;
6102 case 0x1698: // steel wall (right)
6103 element = EL_DC_STEELWALL_1_RIGHT;
6106 case 0x1699: // steel wall (top)
6107 element = EL_DC_STEELWALL_1_TOP;
6110 case 0x169a: // steel wall (left/bottom)
6111 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6114 case 0x169b: // steel wall (right/bottom)
6115 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6118 case 0x169c: // steel wall (right/top)
6119 element = EL_DC_STEELWALL_1_TOPRIGHT;
6122 case 0x169d: // steel wall (left/top)
6123 element = EL_DC_STEELWALL_1_TOPLEFT;
6126 case 0x169e: // steel wall (right/bottom small)
6127 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6130 case 0x169f: // steel wall (left/bottom small)
6131 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6134 case 0x16a0: // steel wall (right/top small)
6135 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6138 case 0x16a1: // steel wall (left/top small)
6139 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6142 case 0x16a2: // steel wall (left/right)
6143 element = EL_DC_STEELWALL_1_VERTICAL;
6146 case 0x16a3: // steel wall (top/bottom)
6147 element = EL_DC_STEELWALL_1_HORIZONTAL;
6150 case 0x16a4: // steel wall 2 (left end)
6151 element = EL_DC_STEELWALL_2_LEFT;
6154 case 0x16a5: // steel wall 2 (right end)
6155 element = EL_DC_STEELWALL_2_RIGHT;
6158 case 0x16a6: // steel wall 2 (top end)
6159 element = EL_DC_STEELWALL_2_TOP;
6162 case 0x16a7: // steel wall 2 (bottom end)
6163 element = EL_DC_STEELWALL_2_BOTTOM;
6166 case 0x16a8: // steel wall 2 (left/right)
6167 element = EL_DC_STEELWALL_2_HORIZONTAL;
6170 case 0x16a9: // steel wall 2 (up/down)
6171 element = EL_DC_STEELWALL_2_VERTICAL;
6174 case 0x16aa: // steel wall 2 (mid)
6175 element = EL_DC_STEELWALL_2_MIDDLE;
6179 element = EL_SIGN_EXCLAMATION;
6183 element = EL_SIGN_RADIOACTIVITY;
6187 element = EL_SIGN_STOP;
6191 element = EL_SIGN_WHEELCHAIR;
6195 element = EL_SIGN_PARKING;
6199 element = EL_SIGN_NO_ENTRY;
6203 element = EL_SIGN_HEART;
6207 element = EL_SIGN_GIVE_WAY;
6211 element = EL_SIGN_ENTRY_FORBIDDEN;
6215 element = EL_SIGN_EMERGENCY_EXIT;
6219 element = EL_SIGN_YIN_YANG;
6223 element = EL_WALL_EMERALD;
6227 element = EL_WALL_DIAMOND;
6231 element = EL_WALL_PEARL;
6235 element = EL_WALL_CRYSTAL;
6239 element = EL_INVISIBLE_WALL;
6243 element = EL_INVISIBLE_STEELWALL;
6247 // EL_INVISIBLE_SAND
6250 element = EL_LIGHT_SWITCH;
6254 element = EL_ENVELOPE_1;
6258 if (element >= 0x0117 && element <= 0x036e) // (?)
6259 element = EL_DIAMOND;
6260 else if (element >= 0x042d && element <= 0x0684) // (?)
6261 element = EL_EMERALD;
6262 else if (element >= 0x157c && element <= 0x158b)
6264 else if (element >= 0x1590 && element <= 0x159f)
6265 element = EL_DC_LANDMINE;
6266 else if (element >= 0x16bc && element <= 0x16cb)
6267 element = EL_INVISIBLE_SAND;
6270 Warn("unknown Diamond Caves element 0x%04x", element);
6272 element = EL_UNKNOWN;
6277 return getMappedElement(element);
6280 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6282 byte header[DC_LEVEL_HEADER_SIZE];
6284 int envelope_header_pos = 62;
6285 int envelope_content_pos = 94;
6286 int level_name_pos = 251;
6287 int level_author_pos = 292;
6288 int envelope_header_len;
6289 int envelope_content_len;
6291 int level_author_len;
6293 int num_yamyam_contents;
6296 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6298 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6300 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6302 header[i * 2 + 0] = header_word >> 8;
6303 header[i * 2 + 1] = header_word & 0xff;
6306 // read some values from level header to check level decoding integrity
6307 fieldx = header[6] | (header[7] << 8);
6308 fieldy = header[8] | (header[9] << 8);
6309 num_yamyam_contents = header[60] | (header[61] << 8);
6311 // do some simple sanity checks to ensure that level was correctly decoded
6312 if (fieldx < 1 || fieldx > 256 ||
6313 fieldy < 1 || fieldy > 256 ||
6314 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6316 level->no_valid_file = TRUE;
6318 Warn("cannot decode level from stream -- using empty level");
6323 // maximum envelope header size is 31 bytes
6324 envelope_header_len = header[envelope_header_pos];
6325 // maximum envelope content size is 110 (156?) bytes
6326 envelope_content_len = header[envelope_content_pos];
6328 // maximum level title size is 40 bytes
6329 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6330 // maximum level author size is 30 (51?) bytes
6331 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6335 for (i = 0; i < envelope_header_len; i++)
6336 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6337 level->envelope[0].text[envelope_size++] =
6338 header[envelope_header_pos + 1 + i];
6340 if (envelope_header_len > 0 && envelope_content_len > 0)
6342 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6343 level->envelope[0].text[envelope_size++] = '\n';
6344 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6345 level->envelope[0].text[envelope_size++] = '\n';
6348 for (i = 0; i < envelope_content_len; i++)
6349 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6350 level->envelope[0].text[envelope_size++] =
6351 header[envelope_content_pos + 1 + i];
6353 level->envelope[0].text[envelope_size] = '\0';
6355 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6356 level->envelope[0].ysize = 10;
6357 level->envelope[0].autowrap = TRUE;
6358 level->envelope[0].centered = TRUE;
6360 for (i = 0; i < level_name_len; i++)
6361 level->name[i] = header[level_name_pos + 1 + i];
6362 level->name[level_name_len] = '\0';
6364 for (i = 0; i < level_author_len; i++)
6365 level->author[i] = header[level_author_pos + 1 + i];
6366 level->author[level_author_len] = '\0';
6368 num_yamyam_contents = header[60] | (header[61] << 8);
6369 level->num_yamyam_contents =
6370 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6372 for (i = 0; i < num_yamyam_contents; i++)
6374 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6376 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6377 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6379 if (i < MAX_ELEMENT_CONTENTS)
6380 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6384 fieldx = header[6] | (header[7] << 8);
6385 fieldy = header[8] | (header[9] << 8);
6386 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6387 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6389 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6391 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6392 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6394 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6395 level->field[x][y] = getMappedElement_DC(element_dc);
6398 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6399 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6400 level->field[x][y] = EL_PLAYER_1;
6402 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6403 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6404 level->field[x][y] = EL_PLAYER_2;
6406 level->gems_needed = header[18] | (header[19] << 8);
6408 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6409 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6410 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6411 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6412 level->score[SC_NUT] = header[28] | (header[29] << 8);
6413 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6414 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6415 level->score[SC_BUG] = header[34] | (header[35] << 8);
6416 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6417 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6418 level->score[SC_KEY] = header[40] | (header[41] << 8);
6419 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6421 level->time = header[44] | (header[45] << 8);
6423 level->amoeba_speed = header[46] | (header[47] << 8);
6424 level->time_light = header[48] | (header[49] << 8);
6425 level->time_timegate = header[50] | (header[51] << 8);
6426 level->time_wheel = header[52] | (header[53] << 8);
6427 level->time_magic_wall = header[54] | (header[55] << 8);
6428 level->extra_time = header[56] | (header[57] << 8);
6429 level->shield_normal_time = header[58] | (header[59] << 8);
6431 // shield and extra time elements do not have a score
6432 level->score[SC_SHIELD] = 0;
6433 level->extra_time_score = 0;
6435 // set time for normal and deadly shields to the same value
6436 level->shield_deadly_time = level->shield_normal_time;
6438 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6439 // can slip down from flat walls, like normal walls and steel walls
6440 level->em_slippery_gems = TRUE;
6442 // time score is counted for each 10 seconds left in Diamond Caves levels
6443 level->time_score_base = 10;
6446 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6447 struct LevelFileInfo *level_file_info,
6448 boolean level_info_only)
6450 char *filename = level_file_info->filename;
6452 int num_magic_bytes = 8;
6453 char magic_bytes[num_magic_bytes + 1];
6454 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6456 if (!(file = openFile(filename, MODE_READ)))
6458 level->no_valid_file = TRUE;
6460 if (!level_info_only)
6461 Warn("cannot read level '%s' -- using empty level", filename);
6466 // fseek(file, 0x0000, SEEK_SET);
6468 if (level_file_info->packed)
6470 // read "magic bytes" from start of file
6471 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6472 magic_bytes[0] = '\0';
6474 // check "magic bytes" for correct file format
6475 if (!strPrefix(magic_bytes, "DC2"))
6477 level->no_valid_file = TRUE;
6479 Warn("unknown DC level file '%s' -- using empty level", filename);
6484 if (strPrefix(magic_bytes, "DC2Win95") ||
6485 strPrefix(magic_bytes, "DC2Win98"))
6487 int position_first_level = 0x00fa;
6488 int extra_bytes = 4;
6491 // advance file stream to first level inside the level package
6492 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6494 // each block of level data is followed by block of non-level data
6495 num_levels_to_skip *= 2;
6497 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6498 while (num_levels_to_skip >= 0)
6500 // advance file stream to next level inside the level package
6501 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6503 level->no_valid_file = TRUE;
6505 Warn("cannot fseek in file '%s' -- using empty level", filename);
6510 // skip apparently unused extra bytes following each level
6511 ReadUnusedBytesFromFile(file, extra_bytes);
6513 // read size of next level in level package
6514 skip_bytes = getFile32BitLE(file);
6516 num_levels_to_skip--;
6521 level->no_valid_file = TRUE;
6523 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6529 LoadLevelFromFileStream_DC(file, level);
6535 // ----------------------------------------------------------------------------
6536 // functions for loading SB level
6537 // ----------------------------------------------------------------------------
6539 int getMappedElement_SB(int element_ascii, boolean use_ces)
6547 sb_element_mapping[] =
6549 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6550 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6551 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6552 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6553 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6554 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6555 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6556 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6563 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6564 if (element_ascii == sb_element_mapping[i].ascii)
6565 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6567 return EL_UNDEFINED;
6570 static void SetLevelSettings_SB(struct LevelInfo *level)
6574 level->use_step_counter = TRUE;
6577 level->score[SC_TIME_BONUS] = 0;
6578 level->time_score_base = 1;
6579 level->rate_time_over_score = TRUE;
6582 level->auto_exit_sokoban = TRUE;
6585 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6586 struct LevelFileInfo *level_file_info,
6587 boolean level_info_only)
6589 char *filename = level_file_info->filename;
6590 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6591 char last_comment[MAX_LINE_LEN];
6592 char level_name[MAX_LINE_LEN];
6595 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6596 boolean read_continued_line = FALSE;
6597 boolean reading_playfield = FALSE;
6598 boolean got_valid_playfield_line = FALSE;
6599 boolean invalid_playfield_char = FALSE;
6600 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6601 int file_level_nr = 0;
6602 int x = 0, y = 0; // initialized to make compilers happy
6604 last_comment[0] = '\0';
6605 level_name[0] = '\0';
6607 if (!(file = openFile(filename, MODE_READ)))
6609 level->no_valid_file = TRUE;
6611 if (!level_info_only)
6612 Warn("cannot read level '%s' -- using empty level", filename);
6617 while (!checkEndOfFile(file))
6619 // level successfully read, but next level may follow here
6620 if (!got_valid_playfield_line && reading_playfield)
6622 // read playfield from single level file -- skip remaining file
6623 if (!level_file_info->packed)
6626 if (file_level_nr >= num_levels_to_skip)
6631 last_comment[0] = '\0';
6632 level_name[0] = '\0';
6634 reading_playfield = FALSE;
6637 got_valid_playfield_line = FALSE;
6639 // read next line of input file
6640 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6643 // cut trailing line break (this can be newline and/or carriage return)
6644 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6645 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6648 // copy raw input line for later use (mainly debugging output)
6649 strcpy(line_raw, line);
6651 if (read_continued_line)
6653 // append new line to existing line, if there is enough space
6654 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6655 strcat(previous_line, line_ptr);
6657 strcpy(line, previous_line); // copy storage buffer to line
6659 read_continued_line = FALSE;
6662 // if the last character is '\', continue at next line
6663 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6665 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6666 strcpy(previous_line, line); // copy line to storage buffer
6668 read_continued_line = TRUE;
6674 if (line[0] == '\0')
6677 // extract comment text from comment line
6680 for (line_ptr = line; *line_ptr; line_ptr++)
6681 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6684 strcpy(last_comment, line_ptr);
6689 // extract level title text from line containing level title
6690 if (line[0] == '\'')
6692 strcpy(level_name, &line[1]);
6694 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6695 level_name[strlen(level_name) - 1] = '\0';
6700 // skip lines containing only spaces (or empty lines)
6701 for (line_ptr = line; *line_ptr; line_ptr++)
6702 if (*line_ptr != ' ')
6704 if (*line_ptr == '\0')
6707 // at this point, we have found a line containing part of a playfield
6709 got_valid_playfield_line = TRUE;
6711 if (!reading_playfield)
6713 reading_playfield = TRUE;
6714 invalid_playfield_char = FALSE;
6716 for (x = 0; x < MAX_LEV_FIELDX; x++)
6717 for (y = 0; y < MAX_LEV_FIELDY; y++)
6718 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6723 // start with topmost tile row
6727 // skip playfield line if larger row than allowed
6728 if (y >= MAX_LEV_FIELDY)
6731 // start with leftmost tile column
6734 // read playfield elements from line
6735 for (line_ptr = line; *line_ptr; line_ptr++)
6737 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6739 // stop parsing playfield line if larger column than allowed
6740 if (x >= MAX_LEV_FIELDX)
6743 if (mapped_sb_element == EL_UNDEFINED)
6745 invalid_playfield_char = TRUE;
6750 level->field[x][y] = mapped_sb_element;
6752 // continue with next tile column
6755 level->fieldx = MAX(x, level->fieldx);
6758 if (invalid_playfield_char)
6760 // if first playfield line, treat invalid lines as comment lines
6762 reading_playfield = FALSE;
6767 // continue with next tile row
6775 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6776 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6778 if (!reading_playfield)
6780 level->no_valid_file = TRUE;
6782 Warn("cannot read level '%s' -- using empty level", filename);
6787 if (*level_name != '\0')
6789 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6790 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6792 else if (*last_comment != '\0')
6794 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6795 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6799 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6802 // set all empty fields beyond the border walls to invisible steel wall
6803 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6805 if ((x == 0 || x == level->fieldx - 1 ||
6806 y == 0 || y == level->fieldy - 1) &&
6807 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6808 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6809 level->field, level->fieldx, level->fieldy);
6812 // set special level settings for Sokoban levels
6813 SetLevelSettings_SB(level);
6815 if (load_xsb_to_ces)
6817 // special global settings can now be set in level template
6818 level->use_custom_template = TRUE;
6823 // -------------------------------------------------------------------------
6824 // functions for handling native levels
6825 // -------------------------------------------------------------------------
6827 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6828 struct LevelFileInfo *level_file_info,
6829 boolean level_info_only)
6833 // determine position of requested level inside level package
6834 if (level_file_info->packed)
6835 pos = level_file_info->nr - leveldir_current->first_level;
6837 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6838 level->no_valid_file = TRUE;
6841 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6842 struct LevelFileInfo *level_file_info,
6843 boolean level_info_only)
6845 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6846 level->no_valid_file = TRUE;
6849 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6850 struct LevelFileInfo *level_file_info,
6851 boolean level_info_only)
6855 // determine position of requested level inside level package
6856 if (level_file_info->packed)
6857 pos = level_file_info->nr - leveldir_current->first_level;
6859 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6860 level->no_valid_file = TRUE;
6863 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6864 struct LevelFileInfo *level_file_info,
6865 boolean level_info_only)
6867 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6868 level->no_valid_file = TRUE;
6871 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6873 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6874 CopyNativeLevel_RND_to_BD(level);
6875 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6876 CopyNativeLevel_RND_to_EM(level);
6877 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6878 CopyNativeLevel_RND_to_SP(level);
6879 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6880 CopyNativeLevel_RND_to_MM(level);
6883 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6885 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6886 CopyNativeLevel_BD_to_RND(level);
6887 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6888 CopyNativeLevel_EM_to_RND(level);
6889 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6890 CopyNativeLevel_SP_to_RND(level);
6891 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6892 CopyNativeLevel_MM_to_RND(level);
6895 void SaveNativeLevel(struct LevelInfo *level)
6897 // saving native level files only supported for some game engines
6898 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6899 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6902 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6903 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6904 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6905 char *filename = getLevelFilenameFromBasename(basename);
6907 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6910 boolean success = FALSE;
6912 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6914 CopyNativeLevel_RND_to_BD(level);
6915 // CopyNativeTape_RND_to_BD(level);
6917 success = SaveNativeLevel_BD(filename);
6919 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6921 CopyNativeLevel_RND_to_SP(level);
6922 CopyNativeTape_RND_to_SP(level);
6924 success = SaveNativeLevel_SP(filename);
6928 Request("Native level file saved!", REQ_CONFIRM);
6930 Request("Failed to save native level file!", REQ_CONFIRM);
6934 // ----------------------------------------------------------------------------
6935 // functions for loading generic level
6936 // ----------------------------------------------------------------------------
6938 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6939 struct LevelFileInfo *level_file_info,
6940 boolean level_info_only)
6942 // always start with reliable default values
6943 setLevelInfoToDefaults(level, level_info_only, TRUE);
6945 switch (level_file_info->type)
6947 case LEVEL_FILE_TYPE_RND:
6948 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6951 case LEVEL_FILE_TYPE_BD:
6952 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6953 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6956 case LEVEL_FILE_TYPE_EM:
6957 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6958 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6961 case LEVEL_FILE_TYPE_SP:
6962 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6963 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6966 case LEVEL_FILE_TYPE_MM:
6967 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6968 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6971 case LEVEL_FILE_TYPE_DC:
6972 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6975 case LEVEL_FILE_TYPE_SB:
6976 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6980 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6984 // if level file is invalid, restore level structure to default values
6985 if (level->no_valid_file)
6986 setLevelInfoToDefaults(level, level_info_only, FALSE);
6988 if (check_special_flags("use_native_bd_game_engine"))
6989 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6991 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6992 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6994 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6995 CopyNativeLevel_Native_to_RND(level);
6998 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7000 static struct LevelFileInfo level_file_info;
7002 // always start with reliable default values
7003 setFileInfoToDefaults(&level_file_info);
7005 level_file_info.nr = 0; // unknown level number
7006 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7008 setString(&level_file_info.filename, filename);
7010 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7013 static void LoadLevel_InitVersion(struct LevelInfo *level)
7017 if (leveldir_current == NULL) // only when dumping level
7020 // all engine modifications also valid for levels which use latest engine
7021 if (level->game_version < VERSION_IDENT(3,2,0,5))
7023 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7024 level->time_score_base = 10;
7027 if (leveldir_current->latest_engine)
7029 // ---------- use latest game engine --------------------------------------
7031 /* For all levels which are forced to use the latest game engine version
7032 (normally all but user contributed, private and undefined levels), set
7033 the game engine version to the actual version; this allows for actual
7034 corrections in the game engine to take effect for existing, converted
7035 levels (from "classic" or other existing games) to make the emulation
7036 of the corresponding game more accurate, while (hopefully) not breaking
7037 existing levels created from other players. */
7039 level->game_version = GAME_VERSION_ACTUAL;
7041 /* Set special EM style gems behaviour: EM style gems slip down from
7042 normal, steel and growing wall. As this is a more fundamental change,
7043 it seems better to set the default behaviour to "off" (as it is more
7044 natural) and make it configurable in the level editor (as a property
7045 of gem style elements). Already existing converted levels (neither
7046 private nor contributed levels) are changed to the new behaviour. */
7048 if (level->file_version < FILE_VERSION_2_0)
7049 level->em_slippery_gems = TRUE;
7054 // ---------- use game engine the level was created with --------------------
7056 /* For all levels which are not forced to use the latest game engine
7057 version (normally user contributed, private and undefined levels),
7058 use the version of the game engine the levels were created for.
7060 Since 2.0.1, the game engine version is now directly stored
7061 in the level file (chunk "VERS"), so there is no need anymore
7062 to set the game version from the file version (except for old,
7063 pre-2.0 levels, where the game version is still taken from the
7064 file format version used to store the level -- see above). */
7066 // player was faster than enemies in 1.0.0 and before
7067 if (level->file_version == FILE_VERSION_1_0)
7068 for (i = 0; i < MAX_PLAYERS; i++)
7069 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7071 // default behaviour for EM style gems was "slippery" only in 2.0.1
7072 if (level->game_version == VERSION_IDENT(2,0,1,0))
7073 level->em_slippery_gems = TRUE;
7075 // springs could be pushed over pits before (pre-release version) 2.2.0
7076 if (level->game_version < VERSION_IDENT(2,2,0,0))
7077 level->use_spring_bug = TRUE;
7079 if (level->game_version < VERSION_IDENT(3,2,0,5))
7081 // time orb caused limited time in endless time levels before 3.2.0-5
7082 level->use_time_orb_bug = TRUE;
7084 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7085 level->block_snap_field = FALSE;
7087 // extra time score was same value as time left score before 3.2.0-5
7088 level->extra_time_score = level->score[SC_TIME_BONUS];
7091 if (level->game_version < VERSION_IDENT(3,2,0,7))
7093 // default behaviour for snapping was "not continuous" before 3.2.0-7
7094 level->continuous_snapping = FALSE;
7097 // only few elements were able to actively move into acid before 3.1.0
7098 // trigger settings did not exist before 3.1.0; set to default "any"
7099 if (level->game_version < VERSION_IDENT(3,1,0,0))
7101 // correct "can move into acid" settings (all zero in old levels)
7103 level->can_move_into_acid_bits = 0; // nothing can move into acid
7104 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7106 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7107 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7108 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7109 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7111 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7112 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7114 // correct trigger settings (stored as zero == "none" in old levels)
7116 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7118 int element = EL_CUSTOM_START + i;
7119 struct ElementInfo *ei = &element_info[element];
7121 for (j = 0; j < ei->num_change_pages; j++)
7123 struct ElementChangeInfo *change = &ei->change_page[j];
7125 change->trigger_player = CH_PLAYER_ANY;
7126 change->trigger_page = CH_PAGE_ANY;
7131 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7133 int element = EL_CUSTOM_256;
7134 struct ElementInfo *ei = &element_info[element];
7135 struct ElementChangeInfo *change = &ei->change_page[0];
7137 /* This is needed to fix a problem that was caused by a bugfix in function
7138 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7139 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7140 not replace walkable elements, but instead just placed the player on it,
7141 without placing the Sokoban field under the player). Unfortunately, this
7142 breaks "Snake Bite" style levels when the snake is halfway through a door
7143 that just closes (the snake head is still alive and can be moved in this
7144 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7145 player (without Sokoban element) which then gets killed as designed). */
7147 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7148 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7149 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7150 change->target_element = EL_PLAYER_1;
7153 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7154 if (level->game_version < VERSION_IDENT(3,2,5,0))
7156 /* This is needed to fix a problem that was caused by a bugfix in function
7157 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7158 corrects the behaviour when a custom element changes to another custom
7159 element with a higher element number that has change actions defined.
7160 Normally, only one change per frame is allowed for custom elements.
7161 Therefore, it is checked if a custom element already changed in the
7162 current frame; if it did, subsequent changes are suppressed.
7163 Unfortunately, this is only checked for element changes, but not for
7164 change actions, which are still executed. As the function above loops
7165 through all custom elements from lower to higher, an element change
7166 resulting in a lower CE number won't be checked again, while a target
7167 element with a higher number will also be checked, and potential change
7168 actions will get executed for this CE, too (which is wrong), while
7169 further changes are ignored (which is correct). As this bugfix breaks
7170 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7171 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7172 behaviour for existing levels and tapes that make use of this bug */
7174 level->use_action_after_change_bug = TRUE;
7177 // not centering level after relocating player was default only in 3.2.3
7178 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7179 level->shifted_relocation = TRUE;
7181 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7182 if (level->game_version < VERSION_IDENT(3,2,6,0))
7183 level->em_explodes_by_fire = TRUE;
7185 // levels were solved by the first player entering an exit up to 4.1.0.0
7186 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7187 level->solved_by_one_player = TRUE;
7189 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7190 if (level->game_version < VERSION_IDENT(4,1,1,1))
7191 level->use_life_bugs = TRUE;
7193 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7194 if (level->game_version < VERSION_IDENT(4,1,1,1))
7195 level->sb_objects_needed = FALSE;
7197 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7198 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7199 level->finish_dig_collect = FALSE;
7201 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7202 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7203 level->keep_walkable_ce = TRUE;
7206 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7208 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7211 // check if this level is (not) a Sokoban level
7212 for (y = 0; y < level->fieldy; y++)
7213 for (x = 0; x < level->fieldx; x++)
7214 if (!IS_SB_ELEMENT(Tile[x][y]))
7215 is_sokoban_level = FALSE;
7217 if (is_sokoban_level)
7219 // set special level settings for Sokoban levels
7220 SetLevelSettings_SB(level);
7224 static void LoadLevel_InitSettings(struct LevelInfo *level)
7226 // adjust level settings for (non-native) Sokoban-style levels
7227 LoadLevel_InitSettings_SB(level);
7229 // rename levels with title "nameless level" or if renaming is forced
7230 if (leveldir_current->empty_level_name != NULL &&
7231 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7232 leveldir_current->force_level_name))
7233 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7234 leveldir_current->empty_level_name, level_nr);
7237 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7241 // map elements that have changed in newer versions
7242 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7243 level->game_version);
7244 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7245 for (x = 0; x < 3; x++)
7246 for (y = 0; y < 3; y++)
7247 level->yamyam_content[i].e[x][y] =
7248 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7249 level->game_version);
7253 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7257 // map custom element change events that have changed in newer versions
7258 // (these following values were accidentally changed in version 3.0.1)
7259 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7260 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7262 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7264 int element = EL_CUSTOM_START + i;
7266 // order of checking and copying events to be mapped is important
7267 // (do not change the start and end value -- they are constant)
7268 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7270 if (HAS_CHANGE_EVENT(element, j - 2))
7272 SET_CHANGE_EVENT(element, j - 2, FALSE);
7273 SET_CHANGE_EVENT(element, j, TRUE);
7277 // order of checking and copying events to be mapped is important
7278 // (do not change the start and end value -- they are constant)
7279 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7281 if (HAS_CHANGE_EVENT(element, j - 1))
7283 SET_CHANGE_EVENT(element, j - 1, FALSE);
7284 SET_CHANGE_EVENT(element, j, TRUE);
7290 // initialize "can_change" field for old levels with only one change page
7291 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7293 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7295 int element = EL_CUSTOM_START + i;
7297 if (CAN_CHANGE(element))
7298 element_info[element].change->can_change = TRUE;
7302 // correct custom element values (for old levels without these options)
7303 if (level->game_version < VERSION_IDENT(3,1,1,0))
7305 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7307 int element = EL_CUSTOM_START + i;
7308 struct ElementInfo *ei = &element_info[element];
7310 if (ei->access_direction == MV_NO_DIRECTION)
7311 ei->access_direction = MV_ALL_DIRECTIONS;
7315 // correct custom element values (fix invalid values for all versions)
7318 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7320 int element = EL_CUSTOM_START + i;
7321 struct ElementInfo *ei = &element_info[element];
7323 for (j = 0; j < ei->num_change_pages; j++)
7325 struct ElementChangeInfo *change = &ei->change_page[j];
7327 if (change->trigger_player == CH_PLAYER_NONE)
7328 change->trigger_player = CH_PLAYER_ANY;
7330 if (change->trigger_side == CH_SIDE_NONE)
7331 change->trigger_side = CH_SIDE_ANY;
7336 // initialize "can_explode" field for old levels which did not store this
7337 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7338 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7340 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7342 int element = EL_CUSTOM_START + i;
7344 if (EXPLODES_1X1_OLD(element))
7345 element_info[element].explosion_type = EXPLODES_1X1;
7347 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7348 EXPLODES_SMASHED(element) ||
7349 EXPLODES_IMPACT(element)));
7353 // correct previously hard-coded move delay values for maze runner style
7354 if (level->game_version < VERSION_IDENT(3,1,1,0))
7356 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7358 int element = EL_CUSTOM_START + i;
7360 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7362 // previously hard-coded and therefore ignored
7363 element_info[element].move_delay_fixed = 9;
7364 element_info[element].move_delay_random = 0;
7369 // set some other uninitialized values of custom elements in older levels
7370 if (level->game_version < VERSION_IDENT(3,1,0,0))
7372 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7374 int element = EL_CUSTOM_START + i;
7376 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7378 element_info[element].explosion_delay = 17;
7379 element_info[element].ignition_delay = 8;
7383 // set mouse click change events to work for left/middle/right mouse button
7384 if (level->game_version < VERSION_IDENT(4,2,3,0))
7386 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7388 int element = EL_CUSTOM_START + i;
7389 struct ElementInfo *ei = &element_info[element];
7391 for (j = 0; j < ei->num_change_pages; j++)
7393 struct ElementChangeInfo *change = &ei->change_page[j];
7395 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7396 change->has_event[CE_PRESSED_BY_MOUSE] ||
7397 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7398 change->has_event[CE_MOUSE_PRESSED_ON_X])
7399 change->trigger_side = CH_SIDE_ANY;
7405 static void LoadLevel_InitElements(struct LevelInfo *level)
7407 LoadLevel_InitStandardElements(level);
7409 if (level->file_has_custom_elements)
7410 LoadLevel_InitCustomElements(level);
7412 // initialize element properties for level editor etc.
7413 InitElementPropertiesEngine(level->game_version);
7414 InitElementPropertiesGfxElement();
7417 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7421 // map elements that have changed in newer versions
7422 for (y = 0; y < level->fieldy; y++)
7423 for (x = 0; x < level->fieldx; x++)
7424 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7425 level->game_version);
7427 // clear unused playfield data (nicer if level gets resized in editor)
7428 for (x = 0; x < MAX_LEV_FIELDX; x++)
7429 for (y = 0; y < MAX_LEV_FIELDY; y++)
7430 if (x >= level->fieldx || y >= level->fieldy)
7431 level->field[x][y] = EL_EMPTY;
7433 // copy elements to runtime playfield array
7434 for (x = 0; x < MAX_LEV_FIELDX; x++)
7435 for (y = 0; y < MAX_LEV_FIELDY; y++)
7436 Tile[x][y] = level->field[x][y];
7438 // initialize level size variables for faster access
7439 lev_fieldx = level->fieldx;
7440 lev_fieldy = level->fieldy;
7442 // determine border element for this level
7443 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7444 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7449 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7451 struct LevelFileInfo *level_file_info = &level->file_info;
7453 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7454 CopyNativeLevel_RND_to_Native(level);
7457 static void LoadLevelTemplate_LoadAndInit(void)
7459 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7461 LoadLevel_InitVersion(&level_template);
7462 LoadLevel_InitElements(&level_template);
7463 LoadLevel_InitSettings(&level_template);
7465 ActivateLevelTemplate();
7468 void LoadLevelTemplate(int nr)
7470 if (!fileExists(getGlobalLevelTemplateFilename()))
7472 Warn("no level template found for this level");
7477 setLevelFileInfo(&level_template.file_info, nr);
7479 LoadLevelTemplate_LoadAndInit();
7482 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7484 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7486 LoadLevelTemplate_LoadAndInit();
7489 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7491 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7493 if (level.use_custom_template)
7495 if (network_level != NULL)
7496 LoadNetworkLevelTemplate(network_level);
7498 LoadLevelTemplate(-1);
7501 LoadLevel_InitVersion(&level);
7502 LoadLevel_InitElements(&level);
7503 LoadLevel_InitPlayfield(&level);
7504 LoadLevel_InitSettings(&level);
7506 LoadLevel_InitNativeEngines(&level);
7509 void LoadLevel(int nr)
7511 SetLevelSetInfo(leveldir_current->identifier, nr);
7513 setLevelFileInfo(&level.file_info, nr);
7515 LoadLevel_LoadAndInit(NULL);
7518 void LoadLevelInfoOnly(int nr)
7520 setLevelFileInfo(&level.file_info, nr);
7522 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7525 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7527 SetLevelSetInfo(network_level->leveldir_identifier,
7528 network_level->file_info.nr);
7530 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7532 LoadLevel_LoadAndInit(network_level);
7535 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7539 chunk_size += putFileVersion(file, level->file_version);
7540 chunk_size += putFileVersion(file, level->game_version);
7545 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7549 chunk_size += putFile16BitBE(file, level->creation_date.year);
7550 chunk_size += putFile8Bit(file, level->creation_date.month);
7551 chunk_size += putFile8Bit(file, level->creation_date.day);
7556 #if ENABLE_HISTORIC_CHUNKS
7557 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7561 putFile8Bit(file, level->fieldx);
7562 putFile8Bit(file, level->fieldy);
7564 putFile16BitBE(file, level->time);
7565 putFile16BitBE(file, level->gems_needed);
7567 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7568 putFile8Bit(file, level->name[i]);
7570 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7571 putFile8Bit(file, level->score[i]);
7573 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7574 for (y = 0; y < 3; y++)
7575 for (x = 0; x < 3; x++)
7576 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7577 level->yamyam_content[i].e[x][y]));
7578 putFile8Bit(file, level->amoeba_speed);
7579 putFile8Bit(file, level->time_magic_wall);
7580 putFile8Bit(file, level->time_wheel);
7581 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7582 level->amoeba_content));
7583 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7584 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7585 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7586 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7588 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7590 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7591 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7592 putFile32BitBE(file, level->can_move_into_acid_bits);
7593 putFile8Bit(file, level->dont_collide_with_bits);
7595 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7596 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7598 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7599 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7600 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7602 putFile8Bit(file, level->game_engine_type);
7604 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7608 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7613 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7614 chunk_size += putFile8Bit(file, level->name[i]);
7619 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7624 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7625 chunk_size += putFile8Bit(file, level->author[i]);
7630 #if ENABLE_HISTORIC_CHUNKS
7631 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7636 for (y = 0; y < level->fieldy; y++)
7637 for (x = 0; x < level->fieldx; x++)
7638 if (level->encoding_16bit_field)
7639 chunk_size += putFile16BitBE(file, level->field[x][y]);
7641 chunk_size += putFile8Bit(file, level->field[x][y]);
7647 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7652 for (y = 0; y < level->fieldy; y++)
7653 for (x = 0; x < level->fieldx; x++)
7654 chunk_size += putFile16BitBE(file, level->field[x][y]);
7659 #if ENABLE_HISTORIC_CHUNKS
7660 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7664 putFile8Bit(file, EL_YAMYAM);
7665 putFile8Bit(file, level->num_yamyam_contents);
7666 putFile8Bit(file, 0);
7667 putFile8Bit(file, 0);
7669 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7670 for (y = 0; y < 3; y++)
7671 for (x = 0; x < 3; x++)
7672 if (level->encoding_16bit_field)
7673 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7675 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7679 #if ENABLE_HISTORIC_CHUNKS
7680 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7683 int num_contents, content_xsize, content_ysize;
7684 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7686 if (element == EL_YAMYAM)
7688 num_contents = level->num_yamyam_contents;
7692 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7693 for (y = 0; y < 3; y++)
7694 for (x = 0; x < 3; x++)
7695 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7697 else if (element == EL_BD_AMOEBA)
7703 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7704 for (y = 0; y < 3; y++)
7705 for (x = 0; x < 3; x++)
7706 content_array[i][x][y] = EL_EMPTY;
7707 content_array[0][0][0] = level->amoeba_content;
7711 // chunk header already written -- write empty chunk data
7712 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7714 Warn("cannot save content for element '%d'", element);
7719 putFile16BitBE(file, element);
7720 putFile8Bit(file, num_contents);
7721 putFile8Bit(file, content_xsize);
7722 putFile8Bit(file, content_ysize);
7724 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7726 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7727 for (y = 0; y < 3; y++)
7728 for (x = 0; x < 3; x++)
7729 putFile16BitBE(file, content_array[i][x][y]);
7733 #if ENABLE_HISTORIC_CHUNKS
7734 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7736 int envelope_nr = element - EL_ENVELOPE_1;
7737 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7741 chunk_size += putFile16BitBE(file, element);
7742 chunk_size += putFile16BitBE(file, envelope_len);
7743 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7744 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7746 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7747 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7749 for (i = 0; i < envelope_len; i++)
7750 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7756 #if ENABLE_HISTORIC_CHUNKS
7757 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7758 int num_changed_custom_elements)
7762 putFile16BitBE(file, num_changed_custom_elements);
7764 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7766 int element = EL_CUSTOM_START + i;
7768 struct ElementInfo *ei = &element_info[element];
7770 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7772 if (check < num_changed_custom_elements)
7774 putFile16BitBE(file, element);
7775 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7782 if (check != num_changed_custom_elements) // should not happen
7783 Warn("inconsistent number of custom element properties");
7787 #if ENABLE_HISTORIC_CHUNKS
7788 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7789 int num_changed_custom_elements)
7793 putFile16BitBE(file, num_changed_custom_elements);
7795 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7797 int element = EL_CUSTOM_START + i;
7799 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7801 if (check < num_changed_custom_elements)
7803 putFile16BitBE(file, element);
7804 putFile16BitBE(file, element_info[element].change->target_element);
7811 if (check != num_changed_custom_elements) // should not happen
7812 Warn("inconsistent number of custom target elements");
7816 #if ENABLE_HISTORIC_CHUNKS
7817 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7818 int num_changed_custom_elements)
7820 int i, j, x, y, check = 0;
7822 putFile16BitBE(file, num_changed_custom_elements);
7824 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7826 int element = EL_CUSTOM_START + i;
7827 struct ElementInfo *ei = &element_info[element];
7829 if (ei->modified_settings)
7831 if (check < num_changed_custom_elements)
7833 putFile16BitBE(file, element);
7835 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7836 putFile8Bit(file, ei->description[j]);
7838 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7840 // some free bytes for future properties and padding
7841 WriteUnusedBytesToFile(file, 7);
7843 putFile8Bit(file, ei->use_gfx_element);
7844 putFile16BitBE(file, ei->gfx_element_initial);
7846 putFile8Bit(file, ei->collect_score_initial);
7847 putFile8Bit(file, ei->collect_count_initial);
7849 putFile16BitBE(file, ei->push_delay_fixed);
7850 putFile16BitBE(file, ei->push_delay_random);
7851 putFile16BitBE(file, ei->move_delay_fixed);
7852 putFile16BitBE(file, ei->move_delay_random);
7854 putFile16BitBE(file, ei->move_pattern);
7855 putFile8Bit(file, ei->move_direction_initial);
7856 putFile8Bit(file, ei->move_stepsize);
7858 for (y = 0; y < 3; y++)
7859 for (x = 0; x < 3; x++)
7860 putFile16BitBE(file, ei->content.e[x][y]);
7862 putFile32BitBE(file, ei->change->events);
7864 putFile16BitBE(file, ei->change->target_element);
7866 putFile16BitBE(file, ei->change->delay_fixed);
7867 putFile16BitBE(file, ei->change->delay_random);
7868 putFile16BitBE(file, ei->change->delay_frames);
7870 putFile16BitBE(file, ei->change->initial_trigger_element);
7872 putFile8Bit(file, ei->change->explode);
7873 putFile8Bit(file, ei->change->use_target_content);
7874 putFile8Bit(file, ei->change->only_if_complete);
7875 putFile8Bit(file, ei->change->use_random_replace);
7877 putFile8Bit(file, ei->change->random_percentage);
7878 putFile8Bit(file, ei->change->replace_when);
7880 for (y = 0; y < 3; y++)
7881 for (x = 0; x < 3; x++)
7882 putFile16BitBE(file, ei->change->content.e[x][y]);
7884 putFile8Bit(file, ei->slippery_type);
7886 // some free bytes for future properties and padding
7887 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7894 if (check != num_changed_custom_elements) // should not happen
7895 Warn("inconsistent number of custom element properties");
7899 #if ENABLE_HISTORIC_CHUNKS
7900 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7902 struct ElementInfo *ei = &element_info[element];
7905 // ---------- custom element base property values (96 bytes) ----------------
7907 putFile16BitBE(file, element);
7909 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7910 putFile8Bit(file, ei->description[i]);
7912 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7914 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7916 putFile8Bit(file, ei->num_change_pages);
7918 putFile16BitBE(file, ei->ce_value_fixed_initial);
7919 putFile16BitBE(file, ei->ce_value_random_initial);
7920 putFile8Bit(file, ei->use_last_ce_value);
7922 putFile8Bit(file, ei->use_gfx_element);
7923 putFile16BitBE(file, ei->gfx_element_initial);
7925 putFile8Bit(file, ei->collect_score_initial);
7926 putFile8Bit(file, ei->collect_count_initial);
7928 putFile8Bit(file, ei->drop_delay_fixed);
7929 putFile8Bit(file, ei->push_delay_fixed);
7930 putFile8Bit(file, ei->drop_delay_random);
7931 putFile8Bit(file, ei->push_delay_random);
7932 putFile16BitBE(file, ei->move_delay_fixed);
7933 putFile16BitBE(file, ei->move_delay_random);
7935 // bits 0 - 15 of "move_pattern" ...
7936 putFile16BitBE(file, ei->move_pattern & 0xffff);
7937 putFile8Bit(file, ei->move_direction_initial);
7938 putFile8Bit(file, ei->move_stepsize);
7940 putFile8Bit(file, ei->slippery_type);
7942 for (y = 0; y < 3; y++)
7943 for (x = 0; x < 3; x++)
7944 putFile16BitBE(file, ei->content.e[x][y]);
7946 putFile16BitBE(file, ei->move_enter_element);
7947 putFile16BitBE(file, ei->move_leave_element);
7948 putFile8Bit(file, ei->move_leave_type);
7950 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7951 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7953 putFile8Bit(file, ei->access_direction);
7955 putFile8Bit(file, ei->explosion_delay);
7956 putFile8Bit(file, ei->ignition_delay);
7957 putFile8Bit(file, ei->explosion_type);
7959 // some free bytes for future custom property values and padding
7960 WriteUnusedBytesToFile(file, 1);
7962 // ---------- change page property values (48 bytes) ------------------------
7964 for (i = 0; i < ei->num_change_pages; i++)
7966 struct ElementChangeInfo *change = &ei->change_page[i];
7967 unsigned int event_bits;
7969 // bits 0 - 31 of "has_event[]" ...
7971 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7972 if (change->has_event[j])
7973 event_bits |= (1u << j);
7974 putFile32BitBE(file, event_bits);
7976 putFile16BitBE(file, change->target_element);
7978 putFile16BitBE(file, change->delay_fixed);
7979 putFile16BitBE(file, change->delay_random);
7980 putFile16BitBE(file, change->delay_frames);
7982 putFile16BitBE(file, change->initial_trigger_element);
7984 putFile8Bit(file, change->explode);
7985 putFile8Bit(file, change->use_target_content);
7986 putFile8Bit(file, change->only_if_complete);
7987 putFile8Bit(file, change->use_random_replace);
7989 putFile8Bit(file, change->random_percentage);
7990 putFile8Bit(file, change->replace_when);
7992 for (y = 0; y < 3; y++)
7993 for (x = 0; x < 3; x++)
7994 putFile16BitBE(file, change->target_content.e[x][y]);
7996 putFile8Bit(file, change->can_change);
7998 putFile8Bit(file, change->trigger_side);
8000 putFile8Bit(file, change->trigger_player);
8001 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8002 log_2(change->trigger_page)));
8004 putFile8Bit(file, change->has_action);
8005 putFile8Bit(file, change->action_type);
8006 putFile8Bit(file, change->action_mode);
8007 putFile16BitBE(file, change->action_arg);
8009 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8011 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8012 if (change->has_event[j])
8013 event_bits |= (1u << (j - 32));
8014 putFile8Bit(file, event_bits);
8019 #if ENABLE_HISTORIC_CHUNKS
8020 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8022 struct ElementInfo *ei = &element_info[element];
8023 struct ElementGroupInfo *group = ei->group;
8026 putFile16BitBE(file, element);
8028 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8029 putFile8Bit(file, ei->description[i]);
8031 putFile8Bit(file, group->num_elements);
8033 putFile8Bit(file, ei->use_gfx_element);
8034 putFile16BitBE(file, ei->gfx_element_initial);
8036 putFile8Bit(file, group->choice_mode);
8038 // some free bytes for future values and padding
8039 WriteUnusedBytesToFile(file, 3);
8041 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8042 putFile16BitBE(file, group->element[i]);
8046 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8047 boolean write_element)
8049 int save_type = entry->save_type;
8050 int data_type = entry->data_type;
8051 int conf_type = entry->conf_type;
8052 int byte_mask = conf_type & CONF_MASK_BYTES;
8053 int element = entry->element;
8054 int default_value = entry->default_value;
8056 boolean modified = FALSE;
8058 if (byte_mask != CONF_MASK_MULTI_BYTES)
8060 void *value_ptr = entry->value;
8061 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8064 // check if any settings have been modified before saving them
8065 if (value != default_value)
8068 // do not save if explicitly told or if unmodified default settings
8069 if ((save_type == SAVE_CONF_NEVER) ||
8070 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8074 num_bytes += putFile16BitBE(file, element);
8076 num_bytes += putFile8Bit(file, conf_type);
8077 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8078 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8079 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8082 else if (data_type == TYPE_STRING)
8084 char *default_string = entry->default_string;
8085 char *string = (char *)(entry->value);
8086 int string_length = strlen(string);
8089 // check if any settings have been modified before saving them
8090 if (!strEqual(string, default_string))
8093 // do not save if explicitly told or if unmodified default settings
8094 if ((save_type == SAVE_CONF_NEVER) ||
8095 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8099 num_bytes += putFile16BitBE(file, element);
8101 num_bytes += putFile8Bit(file, conf_type);
8102 num_bytes += putFile16BitBE(file, string_length);
8104 for (i = 0; i < string_length; i++)
8105 num_bytes += putFile8Bit(file, string[i]);
8107 else if (data_type == TYPE_ELEMENT_LIST)
8109 int *element_array = (int *)(entry->value);
8110 int num_elements = *(int *)(entry->num_entities);
8113 // check if any settings have been modified before saving them
8114 for (i = 0; i < num_elements; i++)
8115 if (element_array[i] != default_value)
8118 // do not save if explicitly told or if unmodified default settings
8119 if ((save_type == SAVE_CONF_NEVER) ||
8120 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8124 num_bytes += putFile16BitBE(file, element);
8126 num_bytes += putFile8Bit(file, conf_type);
8127 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8129 for (i = 0; i < num_elements; i++)
8130 num_bytes += putFile16BitBE(file, element_array[i]);
8132 else if (data_type == TYPE_CONTENT_LIST)
8134 struct Content *content = (struct Content *)(entry->value);
8135 int num_contents = *(int *)(entry->num_entities);
8138 // check if any settings have been modified before saving them
8139 for (i = 0; i < num_contents; i++)
8140 for (y = 0; y < 3; y++)
8141 for (x = 0; x < 3; x++)
8142 if (content[i].e[x][y] != 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_contents * CONF_CONTENT_NUM_BYTES);
8156 for (i = 0; i < num_contents; i++)
8157 for (y = 0; y < 3; y++)
8158 for (x = 0; x < 3; x++)
8159 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8165 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8170 li = *level; // copy level data into temporary buffer
8172 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8173 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8178 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8183 li = *level; // copy level data into temporary buffer
8185 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8186 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8191 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8193 int envelope_nr = element - EL_ENVELOPE_1;
8197 chunk_size += putFile16BitBE(file, element);
8199 // copy envelope data into temporary buffer
8200 xx_envelope = level->envelope[envelope_nr];
8202 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8203 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8208 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8210 struct ElementInfo *ei = &element_info[element];
8214 chunk_size += putFile16BitBE(file, element);
8216 xx_ei = *ei; // copy element data into temporary buffer
8218 // set default description string for this specific element
8219 strcpy(xx_default_description, getDefaultElementDescription(ei));
8221 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8222 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8224 for (i = 0; i < ei->num_change_pages; i++)
8226 struct ElementChangeInfo *change = &ei->change_page[i];
8228 xx_current_change_page = i;
8230 xx_change = *change; // copy change data into temporary buffer
8233 setEventBitsFromEventFlags(change);
8235 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8236 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8243 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8245 struct ElementInfo *ei = &element_info[element];
8246 struct ElementGroupInfo *group = ei->group;
8250 chunk_size += putFile16BitBE(file, element);
8252 xx_ei = *ei; // copy element data into temporary buffer
8253 xx_group = *group; // copy group data into temporary buffer
8255 // set default description string for this specific element
8256 strcpy(xx_default_description, getDefaultElementDescription(ei));
8258 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8259 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8264 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8266 struct ElementInfo *ei = &element_info[element];
8270 chunk_size += putFile16BitBE(file, element);
8272 xx_ei = *ei; // copy element data into temporary buffer
8274 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8275 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8280 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8281 boolean save_as_template)
8287 if (!(file = fopen(filename, MODE_WRITE)))
8289 Warn("cannot save level file '%s'", filename);
8294 level->file_version = FILE_VERSION_ACTUAL;
8295 level->game_version = GAME_VERSION_ACTUAL;
8297 level->creation_date = getCurrentDate();
8299 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8300 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8302 chunk_size = SaveLevel_VERS(NULL, level);
8303 putFileChunkBE(file, "VERS", chunk_size);
8304 SaveLevel_VERS(file, level);
8306 chunk_size = SaveLevel_DATE(NULL, level);
8307 putFileChunkBE(file, "DATE", chunk_size);
8308 SaveLevel_DATE(file, level);
8310 chunk_size = SaveLevel_NAME(NULL, level);
8311 putFileChunkBE(file, "NAME", chunk_size);
8312 SaveLevel_NAME(file, level);
8314 chunk_size = SaveLevel_AUTH(NULL, level);
8315 putFileChunkBE(file, "AUTH", chunk_size);
8316 SaveLevel_AUTH(file, level);
8318 chunk_size = SaveLevel_INFO(NULL, level);
8319 putFileChunkBE(file, "INFO", chunk_size);
8320 SaveLevel_INFO(file, level);
8322 chunk_size = SaveLevel_BODY(NULL, level);
8323 putFileChunkBE(file, "BODY", chunk_size);
8324 SaveLevel_BODY(file, level);
8326 chunk_size = SaveLevel_ELEM(NULL, level);
8327 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8329 putFileChunkBE(file, "ELEM", chunk_size);
8330 SaveLevel_ELEM(file, level);
8333 for (i = 0; i < NUM_ENVELOPES; i++)
8335 int element = EL_ENVELOPE_1 + i;
8337 chunk_size = SaveLevel_NOTE(NULL, level, element);
8338 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8340 putFileChunkBE(file, "NOTE", chunk_size);
8341 SaveLevel_NOTE(file, level, element);
8345 // if not using template level, check for non-default custom/group elements
8346 if (!level->use_custom_template || save_as_template)
8348 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8350 int element = EL_CUSTOM_START + i;
8352 chunk_size = SaveLevel_CUSX(NULL, level, element);
8353 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8355 putFileChunkBE(file, "CUSX", chunk_size);
8356 SaveLevel_CUSX(file, level, element);
8360 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8362 int element = EL_GROUP_START + i;
8364 chunk_size = SaveLevel_GRPX(NULL, level, element);
8365 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8367 putFileChunkBE(file, "GRPX", chunk_size);
8368 SaveLevel_GRPX(file, level, element);
8372 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8374 int element = GET_EMPTY_ELEMENT(i);
8376 chunk_size = SaveLevel_EMPX(NULL, level, element);
8377 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8379 putFileChunkBE(file, "EMPX", chunk_size);
8380 SaveLevel_EMPX(file, level, element);
8387 SetFilePermissions(filename, PERMS_PRIVATE);
8390 void SaveLevel(int nr)
8392 char *filename = getDefaultLevelFilename(nr);
8394 SaveLevelFromFilename(&level, filename, FALSE);
8397 void SaveLevelTemplate(void)
8399 char *filename = getLocalLevelTemplateFilename();
8401 SaveLevelFromFilename(&level, filename, TRUE);
8404 boolean SaveLevelChecked(int nr)
8406 char *filename = getDefaultLevelFilename(nr);
8407 boolean new_level = !fileExists(filename);
8408 boolean level_saved = FALSE;
8410 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8415 Request("Level saved!", REQ_CONFIRM);
8423 void DumpLevel(struct LevelInfo *level)
8425 if (level->no_level_file || level->no_valid_file)
8427 Warn("cannot dump -- no valid level file found");
8433 Print("Level xxx (file version %08d, game version %08d)\n",
8434 level->file_version, level->game_version);
8437 Print("Level author: '%s'\n", level->author);
8438 Print("Level title: '%s'\n", level->name);
8440 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8442 Print("Level time: %d seconds\n", level->time);
8443 Print("Gems needed: %d\n", level->gems_needed);
8445 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8446 Print("Time for wheel: %d seconds\n", level->time_wheel);
8447 Print("Time for light: %d seconds\n", level->time_light);
8448 Print("Time for timegate: %d seconds\n", level->time_timegate);
8450 Print("Amoeba speed: %d\n", level->amoeba_speed);
8453 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8454 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8455 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8456 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8457 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8458 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8464 for (i = 0; i < NUM_ENVELOPES; i++)
8466 char *text = level->envelope[i].text;
8467 int text_len = strlen(text);
8468 boolean has_text = FALSE;
8470 for (j = 0; j < text_len; j++)
8471 if (text[j] != ' ' && text[j] != '\n')
8477 Print("Envelope %d:\n'%s'\n", i + 1, text);
8485 void DumpLevels(void)
8487 static LevelDirTree *dumplevel_leveldir = NULL;
8489 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8490 global.dumplevel_leveldir);
8492 if (dumplevel_leveldir == NULL)
8493 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8495 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8496 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8497 Fail("no such level number: %d", global.dumplevel_level_nr);
8499 leveldir_current = dumplevel_leveldir;
8501 LoadLevel(global.dumplevel_level_nr);
8508 // ============================================================================
8509 // tape file functions
8510 // ============================================================================
8512 static void setTapeInfoToDefaults(void)
8516 // always start with reliable default values (empty tape)
8519 // default values (also for pre-1.2 tapes) with only the first player
8520 tape.player_participates[0] = TRUE;
8521 for (i = 1; i < MAX_PLAYERS; i++)
8522 tape.player_participates[i] = FALSE;
8524 // at least one (default: the first) player participates in every tape
8525 tape.num_participating_players = 1;
8527 tape.property_bits = TAPE_PROPERTY_NONE;
8529 tape.level_nr = level_nr;
8531 tape.changed = FALSE;
8532 tape.solved = FALSE;
8534 tape.recording = FALSE;
8535 tape.playing = FALSE;
8536 tape.pausing = FALSE;
8538 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8539 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8541 tape.no_info_chunk = TRUE;
8542 tape.no_valid_file = FALSE;
8545 static int getTapePosSize(struct TapeInfo *tape)
8547 int tape_pos_size = 0;
8549 if (tape->use_key_actions)
8550 tape_pos_size += tape->num_participating_players;
8552 if (tape->use_mouse_actions)
8553 tape_pos_size += 3; // x and y position and mouse button mask
8555 tape_pos_size += 1; // tape action delay value
8557 return tape_pos_size;
8560 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8562 tape->use_key_actions = FALSE;
8563 tape->use_mouse_actions = FALSE;
8565 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8566 tape->use_key_actions = TRUE;
8568 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8569 tape->use_mouse_actions = TRUE;
8572 static int getTapeActionValue(struct TapeInfo *tape)
8574 return (tape->use_key_actions &&
8575 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8576 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8577 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8578 TAPE_ACTIONS_DEFAULT);
8581 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8583 tape->file_version = getFileVersion(file);
8584 tape->game_version = getFileVersion(file);
8589 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8593 tape->random_seed = getFile32BitBE(file);
8594 tape->date = getFile32BitBE(file);
8595 tape->length = getFile32BitBE(file);
8597 // read header fields that are new since version 1.2
8598 if (tape->file_version >= FILE_VERSION_1_2)
8600 byte store_participating_players = getFile8Bit(file);
8603 // since version 1.2, tapes store which players participate in the tape
8604 tape->num_participating_players = 0;
8605 for (i = 0; i < MAX_PLAYERS; i++)
8607 tape->player_participates[i] = FALSE;
8609 if (store_participating_players & (1 << i))
8611 tape->player_participates[i] = TRUE;
8612 tape->num_participating_players++;
8616 setTapeActionFlags(tape, getFile8Bit(file));
8618 tape->property_bits = getFile8Bit(file);
8619 tape->solved = getFile8Bit(file);
8621 engine_version = getFileVersion(file);
8622 if (engine_version > 0)
8623 tape->engine_version = engine_version;
8625 tape->engine_version = tape->game_version;
8631 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8633 tape->scr_fieldx = getFile8Bit(file);
8634 tape->scr_fieldy = getFile8Bit(file);
8639 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8641 char *level_identifier = NULL;
8642 int level_identifier_size;
8645 tape->no_info_chunk = FALSE;
8647 level_identifier_size = getFile16BitBE(file);
8649 level_identifier = checked_malloc(level_identifier_size);
8651 for (i = 0; i < level_identifier_size; i++)
8652 level_identifier[i] = getFile8Bit(file);
8654 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8655 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8657 checked_free(level_identifier);
8659 tape->level_nr = getFile16BitBE(file);
8661 chunk_size = 2 + level_identifier_size + 2;
8666 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8669 int tape_pos_size = getTapePosSize(tape);
8670 int chunk_size_expected = tape_pos_size * tape->length;
8672 if (chunk_size_expected != chunk_size)
8674 ReadUnusedBytesFromFile(file, chunk_size);
8675 return chunk_size_expected;
8678 for (i = 0; i < tape->length; i++)
8680 if (i >= MAX_TAPE_LEN)
8682 Warn("tape truncated -- size exceeds maximum tape size %d",
8685 // tape too large; read and ignore remaining tape data from this chunk
8686 for (;i < tape->length; i++)
8687 ReadUnusedBytesFromFile(file, tape_pos_size);
8692 if (tape->use_key_actions)
8694 for (j = 0; j < MAX_PLAYERS; j++)
8696 tape->pos[i].action[j] = MV_NONE;
8698 if (tape->player_participates[j])
8699 tape->pos[i].action[j] = getFile8Bit(file);
8703 if (tape->use_mouse_actions)
8705 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8706 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8707 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8710 tape->pos[i].delay = getFile8Bit(file);
8712 if (tape->file_version == FILE_VERSION_1_0)
8714 // eliminate possible diagonal moves in old tapes
8715 // this is only for backward compatibility
8717 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8718 byte action = tape->pos[i].action[0];
8719 int k, num_moves = 0;
8721 for (k = 0; k < 4; k++)
8723 if (action & joy_dir[k])
8725 tape->pos[i + num_moves].action[0] = joy_dir[k];
8727 tape->pos[i + num_moves].delay = 0;
8736 tape->length += num_moves;
8739 else if (tape->file_version < FILE_VERSION_2_0)
8741 // convert pre-2.0 tapes to new tape format
8743 if (tape->pos[i].delay > 1)
8746 tape->pos[i + 1] = tape->pos[i];
8747 tape->pos[i + 1].delay = 1;
8750 for (j = 0; j < MAX_PLAYERS; j++)
8751 tape->pos[i].action[j] = MV_NONE;
8752 tape->pos[i].delay--;
8759 if (checkEndOfFile(file))
8763 if (i != tape->length)
8764 chunk_size = tape_pos_size * i;
8769 static void LoadTape_SokobanSolution(char *filename)
8772 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8774 if (!(file = openFile(filename, MODE_READ)))
8776 tape.no_valid_file = TRUE;
8781 while (!checkEndOfFile(file))
8783 unsigned char c = getByteFromFile(file);
8785 if (checkEndOfFile(file))
8792 tape.pos[tape.length].action[0] = MV_UP;
8793 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8799 tape.pos[tape.length].action[0] = MV_DOWN;
8800 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8806 tape.pos[tape.length].action[0] = MV_LEFT;
8807 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8813 tape.pos[tape.length].action[0] = MV_RIGHT;
8814 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8822 // ignore white-space characters
8826 tape.no_valid_file = TRUE;
8828 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8836 if (tape.no_valid_file)
8839 tape.length_frames = GetTapeLengthFrames();
8840 tape.length_seconds = GetTapeLengthSeconds();
8843 void LoadTapeFromFilename(char *filename)
8845 char cookie[MAX_LINE_LEN];
8846 char chunk_name[CHUNK_ID_LEN + 1];
8850 // always start with reliable default values
8851 setTapeInfoToDefaults();
8853 if (strSuffix(filename, ".sln"))
8855 LoadTape_SokobanSolution(filename);
8860 if (!(file = openFile(filename, MODE_READ)))
8862 tape.no_valid_file = TRUE;
8867 getFileChunkBE(file, chunk_name, NULL);
8868 if (strEqual(chunk_name, "RND1"))
8870 getFile32BitBE(file); // not used
8872 getFileChunkBE(file, chunk_name, NULL);
8873 if (!strEqual(chunk_name, "TAPE"))
8875 tape.no_valid_file = TRUE;
8877 Warn("unknown format of tape file '%s'", filename);
8884 else // check for pre-2.0 file format with cookie string
8886 strcpy(cookie, chunk_name);
8887 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8889 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8890 cookie[strlen(cookie) - 1] = '\0';
8892 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8894 tape.no_valid_file = TRUE;
8896 Warn("unknown format of tape file '%s'", filename);
8903 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8905 tape.no_valid_file = TRUE;
8907 Warn("unsupported version of tape file '%s'", filename);
8914 // pre-2.0 tape files have no game version, so use file version here
8915 tape.game_version = tape.file_version;
8918 if (tape.file_version < FILE_VERSION_1_2)
8920 // tape files from versions before 1.2.0 without chunk structure
8921 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8922 LoadTape_BODY(file, 2 * tape.length, &tape);
8930 int (*loader)(File *, int, struct TapeInfo *);
8934 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8935 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8936 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8937 { "INFO", -1, LoadTape_INFO },
8938 { "BODY", -1, LoadTape_BODY },
8942 while (getFileChunkBE(file, chunk_name, &chunk_size))
8946 while (chunk_info[i].name != NULL &&
8947 !strEqual(chunk_name, chunk_info[i].name))
8950 if (chunk_info[i].name == NULL)
8952 Warn("unknown chunk '%s' in tape file '%s'",
8953 chunk_name, filename);
8955 ReadUnusedBytesFromFile(file, chunk_size);
8957 else if (chunk_info[i].size != -1 &&
8958 chunk_info[i].size != chunk_size)
8960 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8961 chunk_size, chunk_name, filename);
8963 ReadUnusedBytesFromFile(file, chunk_size);
8967 // call function to load this tape chunk
8968 int chunk_size_expected =
8969 (chunk_info[i].loader)(file, chunk_size, &tape);
8971 // the size of some chunks cannot be checked before reading other
8972 // chunks first (like "HEAD" and "BODY") that contain some header
8973 // information, so check them here
8974 if (chunk_size_expected != chunk_size)
8976 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8977 chunk_size, chunk_name, filename);
8985 tape.length_frames = GetTapeLengthFrames();
8986 tape.length_seconds = GetTapeLengthSeconds();
8989 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8991 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8993 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8994 tape.engine_version);
8998 void LoadTape(int nr)
9000 char *filename = getTapeFilename(nr);
9002 LoadTapeFromFilename(filename);
9005 void LoadSolutionTape(int nr)
9007 char *filename = getSolutionTapeFilename(nr);
9009 LoadTapeFromFilename(filename);
9011 if (TAPE_IS_EMPTY(tape))
9013 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9014 level.native_bd_level->replay != NULL)
9015 CopyNativeTape_BD_to_RND(&level);
9016 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9017 level.native_sp_level->demo.is_available)
9018 CopyNativeTape_SP_to_RND(&level);
9022 void LoadScoreTape(char *score_tape_basename, int nr)
9024 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9026 LoadTapeFromFilename(filename);
9029 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9031 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9033 LoadTapeFromFilename(filename);
9036 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9038 // chunk required for team mode tapes with non-default screen size
9039 return (tape->num_participating_players > 1 &&
9040 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9041 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9044 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9046 putFileVersion(file, tape->file_version);
9047 putFileVersion(file, tape->game_version);
9050 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9053 byte store_participating_players = 0;
9055 // set bits for participating players for compact storage
9056 for (i = 0; i < MAX_PLAYERS; i++)
9057 if (tape->player_participates[i])
9058 store_participating_players |= (1 << i);
9060 putFile32BitBE(file, tape->random_seed);
9061 putFile32BitBE(file, tape->date);
9062 putFile32BitBE(file, tape->length);
9064 putFile8Bit(file, store_participating_players);
9066 putFile8Bit(file, getTapeActionValue(tape));
9068 putFile8Bit(file, tape->property_bits);
9069 putFile8Bit(file, tape->solved);
9071 putFileVersion(file, tape->engine_version);
9074 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9076 putFile8Bit(file, tape->scr_fieldx);
9077 putFile8Bit(file, tape->scr_fieldy);
9080 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9082 int level_identifier_size = strlen(tape->level_identifier) + 1;
9085 putFile16BitBE(file, level_identifier_size);
9087 for (i = 0; i < level_identifier_size; i++)
9088 putFile8Bit(file, tape->level_identifier[i]);
9090 putFile16BitBE(file, tape->level_nr);
9093 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9097 for (i = 0; i < tape->length; i++)
9099 if (tape->use_key_actions)
9101 for (j = 0; j < MAX_PLAYERS; j++)
9102 if (tape->player_participates[j])
9103 putFile8Bit(file, tape->pos[i].action[j]);
9106 if (tape->use_mouse_actions)
9108 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9109 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9110 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9113 putFile8Bit(file, tape->pos[i].delay);
9117 void SaveTapeToFilename(char *filename)
9121 int info_chunk_size;
9122 int body_chunk_size;
9124 if (!(file = fopen(filename, MODE_WRITE)))
9126 Warn("cannot save level recording file '%s'", filename);
9131 tape_pos_size = getTapePosSize(&tape);
9133 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9134 body_chunk_size = tape_pos_size * tape.length;
9136 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9137 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9139 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9140 SaveTape_VERS(file, &tape);
9142 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9143 SaveTape_HEAD(file, &tape);
9145 if (checkSaveTape_SCRN(&tape))
9147 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9148 SaveTape_SCRN(file, &tape);
9151 putFileChunkBE(file, "INFO", info_chunk_size);
9152 SaveTape_INFO(file, &tape);
9154 putFileChunkBE(file, "BODY", body_chunk_size);
9155 SaveTape_BODY(file, &tape);
9159 SetFilePermissions(filename, PERMS_PRIVATE);
9162 static void SaveTapeExt(char *filename)
9166 tape.file_version = FILE_VERSION_ACTUAL;
9167 tape.game_version = GAME_VERSION_ACTUAL;
9169 tape.num_participating_players = 0;
9171 // count number of participating players
9172 for (i = 0; i < MAX_PLAYERS; i++)
9173 if (tape.player_participates[i])
9174 tape.num_participating_players++;
9176 SaveTapeToFilename(filename);
9178 tape.changed = FALSE;
9181 void SaveTape(int nr)
9183 char *filename = getTapeFilename(nr);
9185 InitTapeDirectory(leveldir_current->subdir);
9187 SaveTapeExt(filename);
9190 void SaveScoreTape(int nr)
9192 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9194 // used instead of "leveldir_current->subdir" (for network games)
9195 InitScoreTapeDirectory(levelset.identifier, nr);
9197 SaveTapeExt(filename);
9200 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9201 unsigned int req_state_added)
9203 char *filename = getTapeFilename(nr);
9204 boolean new_tape = !fileExists(filename);
9205 boolean tape_saved = FALSE;
9207 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9212 Request(msg_saved, REQ_CONFIRM | req_state_added);
9220 boolean SaveTapeChecked(int nr)
9222 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9225 boolean SaveTapeChecked_LevelSolved(int nr)
9227 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9228 "Level solved! Tape saved!", REQ_STAY_OPEN);
9231 void DumpTape(struct TapeInfo *tape)
9233 int tape_frame_counter;
9236 if (tape->no_valid_file)
9238 Warn("cannot dump -- no valid tape file found");
9245 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9246 tape->level_nr, tape->file_version, tape->game_version);
9247 Print(" (effective engine version %08d)\n",
9248 tape->engine_version);
9249 Print("Level series identifier: '%s'\n", tape->level_identifier);
9251 Print("Solution tape: %s\n",
9252 tape->solved ? "yes" :
9253 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9255 Print("Special tape properties: ");
9256 if (tape->property_bits == TAPE_PROPERTY_NONE)
9258 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9259 Print("[em_random_bug]");
9260 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9261 Print("[game_speed]");
9262 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9264 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9265 Print("[single_step]");
9266 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9267 Print("[snapshot]");
9268 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9269 Print("[replayed]");
9270 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9271 Print("[tas_keys]");
9272 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9273 Print("[small_graphics]");
9276 int year2 = tape->date / 10000;
9277 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9278 int month_index_raw = (tape->date / 100) % 100;
9279 int month_index = month_index_raw % 12; // prevent invalid index
9280 int month = month_index + 1;
9281 int day = tape->date % 100;
9283 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9287 tape_frame_counter = 0;
9289 for (i = 0; i < tape->length; i++)
9291 if (i >= MAX_TAPE_LEN)
9296 for (j = 0; j < MAX_PLAYERS; j++)
9298 if (tape->player_participates[j])
9300 int action = tape->pos[i].action[j];
9302 Print("%d:%02x ", j, action);
9303 Print("[%c%c%c%c|%c%c] - ",
9304 (action & JOY_LEFT ? '<' : ' '),
9305 (action & JOY_RIGHT ? '>' : ' '),
9306 (action & JOY_UP ? '^' : ' '),
9307 (action & JOY_DOWN ? 'v' : ' '),
9308 (action & JOY_BUTTON_1 ? '1' : ' '),
9309 (action & JOY_BUTTON_2 ? '2' : ' '));
9313 Print("(%03d) ", tape->pos[i].delay);
9314 Print("[%05d]\n", tape_frame_counter);
9316 tape_frame_counter += tape->pos[i].delay;
9322 void DumpTapes(void)
9324 static LevelDirTree *dumptape_leveldir = NULL;
9326 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9327 global.dumptape_leveldir);
9329 if (dumptape_leveldir == NULL)
9330 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9332 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9333 global.dumptape_level_nr > dumptape_leveldir->last_level)
9334 Fail("no such level number: %d", global.dumptape_level_nr);
9336 leveldir_current = dumptape_leveldir;
9338 if (options.mytapes)
9339 LoadTape(global.dumptape_level_nr);
9341 LoadSolutionTape(global.dumptape_level_nr);
9349 // ============================================================================
9350 // score file functions
9351 // ============================================================================
9353 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9357 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9359 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9360 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9361 scores->entry[i].score = 0;
9362 scores->entry[i].time = 0;
9364 scores->entry[i].id = -1;
9365 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9366 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9367 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9368 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9369 strcpy(scores->entry[i].country_code, "??");
9372 scores->num_entries = 0;
9373 scores->last_added = -1;
9374 scores->last_added_local = -1;
9376 scores->updated = FALSE;
9377 scores->uploaded = FALSE;
9378 scores->tape_downloaded = FALSE;
9379 scores->force_last_added = FALSE;
9381 // The following values are intentionally not reset here:
9385 // - continue_playing
9386 // - continue_on_return
9389 static void setScoreInfoToDefaults(void)
9391 setScoreInfoToDefaultsExt(&scores);
9394 static void setServerScoreInfoToDefaults(void)
9396 setScoreInfoToDefaultsExt(&server_scores);
9399 static void LoadScore_OLD(int nr)
9402 char *filename = getScoreFilename(nr);
9403 char cookie[MAX_LINE_LEN];
9404 char line[MAX_LINE_LEN];
9408 if (!(file = fopen(filename, MODE_READ)))
9411 // check file identifier
9412 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9414 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9415 cookie[strlen(cookie) - 1] = '\0';
9417 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9419 Warn("unknown format of score file '%s'", filename);
9426 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9428 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9429 Warn("fscanf() failed; %s", strerror(errno));
9431 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9434 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9435 line[strlen(line) - 1] = '\0';
9437 for (line_ptr = line; *line_ptr; line_ptr++)
9439 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9441 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9442 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9451 static void ConvertScore_OLD(void)
9453 // only convert score to time for levels that rate playing time over score
9454 if (!level.rate_time_over_score)
9457 // convert old score to playing time for score-less levels (like Supaplex)
9458 int time_final_max = 999;
9461 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9463 int score = scores.entry[i].score;
9465 if (score > 0 && score < time_final_max)
9466 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9470 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9472 scores->file_version = getFileVersion(file);
9473 scores->game_version = getFileVersion(file);
9478 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9480 char *level_identifier = NULL;
9481 int level_identifier_size;
9484 level_identifier_size = getFile16BitBE(file);
9486 level_identifier = checked_malloc(level_identifier_size);
9488 for (i = 0; i < level_identifier_size; i++)
9489 level_identifier[i] = getFile8Bit(file);
9491 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9492 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9494 checked_free(level_identifier);
9496 scores->level_nr = getFile16BitBE(file);
9497 scores->num_entries = getFile16BitBE(file);
9499 chunk_size = 2 + level_identifier_size + 2 + 2;
9504 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9508 for (i = 0; i < scores->num_entries; i++)
9510 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9511 scores->entry[i].name[j] = getFile8Bit(file);
9513 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9516 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9521 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9525 for (i = 0; i < scores->num_entries; i++)
9526 scores->entry[i].score = getFile16BitBE(file);
9528 chunk_size = scores->num_entries * 2;
9533 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9537 for (i = 0; i < scores->num_entries; i++)
9538 scores->entry[i].score = getFile32BitBE(file);
9540 chunk_size = scores->num_entries * 4;
9545 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9549 for (i = 0; i < scores->num_entries; i++)
9550 scores->entry[i].time = getFile32BitBE(file);
9552 chunk_size = scores->num_entries * 4;
9557 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9561 for (i = 0; i < scores->num_entries; i++)
9563 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9564 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9566 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9569 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9574 void LoadScore(int nr)
9576 char *filename = getScoreFilename(nr);
9577 char cookie[MAX_LINE_LEN];
9578 char chunk_name[CHUNK_ID_LEN + 1];
9580 boolean old_score_file_format = FALSE;
9583 // always start with reliable default values
9584 setScoreInfoToDefaults();
9586 if (!(file = openFile(filename, MODE_READ)))
9589 getFileChunkBE(file, chunk_name, NULL);
9590 if (strEqual(chunk_name, "RND1"))
9592 getFile32BitBE(file); // not used
9594 getFileChunkBE(file, chunk_name, NULL);
9595 if (!strEqual(chunk_name, "SCOR"))
9597 Warn("unknown format of score file '%s'", filename);
9604 else // check for old file format with cookie string
9606 strcpy(cookie, chunk_name);
9607 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9609 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9610 cookie[strlen(cookie) - 1] = '\0';
9612 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9614 Warn("unknown format of score file '%s'", filename);
9621 old_score_file_format = TRUE;
9624 if (old_score_file_format)
9626 // score files from versions before 4.2.4.0 without chunk structure
9629 // convert score to time, if possible (mainly for Supaplex levels)
9638 int (*loader)(File *, int, struct ScoreInfo *);
9642 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9643 { "INFO", -1, LoadScore_INFO },
9644 { "NAME", -1, LoadScore_NAME },
9645 { "SCOR", -1, LoadScore_SCOR },
9646 { "SC4R", -1, LoadScore_SC4R },
9647 { "TIME", -1, LoadScore_TIME },
9648 { "TAPE", -1, LoadScore_TAPE },
9653 while (getFileChunkBE(file, chunk_name, &chunk_size))
9657 while (chunk_info[i].name != NULL &&
9658 !strEqual(chunk_name, chunk_info[i].name))
9661 if (chunk_info[i].name == NULL)
9663 Warn("unknown chunk '%s' in score file '%s'",
9664 chunk_name, filename);
9666 ReadUnusedBytesFromFile(file, chunk_size);
9668 else if (chunk_info[i].size != -1 &&
9669 chunk_info[i].size != chunk_size)
9671 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9672 chunk_size, chunk_name, filename);
9674 ReadUnusedBytesFromFile(file, chunk_size);
9678 // call function to load this score chunk
9679 int chunk_size_expected =
9680 (chunk_info[i].loader)(file, chunk_size, &scores);
9682 // the size of some chunks cannot be checked before reading other
9683 // chunks first (like "HEAD" and "BODY") that contain some header
9684 // information, so check them here
9685 if (chunk_size_expected != chunk_size)
9687 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9688 chunk_size, chunk_name, filename);
9697 #if ENABLE_HISTORIC_CHUNKS
9698 void SaveScore_OLD(int nr)
9701 char *filename = getScoreFilename(nr);
9704 // used instead of "leveldir_current->subdir" (for network games)
9705 InitScoreDirectory(levelset.identifier);
9707 if (!(file = fopen(filename, MODE_WRITE)))
9709 Warn("cannot save score for level %d", nr);
9714 fprintf(file, "%s\n\n", SCORE_COOKIE);
9716 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9717 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9721 SetFilePermissions(filename, PERMS_PRIVATE);
9725 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9727 putFileVersion(file, scores->file_version);
9728 putFileVersion(file, scores->game_version);
9731 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9733 int level_identifier_size = strlen(scores->level_identifier) + 1;
9736 putFile16BitBE(file, level_identifier_size);
9738 for (i = 0; i < level_identifier_size; i++)
9739 putFile8Bit(file, scores->level_identifier[i]);
9741 putFile16BitBE(file, scores->level_nr);
9742 putFile16BitBE(file, scores->num_entries);
9745 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9749 for (i = 0; i < scores->num_entries; i++)
9751 int name_size = strlen(scores->entry[i].name);
9753 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9754 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9758 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9762 for (i = 0; i < scores->num_entries; i++)
9763 putFile16BitBE(file, scores->entry[i].score);
9766 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9770 for (i = 0; i < scores->num_entries; i++)
9771 putFile32BitBE(file, scores->entry[i].score);
9774 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9778 for (i = 0; i < scores->num_entries; i++)
9779 putFile32BitBE(file, scores->entry[i].time);
9782 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9786 for (i = 0; i < scores->num_entries; i++)
9788 int size = strlen(scores->entry[i].tape_basename);
9790 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9791 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9795 static void SaveScoreToFilename(char *filename)
9798 int info_chunk_size;
9799 int name_chunk_size;
9800 int scor_chunk_size;
9801 int sc4r_chunk_size;
9802 int time_chunk_size;
9803 int tape_chunk_size;
9804 boolean has_large_score_values;
9807 if (!(file = fopen(filename, MODE_WRITE)))
9809 Warn("cannot save score file '%s'", filename);
9814 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9815 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9816 scor_chunk_size = scores.num_entries * 2;
9817 sc4r_chunk_size = scores.num_entries * 4;
9818 time_chunk_size = scores.num_entries * 4;
9819 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9821 has_large_score_values = FALSE;
9822 for (i = 0; i < scores.num_entries; i++)
9823 if (scores.entry[i].score > 0xffff)
9824 has_large_score_values = TRUE;
9826 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9827 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9829 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9830 SaveScore_VERS(file, &scores);
9832 putFileChunkBE(file, "INFO", info_chunk_size);
9833 SaveScore_INFO(file, &scores);
9835 putFileChunkBE(file, "NAME", name_chunk_size);
9836 SaveScore_NAME(file, &scores);
9838 if (has_large_score_values)
9840 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9841 SaveScore_SC4R(file, &scores);
9845 putFileChunkBE(file, "SCOR", scor_chunk_size);
9846 SaveScore_SCOR(file, &scores);
9849 putFileChunkBE(file, "TIME", time_chunk_size);
9850 SaveScore_TIME(file, &scores);
9852 putFileChunkBE(file, "TAPE", tape_chunk_size);
9853 SaveScore_TAPE(file, &scores);
9857 SetFilePermissions(filename, PERMS_PRIVATE);
9860 void SaveScore(int nr)
9862 char *filename = getScoreFilename(nr);
9865 // used instead of "leveldir_current->subdir" (for network games)
9866 InitScoreDirectory(levelset.identifier);
9868 scores.file_version = FILE_VERSION_ACTUAL;
9869 scores.game_version = GAME_VERSION_ACTUAL;
9871 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9872 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9873 scores.level_nr = level_nr;
9875 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9876 if (scores.entry[i].score == 0 &&
9877 scores.entry[i].time == 0 &&
9878 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9881 scores.num_entries = i;
9883 if (scores.num_entries == 0)
9886 SaveScoreToFilename(filename);
9889 static void LoadServerScoreFromCache(int nr)
9891 struct ScoreEntry score_entry;
9900 { &score_entry.score, FALSE, 0 },
9901 { &score_entry.time, FALSE, 0 },
9902 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9903 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9904 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9905 { &score_entry.id, FALSE, 0 },
9906 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9907 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9908 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9909 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9913 char *filename = getScoreCacheFilename(nr);
9914 SetupFileHash *score_hash = loadSetupFileHash(filename);
9917 server_scores.num_entries = 0;
9919 if (score_hash == NULL)
9922 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9924 score_entry = server_scores.entry[i];
9926 for (j = 0; score_mapping[j].value != NULL; j++)
9930 sprintf(token, "%02d.%d", i, j);
9932 char *value = getHashEntry(score_hash, token);
9937 if (score_mapping[j].is_string)
9939 char *score_value = (char *)score_mapping[j].value;
9940 int value_size = score_mapping[j].string_size;
9942 strncpy(score_value, value, value_size);
9943 score_value[value_size] = '\0';
9947 int *score_value = (int *)score_mapping[j].value;
9949 *score_value = atoi(value);
9952 server_scores.num_entries = i + 1;
9955 server_scores.entry[i] = score_entry;
9958 freeSetupFileHash(score_hash);
9961 void LoadServerScore(int nr, boolean download_score)
9963 if (!setup.use_api_server)
9966 // always start with reliable default values
9967 setServerScoreInfoToDefaults();
9969 // 1st step: load server scores from cache file (which may not exist)
9970 // (this should prevent reading it while the thread is writing to it)
9971 LoadServerScoreFromCache(nr);
9973 if (download_score && runtime.use_api_server)
9975 // 2nd step: download server scores from score server to cache file
9976 // (as thread, as it might time out if the server is not reachable)
9977 ApiGetScoreAsThread(nr);
9981 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9983 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9985 // if score tape not uploaded, ask for uploading missing tapes later
9986 if (!setup.has_remaining_tapes)
9987 setup.ask_for_remaining_tapes = TRUE;
9989 setup.provide_uploading_tapes = TRUE;
9990 setup.has_remaining_tapes = TRUE;
9992 SaveSetup_ServerSetup();
9995 void SaveServerScore(int nr, boolean tape_saved)
9997 if (!runtime.use_api_server)
9999 PrepareScoreTapesForUpload(leveldir_current->subdir);
10004 ApiAddScoreAsThread(nr, tape_saved, NULL);
10007 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10008 char *score_tape_filename)
10010 if (!runtime.use_api_server)
10013 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10016 void LoadLocalAndServerScore(int nr, boolean download_score)
10018 int last_added_local = scores.last_added_local;
10019 boolean force_last_added = scores.force_last_added;
10021 // needed if only showing server scores
10022 setScoreInfoToDefaults();
10024 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10027 // restore last added local score entry (before merging server scores)
10028 scores.last_added = scores.last_added_local = last_added_local;
10030 if (setup.use_api_server &&
10031 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10033 // load server scores from cache file and trigger update from server
10034 LoadServerScore(nr, download_score);
10036 // merge local scores with scores from server
10037 MergeServerScore();
10040 if (force_last_added)
10041 scores.force_last_added = force_last_added;
10045 // ============================================================================
10046 // setup file functions
10047 // ============================================================================
10049 #define TOKEN_STR_PLAYER_PREFIX "player_"
10052 static struct TokenInfo global_setup_tokens[] =
10056 &setup.player_name, "player_name"
10060 &setup.multiple_users, "multiple_users"
10064 &setup.sound, "sound"
10068 &setup.sound_loops, "repeating_sound_loops"
10072 &setup.sound_music, "background_music"
10076 &setup.sound_simple, "simple_sound_effects"
10080 &setup.toons, "toons"
10084 &setup.global_animations, "global_animations"
10088 &setup.scroll_delay, "scroll_delay"
10092 &setup.forced_scroll_delay, "forced_scroll_delay"
10096 &setup.scroll_delay_value, "scroll_delay_value"
10100 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10104 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10108 &setup.fade_screens, "fade_screens"
10112 &setup.autorecord, "automatic_tape_recording"
10116 &setup.autorecord_after_replay, "autorecord_after_replay"
10120 &setup.auto_pause_on_start, "auto_pause_on_start"
10124 &setup.show_titlescreen, "show_titlescreen"
10128 &setup.quick_doors, "quick_doors"
10132 &setup.team_mode, "team_mode"
10136 &setup.handicap, "handicap"
10140 &setup.skip_levels, "skip_levels"
10144 &setup.increment_levels, "increment_levels"
10148 &setup.auto_play_next_level, "auto_play_next_level"
10152 &setup.count_score_after_game, "count_score_after_game"
10156 &setup.show_scores_after_game, "show_scores_after_game"
10160 &setup.time_limit, "time_limit"
10164 &setup.fullscreen, "fullscreen"
10168 &setup.window_scaling_percent, "window_scaling_percent"
10172 &setup.window_scaling_quality, "window_scaling_quality"
10176 &setup.screen_rendering_mode, "screen_rendering_mode"
10180 &setup.vsync_mode, "vsync_mode"
10184 &setup.ask_on_escape, "ask_on_escape"
10188 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10192 &setup.ask_on_game_over, "ask_on_game_over"
10196 &setup.ask_on_quit_game, "ask_on_quit_game"
10200 &setup.ask_on_quit_program, "ask_on_quit_program"
10204 &setup.quick_switch, "quick_player_switch"
10208 &setup.input_on_focus, "input_on_focus"
10212 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10216 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10220 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10224 &setup.game_speed_extended, "game_speed_extended"
10228 &setup.game_frame_delay, "game_frame_delay"
10232 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10236 &setup.bd_skip_hatching, "bd_skip_hatching"
10240 &setup.bd_scroll_delay, "bd_scroll_delay"
10244 &setup.bd_smooth_movements, "bd_smooth_movements"
10248 &setup.sp_show_border_elements, "sp_show_border_elements"
10252 &setup.small_game_graphics, "small_game_graphics"
10256 &setup.show_load_save_buttons, "show_load_save_buttons"
10260 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10264 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10268 &setup.graphics_set, "graphics_set"
10272 &setup.sounds_set, "sounds_set"
10276 &setup.music_set, "music_set"
10280 &setup.override_level_graphics, "override_level_graphics"
10284 &setup.override_level_sounds, "override_level_sounds"
10288 &setup.override_level_music, "override_level_music"
10292 &setup.volume_simple, "volume_simple"
10296 &setup.volume_loops, "volume_loops"
10300 &setup.volume_music, "volume_music"
10304 &setup.network_mode, "network_mode"
10308 &setup.network_player_nr, "network_player"
10312 &setup.network_server_hostname, "network_server_hostname"
10316 &setup.touch.control_type, "touch.control_type"
10320 &setup.touch.move_distance, "touch.move_distance"
10324 &setup.touch.drop_distance, "touch.drop_distance"
10328 &setup.touch.transparency, "touch.transparency"
10332 &setup.touch.draw_outlined, "touch.draw_outlined"
10336 &setup.touch.draw_pressed, "touch.draw_pressed"
10340 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10344 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10348 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10352 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10356 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10360 static struct TokenInfo auto_setup_tokens[] =
10364 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10368 static struct TokenInfo server_setup_tokens[] =
10372 &setup.player_uuid, "player_uuid"
10376 &setup.player_version, "player_version"
10380 &setup.use_api_server, TEST_PREFIX "use_api_server"
10384 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10388 &setup.api_server_password, TEST_PREFIX "api_server_password"
10392 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10396 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10400 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10404 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10408 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10412 static struct TokenInfo editor_setup_tokens[] =
10416 &setup.editor.el_classic, "editor.el_classic"
10420 &setup.editor.el_custom, "editor.el_custom"
10424 &setup.editor.el_user_defined, "editor.el_user_defined"
10428 &setup.editor.el_dynamic, "editor.el_dynamic"
10432 &setup.editor.el_headlines, "editor.el_headlines"
10436 &setup.editor.show_element_token, "editor.show_element_token"
10440 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10444 static struct TokenInfo editor_cascade_setup_tokens[] =
10448 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10452 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10456 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10460 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10464 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10468 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10472 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10476 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10480 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10484 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10488 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10492 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10496 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10500 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10504 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10508 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10512 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10516 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10520 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10524 static struct TokenInfo shortcut_setup_tokens[] =
10528 &setup.shortcut.save_game, "shortcut.save_game"
10532 &setup.shortcut.load_game, "shortcut.load_game"
10536 &setup.shortcut.restart_game, "shortcut.restart_game"
10540 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10544 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10548 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10552 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10556 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10560 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10564 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10568 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10572 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10576 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10580 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10584 &setup.shortcut.tape_record, "shortcut.tape_record"
10588 &setup.shortcut.tape_play, "shortcut.tape_play"
10592 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10596 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10600 &setup.shortcut.sound_music, "shortcut.sound_music"
10604 &setup.shortcut.snap_left, "shortcut.snap_left"
10608 &setup.shortcut.snap_right, "shortcut.snap_right"
10612 &setup.shortcut.snap_up, "shortcut.snap_up"
10616 &setup.shortcut.snap_down, "shortcut.snap_down"
10620 static struct SetupInputInfo setup_input;
10621 static struct TokenInfo player_setup_tokens[] =
10625 &setup_input.use_joystick, ".use_joystick"
10629 &setup_input.joy.device_name, ".joy.device_name"
10633 &setup_input.joy.xleft, ".joy.xleft"
10637 &setup_input.joy.xmiddle, ".joy.xmiddle"
10641 &setup_input.joy.xright, ".joy.xright"
10645 &setup_input.joy.yupper, ".joy.yupper"
10649 &setup_input.joy.ymiddle, ".joy.ymiddle"
10653 &setup_input.joy.ylower, ".joy.ylower"
10657 &setup_input.joy.snap, ".joy.snap_field"
10661 &setup_input.joy.drop, ".joy.place_bomb"
10665 &setup_input.key.left, ".key.move_left"
10669 &setup_input.key.right, ".key.move_right"
10673 &setup_input.key.up, ".key.move_up"
10677 &setup_input.key.down, ".key.move_down"
10681 &setup_input.key.snap, ".key.snap_field"
10685 &setup_input.key.drop, ".key.place_bomb"
10689 static struct TokenInfo system_setup_tokens[] =
10693 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10697 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10701 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10705 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10709 static struct TokenInfo internal_setup_tokens[] =
10713 &setup.internal.program_title, "program_title"
10717 &setup.internal.program_version, "program_version"
10721 &setup.internal.program_author, "program_author"
10725 &setup.internal.program_email, "program_email"
10729 &setup.internal.program_website, "program_website"
10733 &setup.internal.program_copyright, "program_copyright"
10737 &setup.internal.program_company, "program_company"
10741 &setup.internal.program_icon_file, "program_icon_file"
10745 &setup.internal.default_graphics_set, "default_graphics_set"
10749 &setup.internal.default_sounds_set, "default_sounds_set"
10753 &setup.internal.default_music_set, "default_music_set"
10757 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10761 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10765 &setup.internal.fallback_music_file, "fallback_music_file"
10769 &setup.internal.default_level_series, "default_level_series"
10773 &setup.internal.default_window_width, "default_window_width"
10777 &setup.internal.default_window_height, "default_window_height"
10781 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10785 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10789 &setup.internal.create_user_levelset, "create_user_levelset"
10793 &setup.internal.info_screens_from_main, "info_screens_from_main"
10797 &setup.internal.menu_game, "menu_game"
10801 &setup.internal.menu_engines, "menu_engines"
10805 &setup.internal.menu_editor, "menu_editor"
10809 &setup.internal.menu_graphics, "menu_graphics"
10813 &setup.internal.menu_sound, "menu_sound"
10817 &setup.internal.menu_artwork, "menu_artwork"
10821 &setup.internal.menu_input, "menu_input"
10825 &setup.internal.menu_touch, "menu_touch"
10829 &setup.internal.menu_shortcuts, "menu_shortcuts"
10833 &setup.internal.menu_exit, "menu_exit"
10837 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10841 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10845 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10849 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10853 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10857 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10861 &setup.internal.info_title, "info_title"
10865 &setup.internal.info_elements, "info_elements"
10869 &setup.internal.info_music, "info_music"
10873 &setup.internal.info_credits, "info_credits"
10877 &setup.internal.info_program, "info_program"
10881 &setup.internal.info_version, "info_version"
10885 &setup.internal.info_levelset, "info_levelset"
10889 &setup.internal.info_exit, "info_exit"
10893 static struct TokenInfo debug_setup_tokens[] =
10897 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10901 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10905 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10909 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10913 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10917 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10921 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10925 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10929 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10933 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10937 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10941 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10945 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10949 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10953 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10957 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10961 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10965 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10969 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10973 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10977 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10980 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10984 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10988 &setup.debug.xsn_mode, "debug.xsn_mode"
10992 &setup.debug.xsn_percent, "debug.xsn_percent"
10996 static struct TokenInfo options_setup_tokens[] =
11000 &setup.options.verbose, "options.verbose"
11004 &setup.options.debug, "options.debug"
11008 &setup.options.debug_mode, "options.debug_mode"
11012 static void setSetupInfoToDefaults(struct SetupInfo *si)
11016 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11018 si->multiple_users = TRUE;
11021 si->sound_loops = TRUE;
11022 si->sound_music = TRUE;
11023 si->sound_simple = TRUE;
11025 si->global_animations = TRUE;
11026 si->scroll_delay = TRUE;
11027 si->forced_scroll_delay = FALSE;
11028 si->scroll_delay_value = STD_SCROLL_DELAY;
11029 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11030 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11031 si->fade_screens = TRUE;
11032 si->autorecord = TRUE;
11033 si->autorecord_after_replay = TRUE;
11034 si->auto_pause_on_start = FALSE;
11035 si->show_titlescreen = TRUE;
11036 si->quick_doors = FALSE;
11037 si->team_mode = FALSE;
11038 si->handicap = TRUE;
11039 si->skip_levels = TRUE;
11040 si->increment_levels = TRUE;
11041 si->auto_play_next_level = TRUE;
11042 si->count_score_after_game = TRUE;
11043 si->show_scores_after_game = TRUE;
11044 si->time_limit = TRUE;
11045 si->fullscreen = FALSE;
11046 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11047 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11048 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11049 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11050 si->ask_on_escape = TRUE;
11051 si->ask_on_escape_editor = TRUE;
11052 si->ask_on_game_over = TRUE;
11053 si->ask_on_quit_game = TRUE;
11054 si->ask_on_quit_program = TRUE;
11055 si->quick_switch = FALSE;
11056 si->input_on_focus = FALSE;
11057 si->prefer_aga_graphics = TRUE;
11058 si->prefer_lowpass_sounds = FALSE;
11059 si->prefer_extra_panel_items = TRUE;
11060 si->game_speed_extended = FALSE;
11061 si->game_frame_delay = GAME_FRAME_DELAY;
11062 si->bd_skip_uncovering = FALSE;
11063 si->bd_skip_hatching = FALSE;
11064 si->bd_scroll_delay = TRUE;
11065 si->bd_smooth_movements = AUTO;
11066 si->sp_show_border_elements = FALSE;
11067 si->small_game_graphics = FALSE;
11068 si->show_load_save_buttons = FALSE;
11069 si->show_undo_redo_buttons = FALSE;
11070 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11072 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11073 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11074 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11076 si->override_level_graphics = FALSE;
11077 si->override_level_sounds = FALSE;
11078 si->override_level_music = FALSE;
11080 si->volume_simple = 100; // percent
11081 si->volume_loops = 100; // percent
11082 si->volume_music = 100; // percent
11084 si->network_mode = FALSE;
11085 si->network_player_nr = 0; // first player
11086 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11088 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11089 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11090 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11091 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11092 si->touch.draw_outlined = TRUE;
11093 si->touch.draw_pressed = TRUE;
11095 for (i = 0; i < 2; i++)
11097 char *default_grid_button[6][2] =
11103 { "111222", " vv " },
11104 { "111222", " vv " }
11106 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11107 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11108 int min_xsize = MIN(6, grid_xsize);
11109 int min_ysize = MIN(6, grid_ysize);
11110 int startx = grid_xsize - min_xsize;
11111 int starty = grid_ysize - min_ysize;
11114 // virtual buttons grid can only be set to defaults if video is initialized
11115 // (this will be repeated if virtual buttons are not loaded from setup file)
11116 if (video.initialized)
11118 si->touch.grid_xsize[i] = grid_xsize;
11119 si->touch.grid_ysize[i] = grid_ysize;
11123 si->touch.grid_xsize[i] = -1;
11124 si->touch.grid_ysize[i] = -1;
11127 for (x = 0; x < MAX_GRID_XSIZE; x++)
11128 for (y = 0; y < MAX_GRID_YSIZE; y++)
11129 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11131 for (x = 0; x < min_xsize; x++)
11132 for (y = 0; y < min_ysize; y++)
11133 si->touch.grid_button[i][x][starty + y] =
11134 default_grid_button[y][0][x];
11136 for (x = 0; x < min_xsize; x++)
11137 for (y = 0; y < min_ysize; y++)
11138 si->touch.grid_button[i][startx + x][starty + y] =
11139 default_grid_button[y][1][x];
11142 si->touch.grid_initialized = video.initialized;
11144 si->touch.overlay_buttons = FALSE;
11146 si->editor.el_boulderdash = TRUE;
11147 si->editor.el_boulderdash_native = TRUE;
11148 si->editor.el_emerald_mine = TRUE;
11149 si->editor.el_emerald_mine_club = TRUE;
11150 si->editor.el_more = TRUE;
11151 si->editor.el_sokoban = TRUE;
11152 si->editor.el_supaplex = TRUE;
11153 si->editor.el_diamond_caves = TRUE;
11154 si->editor.el_dx_boulderdash = TRUE;
11156 si->editor.el_mirror_magic = TRUE;
11157 si->editor.el_deflektor = TRUE;
11159 si->editor.el_chars = TRUE;
11160 si->editor.el_steel_chars = TRUE;
11162 si->editor.el_classic = TRUE;
11163 si->editor.el_custom = TRUE;
11165 si->editor.el_user_defined = FALSE;
11166 si->editor.el_dynamic = TRUE;
11168 si->editor.el_headlines = TRUE;
11170 si->editor.show_element_token = FALSE;
11172 si->editor.show_read_only_warning = TRUE;
11174 si->editor.use_template_for_new_levels = TRUE;
11176 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11177 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11178 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11179 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11180 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11182 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11183 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11184 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11185 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11186 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11188 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11189 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11190 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11191 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11192 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11193 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11195 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11196 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11197 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11199 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11200 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11201 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11202 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11204 for (i = 0; i < MAX_PLAYERS; i++)
11206 si->input[i].use_joystick = FALSE;
11207 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11208 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11209 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11210 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11211 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11212 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11213 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11214 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11215 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11216 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11217 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11218 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11219 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11220 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11221 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11224 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11225 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11226 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11227 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11229 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11230 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11231 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11232 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11233 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11234 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11235 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11237 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11239 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11240 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11241 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11243 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11244 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11245 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11247 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11248 si->internal.choose_from_top_leveldir = FALSE;
11249 si->internal.show_scaling_in_title = TRUE;
11250 si->internal.create_user_levelset = TRUE;
11251 si->internal.info_screens_from_main = FALSE;
11253 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11254 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11256 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11257 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11258 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11259 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11260 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11261 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11262 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11263 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11264 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11265 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11267 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11268 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11269 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11270 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11271 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11272 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11273 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11274 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11275 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11276 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11278 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11279 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11281 si->debug.show_frames_per_second = FALSE;
11283 si->debug.xsn_mode = AUTO;
11284 si->debug.xsn_percent = 0;
11286 si->options.verbose = FALSE;
11287 si->options.debug = FALSE;
11288 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11290 #if defined(PLATFORM_ANDROID)
11291 si->fullscreen = TRUE;
11292 si->touch.overlay_buttons = TRUE;
11295 setHideSetupEntry(&setup.debug.xsn_mode);
11298 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11300 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11303 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11305 si->player_uuid = NULL; // (will be set later)
11306 si->player_version = 1; // (will be set later)
11308 si->use_api_server = TRUE;
11309 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11310 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11311 si->ask_for_uploading_tapes = TRUE;
11312 si->ask_for_remaining_tapes = FALSE;
11313 si->provide_uploading_tapes = TRUE;
11314 si->ask_for_using_api_server = TRUE;
11315 si->has_remaining_tapes = FALSE;
11318 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11320 si->editor_cascade.el_bd = TRUE;
11321 si->editor_cascade.el_bd_native = TRUE;
11322 si->editor_cascade.el_em = TRUE;
11323 si->editor_cascade.el_emc = TRUE;
11324 si->editor_cascade.el_rnd = TRUE;
11325 si->editor_cascade.el_sb = TRUE;
11326 si->editor_cascade.el_sp = TRUE;
11327 si->editor_cascade.el_dc = TRUE;
11328 si->editor_cascade.el_dx = TRUE;
11330 si->editor_cascade.el_mm = TRUE;
11331 si->editor_cascade.el_df = TRUE;
11333 si->editor_cascade.el_chars = FALSE;
11334 si->editor_cascade.el_steel_chars = FALSE;
11335 si->editor_cascade.el_ce = FALSE;
11336 si->editor_cascade.el_ge = FALSE;
11337 si->editor_cascade.el_es = FALSE;
11338 si->editor_cascade.el_ref = FALSE;
11339 si->editor_cascade.el_user = FALSE;
11340 si->editor_cascade.el_dynamic = FALSE;
11343 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11345 static char *getHideSetupToken(void *setup_value)
11347 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11349 if (setup_value != NULL)
11350 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11352 return hide_setup_token;
11355 void setHideSetupEntry(void *setup_value)
11357 char *hide_setup_token = getHideSetupToken(setup_value);
11359 if (hide_setup_hash == NULL)
11360 hide_setup_hash = newSetupFileHash();
11362 if (setup_value != NULL)
11363 setHashEntry(hide_setup_hash, hide_setup_token, "");
11366 void removeHideSetupEntry(void *setup_value)
11368 char *hide_setup_token = getHideSetupToken(setup_value);
11370 if (setup_value != NULL)
11371 removeHashEntry(hide_setup_hash, hide_setup_token);
11374 boolean hideSetupEntry(void *setup_value)
11376 char *hide_setup_token = getHideSetupToken(setup_value);
11378 return (setup_value != NULL &&
11379 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11382 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11383 struct TokenInfo *token_info,
11384 int token_nr, char *token_text)
11386 char *token_hide_text = getStringCat2(token_text, ".hide");
11387 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11389 // set the value of this setup option in the setup option structure
11390 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11392 // check if this setup option should be hidden in the setup menu
11393 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11394 setHideSetupEntry(token_info[token_nr].value);
11396 free(token_hide_text);
11399 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11400 struct TokenInfo *token_info,
11403 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11404 token_info[token_nr].text);
11407 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11411 if (!setup_file_hash)
11414 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11415 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11417 setup.touch.grid_initialized = TRUE;
11418 for (i = 0; i < 2; i++)
11420 int grid_xsize = setup.touch.grid_xsize[i];
11421 int grid_ysize = setup.touch.grid_ysize[i];
11424 // if virtual buttons are not loaded from setup file, repeat initializing
11425 // virtual buttons grid with default values later when video is initialized
11426 if (grid_xsize == -1 ||
11429 setup.touch.grid_initialized = FALSE;
11434 for (y = 0; y < grid_ysize; y++)
11436 char token_string[MAX_LINE_LEN];
11438 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11440 char *value_string = getHashEntry(setup_file_hash, token_string);
11442 if (value_string == NULL)
11445 for (x = 0; x < grid_xsize; x++)
11447 char c = value_string[x];
11449 setup.touch.grid_button[i][x][y] =
11450 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11455 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11456 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11458 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11459 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11461 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11465 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11467 setup_input = setup.input[pnr];
11468 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11470 char full_token[100];
11472 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11473 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11476 setup.input[pnr] = setup_input;
11479 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11480 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11482 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11483 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11485 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11486 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11488 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11489 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11491 setHideRelatedSetupEntries();
11494 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11498 if (!setup_file_hash)
11501 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11502 setSetupInfo(auto_setup_tokens, i,
11503 getHashEntry(setup_file_hash,
11504 auto_setup_tokens[i].text));
11507 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11511 if (!setup_file_hash)
11514 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11515 setSetupInfo(server_setup_tokens, i,
11516 getHashEntry(setup_file_hash,
11517 server_setup_tokens[i].text));
11520 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11524 if (!setup_file_hash)
11527 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11528 setSetupInfo(editor_cascade_setup_tokens, i,
11529 getHashEntry(setup_file_hash,
11530 editor_cascade_setup_tokens[i].text));
11533 void LoadUserNames(void)
11535 int last_user_nr = user.nr;
11538 if (global.user_names != NULL)
11540 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11541 checked_free(global.user_names[i]);
11543 checked_free(global.user_names);
11546 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11548 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11552 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11554 if (setup_file_hash)
11556 char *player_name = getHashEntry(setup_file_hash, "player_name");
11558 global.user_names[i] = getFixedUserName(player_name);
11560 freeSetupFileHash(setup_file_hash);
11563 if (global.user_names[i] == NULL)
11564 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11567 user.nr = last_user_nr;
11570 void LoadSetupFromFilename(char *filename)
11572 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11574 if (setup_file_hash)
11576 decodeSetupFileHash_Default(setup_file_hash);
11578 freeSetupFileHash(setup_file_hash);
11582 Debug("setup", "using default setup values");
11586 static void LoadSetup_SpecialPostProcessing(void)
11588 char *player_name_new;
11590 // needed to work around problems with fixed length strings
11591 player_name_new = getFixedUserName(setup.player_name);
11592 free(setup.player_name);
11593 setup.player_name = player_name_new;
11595 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11596 if (setup.scroll_delay == FALSE)
11598 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11599 setup.scroll_delay = TRUE; // now always "on"
11602 // make sure that scroll delay value stays inside valid range
11603 setup.scroll_delay_value =
11604 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11607 void LoadSetup_Default(void)
11611 // always start with reliable default values
11612 setSetupInfoToDefaults(&setup);
11614 // try to load setup values from default setup file
11615 filename = getDefaultSetupFilename();
11617 if (fileExists(filename))
11618 LoadSetupFromFilename(filename);
11620 // try to load setup values from platform setup file
11621 filename = getPlatformSetupFilename();
11623 if (fileExists(filename))
11624 LoadSetupFromFilename(filename);
11626 // try to load setup values from user setup file
11627 filename = getSetupFilename();
11629 LoadSetupFromFilename(filename);
11631 LoadSetup_SpecialPostProcessing();
11634 void LoadSetup_AutoSetup(void)
11636 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11637 SetupFileHash *setup_file_hash = NULL;
11639 // always start with reliable default values
11640 setSetupInfoToDefaults_AutoSetup(&setup);
11642 setup_file_hash = loadSetupFileHash(filename);
11644 if (setup_file_hash)
11646 decodeSetupFileHash_AutoSetup(setup_file_hash);
11648 freeSetupFileHash(setup_file_hash);
11654 void LoadSetup_ServerSetup(void)
11656 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11657 SetupFileHash *setup_file_hash = NULL;
11659 // always start with reliable default values
11660 setSetupInfoToDefaults_ServerSetup(&setup);
11662 setup_file_hash = loadSetupFileHash(filename);
11664 if (setup_file_hash)
11666 decodeSetupFileHash_ServerSetup(setup_file_hash);
11668 freeSetupFileHash(setup_file_hash);
11673 if (setup.player_uuid == NULL)
11675 // player UUID does not yet exist in setup file
11676 setup.player_uuid = getStringCopy(getUUID());
11677 setup.player_version = 2;
11679 SaveSetup_ServerSetup();
11683 void LoadSetup_EditorCascade(void)
11685 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11686 SetupFileHash *setup_file_hash = NULL;
11688 // always start with reliable default values
11689 setSetupInfoToDefaults_EditorCascade(&setup);
11691 setup_file_hash = loadSetupFileHash(filename);
11693 if (setup_file_hash)
11695 decodeSetupFileHash_EditorCascade(setup_file_hash);
11697 freeSetupFileHash(setup_file_hash);
11703 void LoadSetup(void)
11705 LoadSetup_Default();
11706 LoadSetup_AutoSetup();
11707 LoadSetup_ServerSetup();
11708 LoadSetup_EditorCascade();
11711 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11712 char *mapping_line)
11714 char mapping_guid[MAX_LINE_LEN];
11715 char *mapping_start, *mapping_end;
11717 // get GUID from game controller mapping line: copy complete line
11718 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11719 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11721 // get GUID from game controller mapping line: cut after GUID part
11722 mapping_start = strchr(mapping_guid, ',');
11723 if (mapping_start != NULL)
11724 *mapping_start = '\0';
11726 // cut newline from game controller mapping line
11727 mapping_end = strchr(mapping_line, '\n');
11728 if (mapping_end != NULL)
11729 *mapping_end = '\0';
11731 // add mapping entry to game controller mappings hash
11732 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11735 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11740 if (!(file = fopen(filename, MODE_READ)))
11742 Warn("cannot read game controller mappings file '%s'", filename);
11747 while (!feof(file))
11749 char line[MAX_LINE_LEN];
11751 if (!fgets(line, MAX_LINE_LEN, file))
11754 addGameControllerMappingToHash(mappings_hash, line);
11760 void SaveSetup_Default(void)
11762 char *filename = getSetupFilename();
11766 InitUserDataDirectory();
11768 if (!(file = fopen(filename, MODE_WRITE)))
11770 Warn("cannot write setup file '%s'", filename);
11775 fprintFileHeader(file, SETUP_FILENAME);
11777 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11779 // just to make things nicer :)
11780 if (global_setup_tokens[i].value == &setup.multiple_users ||
11781 global_setup_tokens[i].value == &setup.sound ||
11782 global_setup_tokens[i].value == &setup.graphics_set ||
11783 global_setup_tokens[i].value == &setup.volume_simple ||
11784 global_setup_tokens[i].value == &setup.network_mode ||
11785 global_setup_tokens[i].value == &setup.touch.control_type ||
11786 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11787 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11788 fprintf(file, "\n");
11790 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11793 for (i = 0; i < 2; i++)
11795 int grid_xsize = setup.touch.grid_xsize[i];
11796 int grid_ysize = setup.touch.grid_ysize[i];
11799 fprintf(file, "\n");
11801 for (y = 0; y < grid_ysize; y++)
11803 char token_string[MAX_LINE_LEN];
11804 char value_string[MAX_LINE_LEN];
11806 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11808 for (x = 0; x < grid_xsize; x++)
11810 char c = setup.touch.grid_button[i][x][y];
11812 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11815 value_string[grid_xsize] = '\0';
11817 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11821 fprintf(file, "\n");
11822 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11823 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11825 fprintf(file, "\n");
11826 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11827 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11829 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11833 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11834 fprintf(file, "\n");
11836 setup_input = setup.input[pnr];
11837 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11838 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11841 fprintf(file, "\n");
11842 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11843 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11845 // (internal setup values not saved to user setup file)
11847 fprintf(file, "\n");
11848 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11849 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11850 setup.debug.xsn_mode != AUTO)
11851 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11853 fprintf(file, "\n");
11854 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11855 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11859 SetFilePermissions(filename, PERMS_PRIVATE);
11862 void SaveSetup_AutoSetup(void)
11864 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11868 InitUserDataDirectory();
11870 if (!(file = fopen(filename, MODE_WRITE)))
11872 Warn("cannot write auto setup file '%s'", filename);
11879 fprintFileHeader(file, AUTOSETUP_FILENAME);
11881 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11882 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11886 SetFilePermissions(filename, PERMS_PRIVATE);
11891 void SaveSetup_ServerSetup(void)
11893 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11897 InitUserDataDirectory();
11899 if (!(file = fopen(filename, MODE_WRITE)))
11901 Warn("cannot write server setup file '%s'", filename);
11908 fprintFileHeader(file, SERVERSETUP_FILENAME);
11910 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11912 // just to make things nicer :)
11913 if (server_setup_tokens[i].value == &setup.use_api_server)
11914 fprintf(file, "\n");
11916 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11921 SetFilePermissions(filename, PERMS_PRIVATE);
11926 void SaveSetup_EditorCascade(void)
11928 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11932 InitUserDataDirectory();
11934 if (!(file = fopen(filename, MODE_WRITE)))
11936 Warn("cannot write editor cascade state file '%s'", filename);
11943 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11945 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11946 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11950 SetFilePermissions(filename, PERMS_PRIVATE);
11955 void SaveSetup(void)
11957 SaveSetup_Default();
11958 SaveSetup_AutoSetup();
11959 SaveSetup_ServerSetup();
11960 SaveSetup_EditorCascade();
11963 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11968 if (!(file = fopen(filename, MODE_WRITE)))
11970 Warn("cannot write game controller mappings file '%s'", filename);
11975 BEGIN_HASH_ITERATION(mappings_hash, itr)
11977 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11979 END_HASH_ITERATION(mappings_hash, itr)
11984 void SaveSetup_AddGameControllerMapping(char *mapping)
11986 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11987 SetupFileHash *mappings_hash = newSetupFileHash();
11989 InitUserDataDirectory();
11991 // load existing personal game controller mappings
11992 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11994 // add new mapping to personal game controller mappings
11995 addGameControllerMappingToHash(mappings_hash, mapping);
11997 // save updated personal game controller mappings
11998 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12000 freeSetupFileHash(mappings_hash);
12004 void LoadCustomElementDescriptions(void)
12006 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12007 SetupFileHash *setup_file_hash;
12010 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12012 if (element_info[i].custom_description != NULL)
12014 free(element_info[i].custom_description);
12015 element_info[i].custom_description = NULL;
12019 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12022 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12024 char *token = getStringCat2(element_info[i].token_name, ".name");
12025 char *value = getHashEntry(setup_file_hash, token);
12028 element_info[i].custom_description = getStringCopy(value);
12033 freeSetupFileHash(setup_file_hash);
12036 static int getElementFromToken(char *token)
12038 char *value = getHashEntry(element_token_hash, token);
12041 return atoi(value);
12043 Warn("unknown element token '%s'", token);
12045 return EL_UNDEFINED;
12048 void FreeGlobalAnimEventInfo(void)
12050 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12052 if (gaei->event_list == NULL)
12057 for (i = 0; i < gaei->num_event_lists; i++)
12059 checked_free(gaei->event_list[i]->event_value);
12060 checked_free(gaei->event_list[i]);
12063 checked_free(gaei->event_list);
12065 gaei->event_list = NULL;
12066 gaei->num_event_lists = 0;
12069 static int AddGlobalAnimEventList(void)
12071 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12072 int list_pos = gaei->num_event_lists++;
12074 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12075 sizeof(struct GlobalAnimEventListInfo *));
12077 gaei->event_list[list_pos] =
12078 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12080 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12082 gaeli->event_value = NULL;
12083 gaeli->num_event_values = 0;
12088 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12090 // do not add empty global animation events
12091 if (event_value == ANIM_EVENT_NONE)
12094 // if list position is undefined, create new list
12095 if (list_pos == ANIM_EVENT_UNDEFINED)
12096 list_pos = AddGlobalAnimEventList();
12098 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12099 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12100 int value_pos = gaeli->num_event_values++;
12102 gaeli->event_value = checked_realloc(gaeli->event_value,
12103 gaeli->num_event_values * sizeof(int *));
12105 gaeli->event_value[value_pos] = event_value;
12110 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12112 if (list_pos == ANIM_EVENT_UNDEFINED)
12113 return ANIM_EVENT_NONE;
12115 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12116 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12118 return gaeli->event_value[value_pos];
12121 int GetGlobalAnimEventValueCount(int list_pos)
12123 if (list_pos == ANIM_EVENT_UNDEFINED)
12126 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12127 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12129 return gaeli->num_event_values;
12132 // This function checks if a string <s> of the format "string1, string2, ..."
12133 // exactly contains a string <s_contained>.
12135 static boolean string_has_parameter(char *s, char *s_contained)
12139 if (s == NULL || s_contained == NULL)
12142 if (strlen(s_contained) > strlen(s))
12145 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12147 char next_char = s[strlen(s_contained)];
12149 // check if next character is delimiter or whitespace
12150 if (next_char == ',' || next_char == '\0' ||
12151 next_char == ' ' || next_char == '\t')
12155 // check if string contains another parameter string after a comma
12156 substring = strchr(s, ',');
12157 if (substring == NULL) // string does not contain a comma
12160 // advance string pointer to next character after the comma
12163 // skip potential whitespaces after the comma
12164 while (*substring == ' ' || *substring == '\t')
12167 return string_has_parameter(substring, s_contained);
12170 static int get_anim_parameter_value_ce(char *s)
12173 char *pattern_1 = "ce_change:custom_";
12174 char *pattern_2 = ".page_";
12175 int pattern_1_len = strlen(pattern_1);
12176 char *matching_char = strstr(s_ptr, pattern_1);
12177 int result = ANIM_EVENT_NONE;
12179 if (matching_char == NULL)
12180 return ANIM_EVENT_NONE;
12182 result = ANIM_EVENT_CE_CHANGE;
12184 s_ptr = matching_char + pattern_1_len;
12186 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12187 if (*s_ptr >= '0' && *s_ptr <= '9')
12189 int gic_ce_nr = (*s_ptr++ - '0');
12191 if (*s_ptr >= '0' && *s_ptr <= '9')
12193 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12195 if (*s_ptr >= '0' && *s_ptr <= '9')
12196 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12199 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12200 return ANIM_EVENT_NONE;
12202 // custom element stored as 0 to 255
12205 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12209 // invalid custom element number specified
12211 return ANIM_EVENT_NONE;
12214 // check for change page number ("page_X" or "page_XX") (optional)
12215 if (strPrefix(s_ptr, pattern_2))
12217 s_ptr += strlen(pattern_2);
12219 if (*s_ptr >= '0' && *s_ptr <= '9')
12221 int gic_page_nr = (*s_ptr++ - '0');
12223 if (*s_ptr >= '0' && *s_ptr <= '9')
12224 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12226 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12227 return ANIM_EVENT_NONE;
12229 // change page stored as 1 to 32 (0 means "all change pages")
12231 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12235 // invalid animation part number specified
12237 return ANIM_EVENT_NONE;
12241 // discard result if next character is neither delimiter nor whitespace
12242 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12243 *s_ptr == ' ' || *s_ptr == '\t'))
12244 return ANIM_EVENT_NONE;
12249 static int get_anim_parameter_value(char *s)
12251 int event_value[] =
12259 char *pattern_1[] =
12267 char *pattern_2 = ".part_";
12268 char *matching_char = NULL;
12270 int pattern_1_len = 0;
12271 int result = ANIM_EVENT_NONE;
12274 result = get_anim_parameter_value_ce(s);
12276 if (result != ANIM_EVENT_NONE)
12279 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12281 matching_char = strstr(s_ptr, pattern_1[i]);
12282 pattern_1_len = strlen(pattern_1[i]);
12283 result = event_value[i];
12285 if (matching_char != NULL)
12289 if (matching_char == NULL)
12290 return ANIM_EVENT_NONE;
12292 s_ptr = matching_char + pattern_1_len;
12294 // check for main animation number ("anim_X" or "anim_XX")
12295 if (*s_ptr >= '0' && *s_ptr <= '9')
12297 int gic_anim_nr = (*s_ptr++ - '0');
12299 if (*s_ptr >= '0' && *s_ptr <= '9')
12300 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12302 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12303 return ANIM_EVENT_NONE;
12305 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12309 // invalid main animation number specified
12311 return ANIM_EVENT_NONE;
12314 // check for animation part number ("part_X" or "part_XX") (optional)
12315 if (strPrefix(s_ptr, pattern_2))
12317 s_ptr += strlen(pattern_2);
12319 if (*s_ptr >= '0' && *s_ptr <= '9')
12321 int gic_part_nr = (*s_ptr++ - '0');
12323 if (*s_ptr >= '0' && *s_ptr <= '9')
12324 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12326 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12327 return ANIM_EVENT_NONE;
12329 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12333 // invalid animation part number specified
12335 return ANIM_EVENT_NONE;
12339 // discard result if next character is neither delimiter nor whitespace
12340 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12341 *s_ptr == ' ' || *s_ptr == '\t'))
12342 return ANIM_EVENT_NONE;
12347 static int get_anim_parameter_values(char *s)
12349 int list_pos = ANIM_EVENT_UNDEFINED;
12350 int event_value = ANIM_EVENT_DEFAULT;
12352 if (string_has_parameter(s, "any"))
12353 event_value |= ANIM_EVENT_ANY;
12355 if (string_has_parameter(s, "click:self") ||
12356 string_has_parameter(s, "click") ||
12357 string_has_parameter(s, "self"))
12358 event_value |= ANIM_EVENT_SELF;
12360 if (string_has_parameter(s, "unclick:any"))
12361 event_value |= ANIM_EVENT_UNCLICK_ANY;
12363 // if animation event found, add it to global animation event list
12364 if (event_value != ANIM_EVENT_NONE)
12365 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12369 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12370 event_value = get_anim_parameter_value(s);
12372 // if animation event found, add it to global animation event list
12373 if (event_value != ANIM_EVENT_NONE)
12374 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12376 // continue with next part of the string, starting with next comma
12377 s = strchr(s + 1, ',');
12383 static int get_anim_action_parameter_value(char *token)
12385 // check most common default case first to massively speed things up
12386 if (strEqual(token, ARG_UNDEFINED))
12387 return ANIM_EVENT_ACTION_NONE;
12389 int result = getImageIDFromToken(token);
12393 char *gfx_token = getStringCat2("gfx.", token);
12395 result = getImageIDFromToken(gfx_token);
12397 checked_free(gfx_token);
12402 Key key = getKeyFromX11KeyName(token);
12404 if (key != KSYM_UNDEFINED)
12405 result = -(int)key;
12412 result = get_hash_from_string(token); // unsigned int => int
12413 result = ABS(result); // may be negative now
12414 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12416 setHashEntry(anim_url_hash, int2str(result, 0), token);
12421 result = ANIM_EVENT_ACTION_NONE;
12426 int get_parameter_value(char *value_raw, char *suffix, int type)
12428 char *value = getStringToLower(value_raw);
12429 int result = 0; // probably a save default value
12431 if (strEqual(suffix, ".direction"))
12433 result = (strEqual(value, "left") ? MV_LEFT :
12434 strEqual(value, "right") ? MV_RIGHT :
12435 strEqual(value, "up") ? MV_UP :
12436 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12438 else if (strEqual(suffix, ".position"))
12440 result = (strEqual(value, "left") ? POS_LEFT :
12441 strEqual(value, "right") ? POS_RIGHT :
12442 strEqual(value, "top") ? POS_TOP :
12443 strEqual(value, "upper") ? POS_UPPER :
12444 strEqual(value, "middle") ? POS_MIDDLE :
12445 strEqual(value, "lower") ? POS_LOWER :
12446 strEqual(value, "bottom") ? POS_BOTTOM :
12447 strEqual(value, "any") ? POS_ANY :
12448 strEqual(value, "ce") ? POS_CE :
12449 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12450 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12452 else if (strEqual(suffix, ".align"))
12454 result = (strEqual(value, "left") ? ALIGN_LEFT :
12455 strEqual(value, "right") ? ALIGN_RIGHT :
12456 strEqual(value, "center") ? ALIGN_CENTER :
12457 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12459 else if (strEqual(suffix, ".valign"))
12461 result = (strEqual(value, "top") ? VALIGN_TOP :
12462 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12463 strEqual(value, "middle") ? VALIGN_MIDDLE :
12464 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12466 else if (strEqual(suffix, ".anim_mode"))
12468 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12469 string_has_parameter(value, "loop") ? ANIM_LOOP :
12470 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12471 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12472 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12473 string_has_parameter(value, "random") ? ANIM_RANDOM :
12474 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12475 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12476 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12477 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12478 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12479 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12480 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12481 string_has_parameter(value, "all") ? ANIM_ALL :
12482 string_has_parameter(value, "tiled") ? ANIM_TILED :
12483 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12486 if (string_has_parameter(value, "once"))
12487 result |= ANIM_ONCE;
12489 if (string_has_parameter(value, "reverse"))
12490 result |= ANIM_REVERSE;
12492 if (string_has_parameter(value, "opaque_player"))
12493 result |= ANIM_OPAQUE_PLAYER;
12495 if (string_has_parameter(value, "static_panel"))
12496 result |= ANIM_STATIC_PANEL;
12498 else if (strEqual(suffix, ".init_event") ||
12499 strEqual(suffix, ".anim_event"))
12501 result = get_anim_parameter_values(value);
12503 else if (strEqual(suffix, ".init_delay_action") ||
12504 strEqual(suffix, ".anim_delay_action") ||
12505 strEqual(suffix, ".post_delay_action") ||
12506 strEqual(suffix, ".init_event_action") ||
12507 strEqual(suffix, ".anim_event_action"))
12509 result = get_anim_action_parameter_value(value_raw);
12511 else if (strEqual(suffix, ".class"))
12513 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12514 get_hash_from_string(value));
12516 else if (strEqual(suffix, ".style"))
12518 result = STYLE_DEFAULT;
12520 if (string_has_parameter(value, "accurate_borders"))
12521 result |= STYLE_ACCURATE_BORDERS;
12523 if (string_has_parameter(value, "inner_corners"))
12524 result |= STYLE_INNER_CORNERS;
12526 if (string_has_parameter(value, "reverse"))
12527 result |= STYLE_REVERSE;
12529 if (string_has_parameter(value, "leftmost_position"))
12530 result |= STYLE_LEFTMOST_POSITION;
12532 if (string_has_parameter(value, "block_clicks"))
12533 result |= STYLE_BLOCK;
12535 if (string_has_parameter(value, "passthrough_clicks"))
12536 result |= STYLE_PASSTHROUGH;
12538 if (string_has_parameter(value, "multiple_actions"))
12539 result |= STYLE_MULTIPLE_ACTIONS;
12541 if (string_has_parameter(value, "consume_ce_event"))
12542 result |= STYLE_CONSUME_CE_EVENT;
12544 else if (strEqual(suffix, ".fade_mode"))
12546 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12547 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12548 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12549 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12550 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12551 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12552 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12553 FADE_MODE_DEFAULT);
12555 else if (strEqual(suffix, ".auto_delay_unit"))
12557 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12558 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12559 AUTO_DELAY_UNIT_DEFAULT);
12561 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12563 result = gfx.get_font_from_token_function(value);
12565 else // generic parameter of type integer or boolean
12567 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12568 type == TYPE_INTEGER ? get_integer_from_string(value) :
12569 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12570 ARG_UNDEFINED_VALUE);
12578 static int get_token_parameter_value(char *token, char *value_raw)
12582 if (token == NULL || value_raw == NULL)
12583 return ARG_UNDEFINED_VALUE;
12585 suffix = strrchr(token, '.');
12586 if (suffix == NULL)
12589 if (strEqual(suffix, ".element"))
12590 return getElementFromToken(value_raw);
12592 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12593 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12596 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12597 boolean ignore_defaults)
12601 for (i = 0; image_config_vars[i].token != NULL; i++)
12603 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12605 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12606 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12610 *image_config_vars[i].value =
12611 get_token_parameter_value(image_config_vars[i].token, value);
12615 void InitMenuDesignSettings_Static(void)
12617 // always start with reliable default values from static default config
12618 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12621 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12625 // the following initializes hierarchical values from static configuration
12627 // special case: initialize "ARG_DEFAULT" values in static default config
12628 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12629 titlescreen_initial_first_default.fade_mode =
12630 title_initial_first_default.fade_mode;
12631 titlescreen_initial_first_default.fade_delay =
12632 title_initial_first_default.fade_delay;
12633 titlescreen_initial_first_default.post_delay =
12634 title_initial_first_default.post_delay;
12635 titlescreen_initial_first_default.auto_delay =
12636 title_initial_first_default.auto_delay;
12637 titlescreen_initial_first_default.auto_delay_unit =
12638 title_initial_first_default.auto_delay_unit;
12639 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12640 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12641 titlescreen_first_default.post_delay = title_first_default.post_delay;
12642 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12643 titlescreen_first_default.auto_delay_unit =
12644 title_first_default.auto_delay_unit;
12645 titlemessage_initial_first_default.fade_mode =
12646 title_initial_first_default.fade_mode;
12647 titlemessage_initial_first_default.fade_delay =
12648 title_initial_first_default.fade_delay;
12649 titlemessage_initial_first_default.post_delay =
12650 title_initial_first_default.post_delay;
12651 titlemessage_initial_first_default.auto_delay =
12652 title_initial_first_default.auto_delay;
12653 titlemessage_initial_first_default.auto_delay_unit =
12654 title_initial_first_default.auto_delay_unit;
12655 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12656 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12657 titlemessage_first_default.post_delay = title_first_default.post_delay;
12658 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12659 titlemessage_first_default.auto_delay_unit =
12660 title_first_default.auto_delay_unit;
12662 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12663 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12664 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12665 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12666 titlescreen_initial_default.auto_delay_unit =
12667 title_initial_default.auto_delay_unit;
12668 titlescreen_default.fade_mode = title_default.fade_mode;
12669 titlescreen_default.fade_delay = title_default.fade_delay;
12670 titlescreen_default.post_delay = title_default.post_delay;
12671 titlescreen_default.auto_delay = title_default.auto_delay;
12672 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12673 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12674 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12675 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12676 titlemessage_initial_default.auto_delay_unit =
12677 title_initial_default.auto_delay_unit;
12678 titlemessage_default.fade_mode = title_default.fade_mode;
12679 titlemessage_default.fade_delay = title_default.fade_delay;
12680 titlemessage_default.post_delay = title_default.post_delay;
12681 titlemessage_default.auto_delay = title_default.auto_delay;
12682 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12684 // special case: initialize "ARG_DEFAULT" values in static default config
12685 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12686 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12688 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12689 titlescreen_first[i] = titlescreen_first_default;
12690 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12691 titlemessage_first[i] = titlemessage_first_default;
12693 titlescreen_initial[i] = titlescreen_initial_default;
12694 titlescreen[i] = titlescreen_default;
12695 titlemessage_initial[i] = titlemessage_initial_default;
12696 titlemessage[i] = titlemessage_default;
12699 // special case: initialize "ARG_DEFAULT" values in static default config
12700 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12701 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12703 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12706 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12707 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12708 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12711 // special case: initialize "ARG_DEFAULT" values in static default config
12712 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12713 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12715 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12716 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12717 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12719 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12722 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12726 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12730 struct XY *dst, *src;
12732 game_buttons_xy[] =
12734 { &game.button.save, &game.button.stop },
12735 { &game.button.pause2, &game.button.pause },
12736 { &game.button.load, &game.button.play },
12737 { &game.button.undo, &game.button.stop },
12738 { &game.button.redo, &game.button.play },
12744 // special case: initialize later added SETUP list size from LEVELS value
12745 if (menu.list_size[GAME_MODE_SETUP] == -1)
12746 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12748 // set default position for snapshot buttons to stop/pause/play buttons
12749 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12750 if ((*game_buttons_xy[i].dst).x == -1 &&
12751 (*game_buttons_xy[i].dst).y == -1)
12752 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12754 // --------------------------------------------------------------------------
12755 // dynamic viewports (including playfield margins, borders and alignments)
12756 // --------------------------------------------------------------------------
12758 // dynamic viewports currently only supported for landscape mode
12759 int display_width = MAX(video.display_width, video.display_height);
12760 int display_height = MIN(video.display_width, video.display_height);
12762 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12764 struct RectWithBorder *vp_window = &viewport.window[i];
12765 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12766 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12767 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12768 boolean dynamic_window_width = (vp_window->min_width != -1);
12769 boolean dynamic_window_height = (vp_window->min_height != -1);
12770 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12771 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12773 // adjust window size if min/max width/height is specified
12775 if (vp_window->min_width != -1)
12777 int window_width = display_width;
12779 // when using static window height, use aspect ratio of display
12780 if (vp_window->min_height == -1)
12781 window_width = vp_window->height * display_width / display_height;
12783 vp_window->width = MAX(vp_window->min_width, window_width);
12786 if (vp_window->min_height != -1)
12788 int window_height = display_height;
12790 // when using static window width, use aspect ratio of display
12791 if (vp_window->min_width == -1)
12792 window_height = vp_window->width * display_height / display_width;
12794 vp_window->height = MAX(vp_window->min_height, window_height);
12797 if (vp_window->max_width != -1)
12798 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12800 if (vp_window->max_height != -1)
12801 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12803 int playfield_width = vp_window->width;
12804 int playfield_height = vp_window->height;
12806 // adjust playfield size and position according to specified margins
12808 playfield_width -= vp_playfield->margin_left;
12809 playfield_width -= vp_playfield->margin_right;
12811 playfield_height -= vp_playfield->margin_top;
12812 playfield_height -= vp_playfield->margin_bottom;
12814 // adjust playfield size if min/max width/height is specified
12816 if (vp_playfield->min_width != -1)
12817 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12819 if (vp_playfield->min_height != -1)
12820 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12822 if (vp_playfield->max_width != -1)
12823 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12825 if (vp_playfield->max_height != -1)
12826 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12828 // adjust playfield position according to specified alignment
12830 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12831 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12832 else if (vp_playfield->align == ALIGN_CENTER)
12833 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12834 else if (vp_playfield->align == ALIGN_RIGHT)
12835 vp_playfield->x += playfield_width - vp_playfield->width;
12837 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12838 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12839 else if (vp_playfield->valign == VALIGN_MIDDLE)
12840 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12841 else if (vp_playfield->valign == VALIGN_BOTTOM)
12842 vp_playfield->y += playfield_height - vp_playfield->height;
12844 vp_playfield->x += vp_playfield->margin_left;
12845 vp_playfield->y += vp_playfield->margin_top;
12847 // adjust individual playfield borders if only default border is specified
12849 if (vp_playfield->border_left == -1)
12850 vp_playfield->border_left = vp_playfield->border_size;
12851 if (vp_playfield->border_right == -1)
12852 vp_playfield->border_right = vp_playfield->border_size;
12853 if (vp_playfield->border_top == -1)
12854 vp_playfield->border_top = vp_playfield->border_size;
12855 if (vp_playfield->border_bottom == -1)
12856 vp_playfield->border_bottom = vp_playfield->border_size;
12858 // set dynamic playfield borders if borders are specified as undefined
12859 // (but only if window size was dynamic and playfield size was static)
12861 if (dynamic_window_width && !dynamic_playfield_width)
12863 if (vp_playfield->border_left == -1)
12865 vp_playfield->border_left = (vp_playfield->x -
12866 vp_playfield->margin_left);
12867 vp_playfield->x -= vp_playfield->border_left;
12868 vp_playfield->width += vp_playfield->border_left;
12871 if (vp_playfield->border_right == -1)
12873 vp_playfield->border_right = (vp_window->width -
12875 vp_playfield->width -
12876 vp_playfield->margin_right);
12877 vp_playfield->width += vp_playfield->border_right;
12881 if (dynamic_window_height && !dynamic_playfield_height)
12883 if (vp_playfield->border_top == -1)
12885 vp_playfield->border_top = (vp_playfield->y -
12886 vp_playfield->margin_top);
12887 vp_playfield->y -= vp_playfield->border_top;
12888 vp_playfield->height += vp_playfield->border_top;
12891 if (vp_playfield->border_bottom == -1)
12893 vp_playfield->border_bottom = (vp_window->height -
12895 vp_playfield->height -
12896 vp_playfield->margin_bottom);
12897 vp_playfield->height += vp_playfield->border_bottom;
12901 // adjust playfield size to be a multiple of a defined alignment tile size
12903 int align_size = vp_playfield->align_size;
12904 int playfield_xtiles = vp_playfield->width / align_size;
12905 int playfield_ytiles = vp_playfield->height / align_size;
12906 int playfield_width_corrected = playfield_xtiles * align_size;
12907 int playfield_height_corrected = playfield_ytiles * align_size;
12908 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12909 i == GFX_SPECIAL_ARG_EDITOR);
12911 if (is_playfield_mode &&
12912 dynamic_playfield_width &&
12913 vp_playfield->width != playfield_width_corrected)
12915 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12917 vp_playfield->width = playfield_width_corrected;
12919 if (vp_playfield->align == ALIGN_LEFT)
12921 vp_playfield->border_left += playfield_xdiff;
12923 else if (vp_playfield->align == ALIGN_RIGHT)
12925 vp_playfield->border_right += playfield_xdiff;
12927 else if (vp_playfield->align == ALIGN_CENTER)
12929 int border_left_diff = playfield_xdiff / 2;
12930 int border_right_diff = playfield_xdiff - border_left_diff;
12932 vp_playfield->border_left += border_left_diff;
12933 vp_playfield->border_right += border_right_diff;
12937 if (is_playfield_mode &&
12938 dynamic_playfield_height &&
12939 vp_playfield->height != playfield_height_corrected)
12941 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12943 vp_playfield->height = playfield_height_corrected;
12945 if (vp_playfield->valign == VALIGN_TOP)
12947 vp_playfield->border_top += playfield_ydiff;
12949 else if (vp_playfield->align == VALIGN_BOTTOM)
12951 vp_playfield->border_right += playfield_ydiff;
12953 else if (vp_playfield->align == VALIGN_MIDDLE)
12955 int border_top_diff = playfield_ydiff / 2;
12956 int border_bottom_diff = playfield_ydiff - border_top_diff;
12958 vp_playfield->border_top += border_top_diff;
12959 vp_playfield->border_bottom += border_bottom_diff;
12963 // adjust door positions according to specified alignment
12965 for (j = 0; j < 2; j++)
12967 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12969 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12970 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12971 else if (vp_door->align == ALIGN_CENTER)
12972 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12973 else if (vp_door->align == ALIGN_RIGHT)
12974 vp_door->x += vp_window->width - vp_door->width;
12976 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12977 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12978 else if (vp_door->valign == VALIGN_MIDDLE)
12979 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12980 else if (vp_door->valign == VALIGN_BOTTOM)
12981 vp_door->y += vp_window->height - vp_door->height;
12986 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12990 struct XYTileSize *dst, *src;
12993 editor_buttons_xy[] =
12996 &editor.button.element_left, &editor.palette.element_left,
12997 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13000 &editor.button.element_middle, &editor.palette.element_middle,
13001 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13004 &editor.button.element_right, &editor.palette.element_right,
13005 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13012 // set default position for element buttons to element graphics
13013 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13015 if ((*editor_buttons_xy[i].dst).x == -1 &&
13016 (*editor_buttons_xy[i].dst).y == -1)
13018 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13020 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13022 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13026 // adjust editor palette rows and columns if specified to be dynamic
13028 if (editor.palette.cols == -1)
13030 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13031 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13032 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13034 editor.palette.cols = (vp_width - sc_width) / bt_width;
13036 if (editor.palette.x == -1)
13038 int palette_width = editor.palette.cols * bt_width + sc_width;
13040 editor.palette.x = (vp_width - palette_width) / 2;
13044 if (editor.palette.rows == -1)
13046 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13047 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13048 int tx_height = getFontHeight(FONT_TEXT_2);
13050 editor.palette.rows = (vp_height - tx_height) / bt_height;
13052 if (editor.palette.y == -1)
13054 int palette_height = editor.palette.rows * bt_height + tx_height;
13056 editor.palette.y = (vp_height - palette_height) / 2;
13061 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13062 boolean initialize)
13064 // special case: check if network and preview player positions are redefined,
13065 // to compare this later against the main menu level preview being redefined
13066 struct TokenIntPtrInfo menu_config_players[] =
13068 { "main.network_players.x", &menu.main.network_players.redefined },
13069 { "main.network_players.y", &menu.main.network_players.redefined },
13070 { "main.preview_players.x", &menu.main.preview_players.redefined },
13071 { "main.preview_players.y", &menu.main.preview_players.redefined },
13072 { "preview.x", &preview.redefined },
13073 { "preview.y", &preview.redefined }
13079 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13080 *menu_config_players[i].value = FALSE;
13084 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13085 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13086 *menu_config_players[i].value = TRUE;
13090 static void InitMenuDesignSettings_PreviewPlayers(void)
13092 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13095 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13097 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13100 static void LoadMenuDesignSettingsFromFilename(char *filename)
13102 static struct TitleFadingInfo tfi;
13103 static struct TitleMessageInfo tmi;
13104 static struct TokenInfo title_tokens[] =
13106 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13107 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13108 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13109 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13110 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13114 static struct TokenInfo titlemessage_tokens[] =
13116 { TYPE_INTEGER, &tmi.x, ".x" },
13117 { TYPE_INTEGER, &tmi.y, ".y" },
13118 { TYPE_INTEGER, &tmi.width, ".width" },
13119 { TYPE_INTEGER, &tmi.height, ".height" },
13120 { TYPE_INTEGER, &tmi.chars, ".chars" },
13121 { TYPE_INTEGER, &tmi.lines, ".lines" },
13122 { TYPE_INTEGER, &tmi.align, ".align" },
13123 { TYPE_INTEGER, &tmi.valign, ".valign" },
13124 { TYPE_INTEGER, &tmi.font, ".font" },
13125 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13126 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13127 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13128 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13129 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13130 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13131 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13132 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13133 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13139 struct TitleFadingInfo *info;
13144 // initialize first titles from "enter screen" definitions, if defined
13145 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13146 { &title_first_default, "menu.enter_screen.TITLE" },
13148 // initialize title screens from "next screen" definitions, if defined
13149 { &title_initial_default, "menu.next_screen.TITLE" },
13150 { &title_default, "menu.next_screen.TITLE" },
13156 struct TitleMessageInfo *array;
13159 titlemessage_arrays[] =
13161 // initialize first titles from "enter screen" definitions, if defined
13162 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13163 { titlescreen_first, "menu.enter_screen.TITLE" },
13164 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13165 { titlemessage_first, "menu.enter_screen.TITLE" },
13167 // initialize titles from "next screen" definitions, if defined
13168 { titlescreen_initial, "menu.next_screen.TITLE" },
13169 { titlescreen, "menu.next_screen.TITLE" },
13170 { titlemessage_initial, "menu.next_screen.TITLE" },
13171 { titlemessage, "menu.next_screen.TITLE" },
13173 // overwrite titles with title definitions, if defined
13174 { titlescreen_initial_first, "[title_initial]" },
13175 { titlescreen_first, "[title]" },
13176 { titlemessage_initial_first, "[title_initial]" },
13177 { titlemessage_first, "[title]" },
13179 { titlescreen_initial, "[title_initial]" },
13180 { titlescreen, "[title]" },
13181 { titlemessage_initial, "[title_initial]" },
13182 { titlemessage, "[title]" },
13184 // overwrite titles with title screen/message definitions, if defined
13185 { titlescreen_initial_first, "[titlescreen_initial]" },
13186 { titlescreen_first, "[titlescreen]" },
13187 { titlemessage_initial_first, "[titlemessage_initial]" },
13188 { titlemessage_first, "[titlemessage]" },
13190 { titlescreen_initial, "[titlescreen_initial]" },
13191 { titlescreen, "[titlescreen]" },
13192 { titlemessage_initial, "[titlemessage_initial]" },
13193 { titlemessage, "[titlemessage]" },
13197 SetupFileHash *setup_file_hash;
13200 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13203 // the following initializes hierarchical values from dynamic configuration
13205 // special case: initialize with default values that may be overwritten
13206 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13207 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13209 struct TokenIntPtrInfo menu_config[] =
13211 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13212 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13213 { "menu.list_size", &menu.list_size[i] }
13216 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13218 char *token = menu_config[j].token;
13219 char *value = getHashEntry(setup_file_hash, token);
13222 *menu_config[j].value = get_integer_from_string(value);
13226 // special case: initialize with default values that may be overwritten
13227 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13228 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13230 struct TokenIntPtrInfo menu_config[] =
13232 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13233 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13234 { "menu.list_size.INFO", &menu.list_size_info[i] },
13235 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13236 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13239 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13241 char *token = menu_config[j].token;
13242 char *value = getHashEntry(setup_file_hash, token);
13245 *menu_config[j].value = get_integer_from_string(value);
13249 // special case: initialize with default values that may be overwritten
13250 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13251 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13253 struct TokenIntPtrInfo menu_config[] =
13255 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13256 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13259 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13261 char *token = menu_config[j].token;
13262 char *value = getHashEntry(setup_file_hash, token);
13265 *menu_config[j].value = get_integer_from_string(value);
13269 // special case: initialize with default values that may be overwritten
13270 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13271 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13273 struct TokenIntPtrInfo menu_config[] =
13275 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13276 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13277 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13278 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13279 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13280 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13281 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13282 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13283 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13284 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13287 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13289 char *token = menu_config[j].token;
13290 char *value = getHashEntry(setup_file_hash, token);
13293 *menu_config[j].value = get_integer_from_string(value);
13297 // special case: initialize with default values that may be overwritten
13298 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13299 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13301 struct TokenIntPtrInfo menu_config[] =
13303 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13304 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13305 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13306 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13307 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13308 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13309 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13310 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13311 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
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_token_parameter_value(token, value);
13324 // special case: initialize with default values that may be overwritten
13325 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13326 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13330 char *token_prefix;
13331 struct RectWithBorder *struct_ptr;
13335 { "viewport.window", &viewport.window[i] },
13336 { "viewport.playfield", &viewport.playfield[i] },
13337 { "viewport.door_1", &viewport.door_1[i] },
13338 { "viewport.door_2", &viewport.door_2[i] }
13341 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13343 struct TokenIntPtrInfo vp_config[] =
13345 { ".x", &vp_struct[j].struct_ptr->x },
13346 { ".y", &vp_struct[j].struct_ptr->y },
13347 { ".width", &vp_struct[j].struct_ptr->width },
13348 { ".height", &vp_struct[j].struct_ptr->height },
13349 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13350 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13351 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13352 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13353 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13354 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13355 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13356 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13357 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13358 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13359 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13360 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13361 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13362 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13363 { ".align", &vp_struct[j].struct_ptr->align },
13364 { ".valign", &vp_struct[j].struct_ptr->valign }
13367 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13369 char *token = getStringCat2(vp_struct[j].token_prefix,
13370 vp_config[k].token);
13371 char *value = getHashEntry(setup_file_hash, token);
13374 *vp_config[k].value = get_token_parameter_value(token, value);
13381 // special case: initialize with default values that may be overwritten
13382 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13383 for (i = 0; title_info[i].info != NULL; i++)
13385 struct TitleFadingInfo *info = title_info[i].info;
13386 char *base_token = title_info[i].text;
13388 for (j = 0; title_tokens[j].type != -1; j++)
13390 char *token = getStringCat2(base_token, title_tokens[j].text);
13391 char *value = getHashEntry(setup_file_hash, token);
13395 int parameter_value = get_token_parameter_value(token, value);
13399 *(int *)title_tokens[j].value = (int)parameter_value;
13408 // special case: initialize with default values that may be overwritten
13409 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13410 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13412 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13413 char *base_token = titlemessage_arrays[i].text;
13415 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13417 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13418 char *value = getHashEntry(setup_file_hash, token);
13422 int parameter_value = get_token_parameter_value(token, value);
13424 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13428 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13429 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13431 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13441 // read (and overwrite with) values that may be specified in config file
13442 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13444 // special case: check if network and preview player positions are redefined
13445 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13447 freeSetupFileHash(setup_file_hash);
13450 void LoadMenuDesignSettings(void)
13452 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13454 InitMenuDesignSettings_Static();
13455 InitMenuDesignSettings_SpecialPreProcessing();
13456 InitMenuDesignSettings_PreviewPlayers();
13458 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13460 // first look for special settings configured in level series config
13461 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13463 if (fileExists(filename_base))
13464 LoadMenuDesignSettingsFromFilename(filename_base);
13467 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13469 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13470 LoadMenuDesignSettingsFromFilename(filename_local);
13472 InitMenuDesignSettings_SpecialPostProcessing();
13475 void LoadMenuDesignSettings_AfterGraphics(void)
13477 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13480 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13481 boolean ignore_defaults)
13485 for (i = 0; sound_config_vars[i].token != NULL; i++)
13487 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13489 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13490 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13494 *sound_config_vars[i].value =
13495 get_token_parameter_value(sound_config_vars[i].token, value);
13499 void InitSoundSettings_Static(void)
13501 // always start with reliable default values from static default config
13502 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13505 static void LoadSoundSettingsFromFilename(char *filename)
13507 SetupFileHash *setup_file_hash;
13509 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13512 // read (and overwrite with) values that may be specified in config file
13513 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13515 freeSetupFileHash(setup_file_hash);
13518 void LoadSoundSettings(void)
13520 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13522 InitSoundSettings_Static();
13524 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13526 // first look for special settings configured in level series config
13527 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13529 if (fileExists(filename_base))
13530 LoadSoundSettingsFromFilename(filename_base);
13533 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13535 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13536 LoadSoundSettingsFromFilename(filename_local);
13539 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13541 char *filename = getEditorSetupFilename();
13542 SetupFileList *setup_file_list, *list;
13543 SetupFileHash *element_hash;
13544 int num_unknown_tokens = 0;
13547 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13550 element_hash = newSetupFileHash();
13552 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13553 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13555 // determined size may be larger than needed (due to unknown elements)
13557 for (list = setup_file_list; list != NULL; list = list->next)
13560 // add space for up to 3 more elements for padding that may be needed
13561 *num_elements += 3;
13563 // free memory for old list of elements, if needed
13564 checked_free(*elements);
13566 // allocate memory for new list of elements
13567 *elements = checked_malloc(*num_elements * sizeof(int));
13570 for (list = setup_file_list; list != NULL; list = list->next)
13572 char *value = getHashEntry(element_hash, list->token);
13574 if (value == NULL) // try to find obsolete token mapping
13576 char *mapped_token = get_mapped_token(list->token);
13578 if (mapped_token != NULL)
13580 value = getHashEntry(element_hash, mapped_token);
13582 free(mapped_token);
13588 (*elements)[(*num_elements)++] = atoi(value);
13592 if (num_unknown_tokens == 0)
13595 Warn("unknown token(s) found in config file:");
13596 Warn("- config file: '%s'", filename);
13598 num_unknown_tokens++;
13601 Warn("- token: '%s'", list->token);
13605 if (num_unknown_tokens > 0)
13608 while (*num_elements % 4) // pad with empty elements, if needed
13609 (*elements)[(*num_elements)++] = EL_EMPTY;
13611 freeSetupFileList(setup_file_list);
13612 freeSetupFileHash(element_hash);
13615 for (i = 0; i < *num_elements; i++)
13616 Debug("editor", "element '%s' [%d]\n",
13617 element_info[(*elements)[i]].token_name, (*elements)[i]);
13621 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13624 SetupFileHash *setup_file_hash = NULL;
13625 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13626 char *filename_music, *filename_prefix, *filename_info;
13632 token_to_value_ptr[] =
13634 { "title_header", &tmp_music_file_info.title_header },
13635 { "artist_header", &tmp_music_file_info.artist_header },
13636 { "album_header", &tmp_music_file_info.album_header },
13637 { "year_header", &tmp_music_file_info.year_header },
13638 { "played_header", &tmp_music_file_info.played_header },
13640 { "title", &tmp_music_file_info.title },
13641 { "artist", &tmp_music_file_info.artist },
13642 { "album", &tmp_music_file_info.album },
13643 { "year", &tmp_music_file_info.year },
13644 { "played", &tmp_music_file_info.played },
13650 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13651 getCustomMusicFilename(basename));
13653 if (filename_music == NULL)
13656 // ---------- try to replace file extension ----------
13658 filename_prefix = getStringCopy(filename_music);
13659 if (strrchr(filename_prefix, '.') != NULL)
13660 *strrchr(filename_prefix, '.') = '\0';
13661 filename_info = getStringCat2(filename_prefix, ".txt");
13663 if (fileExists(filename_info))
13664 setup_file_hash = loadSetupFileHash(filename_info);
13666 free(filename_prefix);
13667 free(filename_info);
13669 if (setup_file_hash == NULL)
13671 // ---------- try to add file extension ----------
13673 filename_prefix = getStringCopy(filename_music);
13674 filename_info = getStringCat2(filename_prefix, ".txt");
13676 if (fileExists(filename_info))
13677 setup_file_hash = loadSetupFileHash(filename_info);
13679 free(filename_prefix);
13680 free(filename_info);
13683 if (setup_file_hash == NULL)
13686 // ---------- music file info found ----------
13688 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13690 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13692 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13694 *token_to_value_ptr[i].value_ptr =
13695 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13698 tmp_music_file_info.basename = getStringCopy(basename);
13699 tmp_music_file_info.music = music;
13700 tmp_music_file_info.is_sound = is_sound;
13702 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13703 *new_music_file_info = tmp_music_file_info;
13705 return new_music_file_info;
13708 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13710 return get_music_file_info_ext(basename, music, FALSE);
13713 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13715 return get_music_file_info_ext(basename, sound, TRUE);
13718 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13719 char *basename, boolean is_sound)
13721 for (; list != NULL; list = list->next)
13722 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13728 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13730 return music_info_listed_ext(list, basename, FALSE);
13733 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13735 return music_info_listed_ext(list, basename, TRUE);
13738 void LoadMusicInfo(void)
13740 int num_music_noconf = getMusicListSize_NoConf();
13741 int num_music = getMusicListSize();
13742 int num_sounds = getSoundListSize();
13743 struct FileInfo *music, *sound;
13744 struct MusicFileInfo *next, **new;
13748 while (music_file_info != NULL)
13750 next = music_file_info->next;
13752 checked_free(music_file_info->basename);
13754 checked_free(music_file_info->title_header);
13755 checked_free(music_file_info->artist_header);
13756 checked_free(music_file_info->album_header);
13757 checked_free(music_file_info->year_header);
13758 checked_free(music_file_info->played_header);
13760 checked_free(music_file_info->title);
13761 checked_free(music_file_info->artist);
13762 checked_free(music_file_info->album);
13763 checked_free(music_file_info->year);
13764 checked_free(music_file_info->played);
13766 free(music_file_info);
13768 music_file_info = next;
13771 new = &music_file_info;
13773 // get (configured or unconfigured) music file info for all levels
13774 for (i = leveldir_current->first_level;
13775 i <= leveldir_current->last_level; i++)
13779 if (levelset.music[i] != MUS_UNDEFINED)
13781 // get music file info for configured level music
13782 music_nr = levelset.music[i];
13784 else if (num_music_noconf > 0)
13786 // get music file info for unconfigured level music
13787 int level_pos = i - leveldir_current->first_level;
13789 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13796 char *basename = getMusicInfoEntryFilename(music_nr);
13798 if (basename == NULL)
13801 if (!music_info_listed(music_file_info, basename))
13803 *new = get_music_file_info(basename, music_nr);
13806 new = &(*new)->next;
13810 // get music file info for all remaining configured music files
13811 for (i = 0; i < num_music; i++)
13813 music = getMusicListEntry(i);
13815 if (music->filename == NULL)
13818 if (strEqual(music->filename, UNDEFINED_FILENAME))
13821 // a configured file may be not recognized as music
13822 if (!FileIsMusic(music->filename))
13825 if (!music_info_listed(music_file_info, music->filename))
13827 *new = get_music_file_info(music->filename, i);
13830 new = &(*new)->next;
13834 // get sound file info for all configured sound files
13835 for (i = 0; i < num_sounds; i++)
13837 sound = getSoundListEntry(i);
13839 if (sound->filename == NULL)
13842 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13845 // a configured file may be not recognized as sound
13846 if (!FileIsSound(sound->filename))
13849 if (!sound_info_listed(music_file_info, sound->filename))
13851 *new = get_sound_file_info(sound->filename, i);
13853 new = &(*new)->next;
13857 // add pointers to previous list nodes
13859 struct MusicFileInfo *node = music_file_info;
13861 while (node != NULL)
13864 node->next->prev = node;
13870 static void add_helpanim_entry(int element, int action, int direction,
13871 int delay, int *num_list_entries)
13873 struct HelpAnimInfo *new_list_entry;
13874 (*num_list_entries)++;
13877 checked_realloc(helpanim_info,
13878 *num_list_entries * sizeof(struct HelpAnimInfo));
13879 new_list_entry = &helpanim_info[*num_list_entries - 1];
13881 new_list_entry->element = element;
13882 new_list_entry->action = action;
13883 new_list_entry->direction = direction;
13884 new_list_entry->delay = delay;
13887 static void print_unknown_token(char *filename, char *token, int token_nr)
13892 Warn("unknown token(s) found in config file:");
13893 Warn("- config file: '%s'", filename);
13896 Warn("- token: '%s'", token);
13899 static void print_unknown_token_end(int token_nr)
13905 void LoadHelpAnimInfo(void)
13907 char *filename = getHelpAnimFilename();
13908 SetupFileList *setup_file_list = NULL, *list;
13909 SetupFileHash *element_hash, *action_hash, *direction_hash;
13910 int num_list_entries = 0;
13911 int num_unknown_tokens = 0;
13914 if (fileExists(filename))
13915 setup_file_list = loadSetupFileList(filename);
13917 if (setup_file_list == NULL)
13919 // use reliable default values from static configuration
13920 SetupFileList *insert_ptr;
13922 insert_ptr = setup_file_list =
13923 newSetupFileList(helpanim_config[0].token,
13924 helpanim_config[0].value);
13926 for (i = 1; helpanim_config[i].token; i++)
13927 insert_ptr = addListEntry(insert_ptr,
13928 helpanim_config[i].token,
13929 helpanim_config[i].value);
13932 element_hash = newSetupFileHash();
13933 action_hash = newSetupFileHash();
13934 direction_hash = newSetupFileHash();
13936 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13937 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13939 for (i = 0; i < NUM_ACTIONS; i++)
13940 setHashEntry(action_hash, element_action_info[i].suffix,
13941 i_to_a(element_action_info[i].value));
13943 // do not store direction index (bit) here, but direction value!
13944 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13945 setHashEntry(direction_hash, element_direction_info[i].suffix,
13946 i_to_a(1 << element_direction_info[i].value));
13948 for (list = setup_file_list; list != NULL; list = list->next)
13950 char *element_token, *action_token, *direction_token;
13951 char *element_value, *action_value, *direction_value;
13952 int delay = atoi(list->value);
13954 if (strEqual(list->token, "end"))
13956 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13961 /* first try to break element into element/action/direction parts;
13962 if this does not work, also accept combined "element[.act][.dir]"
13963 elements (like "dynamite.active"), which are unique elements */
13965 if (strchr(list->token, '.') == NULL) // token contains no '.'
13967 element_value = getHashEntry(element_hash, list->token);
13968 if (element_value != NULL) // element found
13969 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13970 &num_list_entries);
13973 // no further suffixes found -- this is not an element
13974 print_unknown_token(filename, list->token, num_unknown_tokens++);
13980 // token has format "<prefix>.<something>"
13982 action_token = strchr(list->token, '.'); // suffix may be action ...
13983 direction_token = action_token; // ... or direction
13985 element_token = getStringCopy(list->token);
13986 *strchr(element_token, '.') = '\0';
13988 element_value = getHashEntry(element_hash, element_token);
13990 if (element_value == NULL) // this is no element
13992 element_value = getHashEntry(element_hash, list->token);
13993 if (element_value != NULL) // combined element found
13994 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13995 &num_list_entries);
13997 print_unknown_token(filename, list->token, num_unknown_tokens++);
13999 free(element_token);
14004 action_value = getHashEntry(action_hash, action_token);
14006 if (action_value != NULL) // action found
14008 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14009 &num_list_entries);
14011 free(element_token);
14016 direction_value = getHashEntry(direction_hash, direction_token);
14018 if (direction_value != NULL) // direction found
14020 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14021 &num_list_entries);
14023 free(element_token);
14028 if (strchr(action_token + 1, '.') == NULL)
14030 // no further suffixes found -- this is not an action nor direction
14032 element_value = getHashEntry(element_hash, list->token);
14033 if (element_value != NULL) // combined element found
14034 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14035 &num_list_entries);
14037 print_unknown_token(filename, list->token, num_unknown_tokens++);
14039 free(element_token);
14044 // token has format "<prefix>.<suffix>.<something>"
14046 direction_token = strchr(action_token + 1, '.');
14048 action_token = getStringCopy(action_token);
14049 *strchr(action_token + 1, '.') = '\0';
14051 action_value = getHashEntry(action_hash, action_token);
14053 if (action_value == NULL) // this is no action
14055 element_value = getHashEntry(element_hash, list->token);
14056 if (element_value != NULL) // combined element found
14057 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14058 &num_list_entries);
14060 print_unknown_token(filename, list->token, num_unknown_tokens++);
14062 free(element_token);
14063 free(action_token);
14068 direction_value = getHashEntry(direction_hash, direction_token);
14070 if (direction_value != NULL) // direction found
14072 add_helpanim_entry(atoi(element_value), atoi(action_value),
14073 atoi(direction_value), delay, &num_list_entries);
14075 free(element_token);
14076 free(action_token);
14081 // this is no direction
14083 element_value = getHashEntry(element_hash, list->token);
14084 if (element_value != NULL) // combined element found
14085 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14086 &num_list_entries);
14088 print_unknown_token(filename, list->token, num_unknown_tokens++);
14090 free(element_token);
14091 free(action_token);
14094 print_unknown_token_end(num_unknown_tokens);
14096 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14097 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14099 freeSetupFileList(setup_file_list);
14100 freeSetupFileHash(element_hash);
14101 freeSetupFileHash(action_hash);
14102 freeSetupFileHash(direction_hash);
14105 for (i = 0; i < num_list_entries; i++)
14106 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14107 EL_NAME(helpanim_info[i].element),
14108 helpanim_info[i].element,
14109 helpanim_info[i].action,
14110 helpanim_info[i].direction,
14111 helpanim_info[i].delay);
14115 void LoadHelpTextInfo(void)
14117 char *filename = getHelpTextFilename();
14120 if (helptext_info != NULL)
14122 freeSetupFileHash(helptext_info);
14123 helptext_info = NULL;
14126 if (fileExists(filename))
14127 helptext_info = loadSetupFileHash(filename);
14129 if (helptext_info == NULL)
14131 // use reliable default values from static configuration
14132 helptext_info = newSetupFileHash();
14134 for (i = 0; helptext_config[i].token; i++)
14135 setHashEntry(helptext_info,
14136 helptext_config[i].token,
14137 helptext_config[i].value);
14141 BEGIN_HASH_ITERATION(helptext_info, itr)
14143 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14144 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14146 END_HASH_ITERATION(hash, itr)
14151 // ----------------------------------------------------------------------------
14153 // ----------------------------------------------------------------------------
14155 #define MAX_NUM_CONVERT_LEVELS 1000
14157 void ConvertLevels(void)
14159 static LevelDirTree *convert_leveldir = NULL;
14160 static int convert_level_nr = -1;
14161 static int num_levels_handled = 0;
14162 static int num_levels_converted = 0;
14163 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14166 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14167 global.convert_leveldir);
14169 if (convert_leveldir == NULL)
14170 Fail("no such level identifier: '%s'", global.convert_leveldir);
14172 leveldir_current = convert_leveldir;
14174 if (global.convert_level_nr != -1)
14176 convert_leveldir->first_level = global.convert_level_nr;
14177 convert_leveldir->last_level = global.convert_level_nr;
14180 convert_level_nr = convert_leveldir->first_level;
14182 PrintLine("=", 79);
14183 Print("Converting levels\n");
14184 PrintLine("-", 79);
14185 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14186 Print("Level series name: '%s'\n", convert_leveldir->name);
14187 Print("Level series author: '%s'\n", convert_leveldir->author);
14188 Print("Number of levels: %d\n", convert_leveldir->levels);
14189 PrintLine("=", 79);
14192 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14193 levels_failed[i] = FALSE;
14195 while (convert_level_nr <= convert_leveldir->last_level)
14197 char *level_filename;
14200 level_nr = convert_level_nr++;
14202 Print("Level %03d: ", level_nr);
14204 LoadLevel(level_nr);
14205 if (level.no_level_file || level.no_valid_file)
14207 Print("(no level)\n");
14211 Print("converting level ... ");
14214 // special case: conversion of some EMC levels as requested by ACME
14215 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14218 level_filename = getDefaultLevelFilename(level_nr);
14219 new_level = !fileExists(level_filename);
14223 SaveLevel(level_nr);
14225 num_levels_converted++;
14227 Print("converted.\n");
14231 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14232 levels_failed[level_nr] = TRUE;
14234 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14237 num_levels_handled++;
14241 PrintLine("=", 79);
14242 Print("Number of levels handled: %d\n", num_levels_handled);
14243 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14244 (num_levels_handled ?
14245 num_levels_converted * 100 / num_levels_handled : 0));
14246 PrintLine("-", 79);
14247 Print("Summary (for automatic parsing by scripts):\n");
14248 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14249 convert_leveldir->identifier, num_levels_converted,
14250 num_levels_handled,
14251 (num_levels_handled ?
14252 num_levels_converted * 100 / num_levels_handled : 0));
14254 if (num_levels_handled != num_levels_converted)
14256 Print(", FAILED:");
14257 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14258 if (levels_failed[i])
14263 PrintLine("=", 79);
14265 CloseAllAndExit(0);
14269 // ----------------------------------------------------------------------------
14270 // create and save images for use in level sketches (raw BMP format)
14271 // ----------------------------------------------------------------------------
14273 void CreateLevelSketchImages(void)
14279 InitElementPropertiesGfxElement();
14281 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14282 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14284 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14286 int element = getMappedElement(i);
14287 char basename1[16];
14288 char basename2[16];
14292 sprintf(basename1, "%04d.bmp", i);
14293 sprintf(basename2, "%04ds.bmp", i);
14295 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14296 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14298 DrawSizedElement(0, 0, element, TILESIZE);
14299 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14301 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14302 Fail("cannot save level sketch image file '%s'", filename1);
14304 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14305 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14307 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14308 Fail("cannot save level sketch image file '%s'", filename2);
14313 // create corresponding SQL statements (for normal and small images)
14316 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14317 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14320 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14321 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14323 // optional: create content for forum level sketch demonstration post
14325 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14328 FreeBitmap(bitmap1);
14329 FreeBitmap(bitmap2);
14332 fprintf(stderr, "\n");
14334 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14336 CloseAllAndExit(0);
14340 // ----------------------------------------------------------------------------
14341 // create and save images for element collecting animations (raw BMP format)
14342 // ----------------------------------------------------------------------------
14344 static boolean createCollectImage(int element)
14346 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14349 void CreateCollectElementImages(void)
14353 int anim_frames = num_steps - 1;
14354 int tile_size = TILESIZE;
14355 int anim_width = tile_size * anim_frames;
14356 int anim_height = tile_size;
14357 int num_collect_images = 0;
14358 int pos_collect_images = 0;
14360 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14361 if (createCollectImage(i))
14362 num_collect_images++;
14364 Info("Creating %d element collecting animation images ...",
14365 num_collect_images);
14367 int dst_width = anim_width * 2;
14368 int dst_height = anim_height * num_collect_images / 2;
14369 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14370 char *basename_bmp = "RocksCollect.bmp";
14371 char *basename_png = "RocksCollect.png";
14372 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14373 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14374 int len_filename_bmp = strlen(filename_bmp);
14375 int len_filename_png = strlen(filename_png);
14376 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14377 char cmd_convert[max_command_len];
14379 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14383 // force using RGBA surface for destination bitmap
14384 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14385 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14387 dst_bitmap->surface =
14388 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14390 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14392 if (!createCollectImage(i))
14395 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14396 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14397 int graphic = el2img(i);
14398 char *token_name = element_info[i].token_name;
14399 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14400 Bitmap *src_bitmap;
14403 Info("- creating collecting image for '%s' ...", token_name);
14405 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14407 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14408 tile_size, tile_size, 0, 0);
14410 // force using RGBA surface for temporary bitmap (using transparent black)
14411 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14412 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14414 tmp_bitmap->surface =
14415 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14417 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14419 for (j = 0; j < anim_frames; j++)
14421 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14422 int frame_size = frame_size_final * num_steps;
14423 int offset = (tile_size - frame_size_final) / 2;
14424 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14426 while (frame_size > frame_size_final)
14430 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14432 FreeBitmap(frame_bitmap);
14434 frame_bitmap = half_bitmap;
14437 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14438 frame_size_final, frame_size_final,
14439 dst_x + j * tile_size + offset, dst_y + offset);
14441 FreeBitmap(frame_bitmap);
14444 tmp_bitmap->surface_masked = NULL;
14446 FreeBitmap(tmp_bitmap);
14448 pos_collect_images++;
14451 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14452 Fail("cannot save element collecting image file '%s'", filename_bmp);
14454 FreeBitmap(dst_bitmap);
14456 Info("Converting image file from BMP to PNG ...");
14458 if (system(cmd_convert) != 0)
14459 Fail("converting image file failed");
14461 unlink(filename_bmp);
14465 CloseAllAndExit(0);
14469 // ----------------------------------------------------------------------------
14470 // create and save images for custom and group elements (raw BMP format)
14471 // ----------------------------------------------------------------------------
14473 void CreateCustomElementImages(char *directory)
14475 char *src_basename = "RocksCE-template.ilbm";
14476 char *dst_basename = "RocksCE.bmp";
14477 char *src_filename = getPath2(directory, src_basename);
14478 char *dst_filename = getPath2(directory, dst_basename);
14479 Bitmap *src_bitmap;
14481 int yoffset_ce = 0;
14482 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14485 InitVideoDefaults();
14487 ReCreateBitmap(&backbuffer, video.width, video.height);
14489 src_bitmap = LoadImage(src_filename);
14491 bitmap = CreateBitmap(TILEX * 16 * 2,
14492 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14495 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14502 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14503 TILEX * x, TILEY * y + yoffset_ce);
14505 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14507 TILEX * x + TILEX * 16,
14508 TILEY * y + yoffset_ce);
14510 for (j = 2; j >= 0; j--)
14514 BlitBitmap(src_bitmap, bitmap,
14515 TILEX + c * 7, 0, 6, 10,
14516 TILEX * x + 6 + j * 7,
14517 TILEY * y + 11 + yoffset_ce);
14519 BlitBitmap(src_bitmap, bitmap,
14520 TILEX + c * 8, TILEY, 6, 10,
14521 TILEX * 16 + TILEX * x + 6 + j * 8,
14522 TILEY * y + 10 + yoffset_ce);
14528 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14535 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14536 TILEX * x, TILEY * y + yoffset_ge);
14538 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14540 TILEX * x + TILEX * 16,
14541 TILEY * y + yoffset_ge);
14543 for (j = 1; j >= 0; j--)
14547 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14548 TILEX * x + 6 + j * 10,
14549 TILEY * y + 11 + yoffset_ge);
14551 BlitBitmap(src_bitmap, bitmap,
14552 TILEX + c * 8, TILEY + 12, 6, 10,
14553 TILEX * 16 + TILEX * x + 10 + j * 8,
14554 TILEY * y + 10 + yoffset_ge);
14560 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14561 Fail("cannot save CE graphics file '%s'", dst_filename);
14563 FreeBitmap(bitmap);
14565 CloseAllAndExit(0);