1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
17 #include "libgame/libgame.h"
27 #define ENABLE_UNUSED_CODE 0 // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS 0 // only for historic reference
29 #define ENABLE_RESERVED_CODE 0 // reserved for later use
31 #define CHUNK_ID_LEN 4 // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED 0 // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE -1 // do not write chunk size
35 #define LEVEL_CHUNK_NAME_SIZE MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE MAX_LEVEL_AUTHOR_LEN
38 #define LEVEL_CHUNK_VERS_SIZE 8 // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE 4 // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE 80 // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0 // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE 160 // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11 // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16 // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10 // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE 134 // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15 // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE 74 // size of level GRP1 chunk
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED (2 + (1 + 1) + (1 + 1))
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED 2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED 2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED 2
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED 0
61 #define TAPE_CHUNK_VERS_SIZE 8 // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE 20 // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE 2 // size of screen size chunk
65 #define SCORE_CHUNK_VERS_SIZE 8 // size of file version chunk
67 #define LEVEL_CHUNK_CNT3_SIZE(x) (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x) (96 + (x) * 48)
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER 0
78 #define SAVE_CONF_ALWAYS 1
79 #define SAVE_CONF_WHEN_CHANGED -1
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE 0x00
83 #define CONF_MASK_2_BYTE 0x40
84 #define CONF_MASK_4_BYTE 0x80
85 #define CONF_MASK_MULTI_BYTES 0xc0
87 #define CONF_MASK_BYTES 0xc0
88 #define CONF_MASK_TOKEN 0x3f
90 #define CONF_VALUE_1_BYTE(x) (CONF_MASK_1_BYTE | (x))
91 #define CONF_VALUE_2_BYTE(x) (CONF_MASK_2_BYTE | (x))
92 #define CONF_VALUE_4_BYTE(x) (CONF_MASK_4_BYTE | (x))
93 #define CONF_VALUE_MULTI_BYTES(x) (CONF_MASK_MULTI_BYTES | (x))
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x) CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x) CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x) CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x) CONF_VALUE_MULTI_BYTES(x)
101 #define CONF_VALUE_NUM_BYTES(x) ((x) == CONF_MASK_1_BYTE ? 1 : \
102 (x) == CONF_MASK_2_BYTE ? 2 : \
103 (x) == CONF_MASK_4_BYTE ? 4 : 0)
105 #define CONF_CONTENT_NUM_ELEMENTS (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES (2)
109 #define CONF_ENTITY_NUM_BYTES(t) ((t) == TYPE_ELEMENT || \
110 (t) == TYPE_ELEMENT_LIST ? \
111 CONF_ELEMENT_NUM_BYTES : \
112 (t) == TYPE_CONTENT || \
113 (t) == TYPE_CONTENT_LIST ? \
114 CONF_CONTENT_NUM_BYTES : 1)
116 #define CONF_ELEMENT_BYTE_POS(i) ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i) ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118 (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS + \
122 #define CONF_CONTENT_BYTE_POS(c,x,y) (CONF_CONTENT_ELEMENT_POS(c,x,y) * \
123 CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125 (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
140 struct LevelFileConfigInfo
142 int element; // element for which data is to be stored
143 int save_type; // save data always, never or when changed
144 int data_type; // data type (used internally, not stored)
145 int conf_type; // micro chunk identifier (stored in file)
148 void *value; // variable that holds the data to be stored
149 int default_value; // initial default value for this variable
152 void *value_copy; // variable that holds the data to be copied
153 void *num_entities; // number of entities for multi-byte data
154 int default_num_entities; // default number of entities for this data
155 int max_num_entities; // maximal number of entities for this data
156 char *default_string; // optional default string for string data
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 // ---------- values not related to single elements -------------------------
164 -1, SAVE_CONF_ALWAYS,
165 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
166 &li.game_engine_type, GAME_ENGINE_TYPE_RND
169 -1, SAVE_CONF_ALWAYS,
170 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
171 &li.fieldx, STD_LEV_FIELDX
174 -1, SAVE_CONF_ALWAYS,
175 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
176 &li.fieldy, STD_LEV_FIELDY
179 -1, SAVE_CONF_ALWAYS,
180 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
184 -1, SAVE_CONF_ALWAYS,
185 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
190 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
195 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
196 &li.use_step_counter, FALSE
200 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
201 &li.wind_direction_initial, MV_NONE
205 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
206 &li.em_slippery_gems, FALSE
210 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
211 &li.use_custom_template, FALSE
215 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
216 &li.can_move_into_acid_bits, ~0 // default: everything can
220 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
221 &li.dont_collide_with_bits, ~0 // default: always deadly
225 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
226 &li.em_explodes_by_fire, FALSE
230 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
231 &li.score[SC_TIME_BONUS], 1
235 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
236 &li.auto_exit_sokoban, FALSE
240 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
241 &li.auto_count_gems, FALSE
245 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
246 &li.solved_by_one_player, FALSE
250 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
251 &li.time_score_base, 1
255 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
256 &li.rate_time_over_score, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
261 &li.bd_intermission, FALSE
265 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
266 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
271 &li.bd_pal_timing, FALSE
275 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
276 &li.bd_cycle_delay_ms, 200
280 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
281 &li.bd_cycle_delay_c64, 0
285 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
286 &li.bd_hatching_delay_cycles, 21
290 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
291 &li.bd_hatching_delay_seconds, 2
295 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
296 &li.bd_line_shifting_borders, FALSE
300 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
301 &li.bd_scan_first_and_last_row, TRUE
305 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
306 &li.bd_short_explosions, TRUE
310 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
311 &li.bd_gravity_affects_all, TRUE
315 TYPE_INTEGER, CONF_VALUE_8_BIT(24),
316 &li.bd_cave_random_seed_c64, 0
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
328 // (these values are the same for each player)
331 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
332 &li.block_last_field, FALSE // default case for EM levels
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
337 &li.sp_block_last_field, TRUE // default case for SP levels
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
342 &li.instant_relocation, FALSE
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
347 &li.can_pass_to_walkable, FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
352 &li.block_snap_field, TRUE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
357 &li.continuous_snapping, TRUE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
362 &li.shifted_relocation, FALSE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
367 &li.lazy_relocation, FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
372 &li.finish_dig_collect, TRUE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
377 &li.keep_walkable_ce, FALSE
380 // (these values are different for each player)
383 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
384 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
388 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
389 &li.initial_player_gravity[0], FALSE
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
394 &li.use_start_element[0], FALSE
398 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
399 &li.start_element[0], EL_PLAYER_1
403 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
404 &li.use_artwork_element[0], FALSE
408 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
409 &li.artwork_element[0], EL_PLAYER_1
413 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
414 &li.use_explosion_element[0], FALSE
418 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
419 &li.explosion_element[0], EL_PLAYER_1
423 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
424 &li.use_initial_inventory[0], FALSE
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
429 &li.initial_inventory_size[0], 1
433 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
434 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
440 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
441 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
445 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
446 &li.initial_player_gravity[1], FALSE
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
451 &li.use_start_element[1], FALSE
455 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
456 &li.start_element[1], EL_PLAYER_2
460 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
461 &li.use_artwork_element[1], FALSE
465 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
466 &li.artwork_element[1], EL_PLAYER_2
470 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
471 &li.use_explosion_element[1], FALSE
475 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
476 &li.explosion_element[1], EL_PLAYER_2
480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
481 &li.use_initial_inventory[1], FALSE
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
486 &li.initial_inventory_size[1], 1
490 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
491 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
497 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
498 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
502 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
503 &li.initial_player_gravity[2], FALSE
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
508 &li.use_start_element[2], FALSE
512 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
513 &li.start_element[2], EL_PLAYER_3
517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
518 &li.use_artwork_element[2], FALSE
522 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
523 &li.artwork_element[2], EL_PLAYER_3
527 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
528 &li.use_explosion_element[2], FALSE
532 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
533 &li.explosion_element[2], EL_PLAYER_3
537 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
538 &li.use_initial_inventory[2], FALSE
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
543 &li.initial_inventory_size[2], 1
547 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
548 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
554 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
555 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
559 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
560 &li.initial_player_gravity[3], FALSE
564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
565 &li.use_start_element[3], FALSE
569 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
570 &li.start_element[3], EL_PLAYER_4
574 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
575 &li.use_artwork_element[3], FALSE
579 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
580 &li.artwork_element[3], EL_PLAYER_4
584 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
585 &li.use_explosion_element[3], FALSE
589 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
590 &li.explosion_element[3], EL_PLAYER_4
594 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
595 &li.use_initial_inventory[3], FALSE
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
600 &li.initial_inventory_size[3], 1
604 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
605 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
609 // (these values are only valid for BD style levels)
610 // (some values for BD style amoeba following below)
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
614 &li.bd_diagonal_movements, FALSE
618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
619 &li.bd_topmost_player_active, TRUE
623 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
624 &li.bd_pushing_prob, 25
628 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
629 &li.bd_pushing_prob_with_sweet, 100
633 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
634 &li.bd_push_mega_rock_with_sweet, FALSE
638 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
639 &li.bd_snap_element, EL_EMPTY
644 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
645 &li.score[SC_DIAMOND_EXTRA], 20
649 EL_BD_MAGIC_WALL, -1,
650 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
651 &li.bd_magic_wall_wait_hatching, FALSE
654 EL_BD_MAGIC_WALL, -1,
655 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
656 &li.bd_magic_wall_stops_amoeba, TRUE
661 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
662 &li.bd_clock_extra_time, 30
666 EL_BD_VOODOO_DOLL, -1,
667 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
668 &li.bd_voodoo_collects_diamonds, FALSE
671 EL_BD_VOODOO_DOLL, -1,
672 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
673 &li.bd_voodoo_hurt_kills_player, FALSE
676 EL_BD_VOODOO_DOLL, -1,
677 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
678 &li.bd_voodoo_dies_by_rock, FALSE
681 EL_BD_VOODOO_DOLL, -1,
682 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
683 &li.bd_voodoo_vanish_by_explosion, TRUE
686 EL_BD_VOODOO_DOLL, -1,
687 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
688 &li.bd_voodoo_penalty_time, 30
693 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
694 &li.bd_slime_is_predictable, TRUE
698 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
699 &li.bd_slime_permeability_rate, 100
703 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
704 &li.bd_slime_permeability_bits_c64, 0
708 TYPE_INTEGER, CONF_VALUE_32_BIT(1),
709 &li.bd_slime_random_seed_c64, -1
714 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
715 &li.bd_acid_eats_element, EL_BD_SAND
719 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
720 &li.bd_acid_spread_rate, 3
724 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
725 &li.bd_acid_turns_to_element, EL_EMPTY
730 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
731 &li.bd_biter_move_delay, 0
735 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
736 &li.bd_biter_eats_element, EL_BD_DIAMOND
741 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
742 &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
746 EL_BD_EXPANDABLE_WALL_ANY, -1,
747 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
748 &li.bd_change_expanding_wall, FALSE
752 EL_BD_REPLICATOR, -1,
753 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
754 &li.bd_replicators_active, TRUE
757 EL_BD_REPLICATOR, -1,
758 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
759 &li.bd_replicator_create_delay, 4
763 EL_BD_CONVEYOR_LEFT, -1,
764 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
765 &li.bd_conveyor_belts_active, TRUE
768 EL_BD_CONVEYOR_LEFT, -1,
769 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
770 &li.bd_conveyor_belts_changed, FALSE
773 // (the following values are related to various game elements)
777 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
778 &li.score[SC_EMERALD], 10
783 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
784 &li.score[SC_DIAMOND], 10
789 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
790 &li.score[SC_BUG], 10
795 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
796 &li.score[SC_SPACESHIP], 10
801 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
802 &li.score[SC_PACMAN], 10
807 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
808 &li.score[SC_NUT], 10
813 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
814 &li.score[SC_DYNAMITE], 10
819 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
820 &li.score[SC_KEY], 10
825 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
826 &li.score[SC_PEARL], 10
831 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
832 &li.score[SC_CRYSTAL], 10
835 // (amoeba values used by R'n'D game engine only)
838 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
839 &li.amoeba_content, EL_DIAMOND
843 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
848 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
849 &li.grow_into_diggable, TRUE
851 // (amoeba values used by BD game engine only)
854 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
855 &li.bd_amoeba_wait_for_hatching, FALSE
859 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
860 &li.bd_amoeba_start_immediately, TRUE
864 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
865 &li.bd_amoeba_2_explode_by_amoeba, TRUE
869 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
870 &li.bd_amoeba_threshold_too_big, 200
874 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
875 &li.bd_amoeba_slow_growth_time, 200
879 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
880 &li.bd_amoeba_slow_growth_rate, 3
884 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
885 &li.bd_amoeba_fast_growth_rate, 25
889 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
890 &li.bd_amoeba_content_too_big, EL_BD_ROCK
894 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
895 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
900 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
901 &li.bd_amoeba_2_threshold_too_big, 200
905 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
906 &li.bd_amoeba_2_slow_growth_time, 200
910 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
911 &li.bd_amoeba_2_slow_growth_rate, 3
915 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
916 &li.bd_amoeba_2_fast_growth_rate, 25
920 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
921 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
925 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
926 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
930 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
931 &li.bd_amoeba_2_content_exploding, EL_EMPTY
935 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
936 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
941 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
942 &li.yamyam_content, EL_ROCK, NULL,
943 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
947 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
948 &li.score[SC_YAMYAM], 10
953 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
954 &li.score[SC_ROBOT], 10
958 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
964 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
970 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
971 &li.time_magic_wall, 10
976 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
977 &li.game_of_life[0], 2
981 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
982 &li.game_of_life[1], 3
986 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
987 &li.game_of_life[2], 3
991 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
992 &li.game_of_life[3], 3
996 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
997 &li.use_life_bugs, FALSE
1002 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1007 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1012 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1017 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1022 EL_TIMEGATE_SWITCH, -1,
1023 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1024 &li.time_timegate, 10
1028 EL_LIGHT_SWITCH_ACTIVE, -1,
1029 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1034 EL_SHIELD_NORMAL, -1,
1035 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1036 &li.shield_normal_time, 10
1039 EL_SHIELD_NORMAL, -1,
1040 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1041 &li.score[SC_SHIELD], 10
1045 EL_SHIELD_DEADLY, -1,
1046 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1047 &li.shield_deadly_time, 10
1050 EL_SHIELD_DEADLY, -1,
1051 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1052 &li.score[SC_SHIELD], 10
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1062 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1063 &li.extra_time_score, 10
1067 EL_TIME_ORB_FULL, -1,
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1069 &li.time_orb_time, 10
1072 EL_TIME_ORB_FULL, -1,
1073 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1074 &li.use_time_orb_bug, FALSE
1079 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1080 &li.use_spring_bug, FALSE
1085 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1086 &li.android_move_time, 10
1090 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1091 &li.android_clone_time, 10
1094 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1095 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1096 &li.android_clone_element[0], EL_EMPTY, NULL,
1097 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1101 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1102 &li.android_clone_element[0], EL_EMPTY, NULL,
1103 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1108 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1109 &li.lenses_score, 10
1113 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1118 EL_EMC_MAGNIFIER, -1,
1119 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1120 &li.magnify_score, 10
1123 EL_EMC_MAGNIFIER, -1,
1124 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1125 &li.magnify_time, 10
1129 EL_EMC_MAGIC_BALL, -1,
1130 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1134 EL_EMC_MAGIC_BALL, -1,
1135 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1136 &li.ball_random, FALSE
1139 EL_EMC_MAGIC_BALL, -1,
1140 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1141 &li.ball_active_initial, FALSE
1144 EL_EMC_MAGIC_BALL, -1,
1145 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1146 &li.ball_content, EL_EMPTY, NULL,
1147 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1151 EL_SOKOBAN_FIELD_EMPTY, -1,
1152 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1153 &li.sb_fields_needed, TRUE
1157 EL_SOKOBAN_OBJECT, -1,
1158 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1159 &li.sb_objects_needed, TRUE
1164 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1165 &li.mm_laser_red, FALSE
1169 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1170 &li.mm_laser_green, FALSE
1174 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1175 &li.mm_laser_blue, TRUE
1180 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1181 &li.df_laser_red, TRUE
1185 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1186 &li.df_laser_green, TRUE
1190 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1191 &li.df_laser_blue, FALSE
1195 EL_MM_FUSE_ACTIVE, -1,
1196 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1197 &li.mm_time_fuse, 25
1201 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1202 &li.mm_time_bomb, 75
1206 EL_MM_GRAY_BALL, -1,
1207 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1208 &li.mm_time_ball, 75
1211 EL_MM_GRAY_BALL, -1,
1212 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1213 &li.mm_ball_choice_mode, ANIM_RANDOM
1216 EL_MM_GRAY_BALL, -1,
1217 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1218 &li.mm_ball_content, EL_EMPTY, NULL,
1219 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1222 EL_MM_GRAY_BALL, -1,
1223 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1224 &li.rotate_mm_ball_content, TRUE
1227 EL_MM_GRAY_BALL, -1,
1228 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1229 &li.explode_mm_ball, FALSE
1233 EL_MM_STEEL_BLOCK, -1,
1234 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1235 &li.mm_time_block, 75
1238 EL_MM_LIGHTBALL, -1,
1239 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1240 &li.score[SC_ELEM_BONUS], 10
1250 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1254 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1255 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1259 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1260 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1265 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1266 &xx_envelope.autowrap, FALSE
1270 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1271 &xx_envelope.centered, FALSE
1276 TYPE_STRING, CONF_VALUE_BYTES(1),
1277 &xx_envelope.text, -1, NULL,
1278 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1279 &xx_default_string_empty[0]
1289 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1293 TYPE_STRING, CONF_VALUE_BYTES(1),
1294 &xx_ei.description[0], -1,
1295 &yy_ei.description[0],
1296 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1297 &xx_default_description[0]
1302 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1303 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1304 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1306 #if ENABLE_RESERVED_CODE
1307 // (reserved for later use)
1310 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1311 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1312 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1319 &xx_ei.use_gfx_element, FALSE,
1320 &yy_ei.use_gfx_element
1324 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1325 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1326 &yy_ei.gfx_element_initial
1331 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1332 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1333 &yy_ei.access_direction
1338 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1339 &xx_ei.collect_score_initial, 10,
1340 &yy_ei.collect_score_initial
1344 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1345 &xx_ei.collect_count_initial, 1,
1346 &yy_ei.collect_count_initial
1351 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1352 &xx_ei.ce_value_fixed_initial, 0,
1353 &yy_ei.ce_value_fixed_initial
1357 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1358 &xx_ei.ce_value_random_initial, 0,
1359 &yy_ei.ce_value_random_initial
1363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1364 &xx_ei.use_last_ce_value, FALSE,
1365 &yy_ei.use_last_ce_value
1370 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1371 &xx_ei.push_delay_fixed, 8,
1372 &yy_ei.push_delay_fixed
1376 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1377 &xx_ei.push_delay_random, 8,
1378 &yy_ei.push_delay_random
1382 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1383 &xx_ei.drop_delay_fixed, 0,
1384 &yy_ei.drop_delay_fixed
1388 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1389 &xx_ei.drop_delay_random, 0,
1390 &yy_ei.drop_delay_random
1394 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1395 &xx_ei.move_delay_fixed, 0,
1396 &yy_ei.move_delay_fixed
1400 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1401 &xx_ei.move_delay_random, 0,
1402 &yy_ei.move_delay_random
1406 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1407 &xx_ei.step_delay_fixed, 0,
1408 &yy_ei.step_delay_fixed
1412 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1413 &xx_ei.step_delay_random, 0,
1414 &yy_ei.step_delay_random
1419 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1420 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1425 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1426 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1427 &yy_ei.move_direction_initial
1431 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1432 &xx_ei.move_stepsize, TILEX / 8,
1433 &yy_ei.move_stepsize
1438 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1439 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1440 &yy_ei.move_enter_element
1444 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1445 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1446 &yy_ei.move_leave_element
1450 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1451 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1452 &yy_ei.move_leave_type
1457 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1458 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1459 &yy_ei.slippery_type
1464 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1465 &xx_ei.explosion_type, EXPLODES_3X3,
1466 &yy_ei.explosion_type
1470 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1471 &xx_ei.explosion_delay, 16,
1472 &yy_ei.explosion_delay
1476 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1477 &xx_ei.ignition_delay, 8,
1478 &yy_ei.ignition_delay
1483 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1484 &xx_ei.content, EL_EMPTY_SPACE,
1486 &xx_num_contents, 1, 1
1489 // ---------- "num_change_pages" must be the last entry ---------------------
1492 -1, SAVE_CONF_ALWAYS,
1493 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1494 &xx_ei.num_change_pages, 1,
1495 &yy_ei.num_change_pages
1506 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1508 // ---------- "current_change_page" must be the first entry -----------------
1511 -1, SAVE_CONF_ALWAYS,
1512 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1513 &xx_current_change_page, -1
1516 // ---------- (the remaining entries can be in any order) -------------------
1520 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1521 &xx_change.can_change, FALSE
1526 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1527 &xx_event_bits[0], 0
1531 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1532 &xx_event_bits[1], 0
1537 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1538 &xx_change.trigger_player, CH_PLAYER_ANY
1542 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1543 &xx_change.trigger_side, CH_SIDE_ANY
1547 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1548 &xx_change.trigger_page, CH_PAGE_ANY
1553 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1554 &xx_change.target_element, EL_EMPTY_SPACE
1559 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1560 &xx_change.delay_fixed, 0
1564 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1565 &xx_change.delay_random, 0
1569 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1570 &xx_change.delay_frames, FRAMES_PER_SECOND
1575 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1576 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1581 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1582 &xx_change.explode, FALSE
1586 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1587 &xx_change.use_target_content, FALSE
1591 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1592 &xx_change.only_if_complete, FALSE
1596 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1597 &xx_change.use_random_replace, FALSE
1601 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1602 &xx_change.random_percentage, 100
1606 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1607 &xx_change.replace_when, CP_WHEN_EMPTY
1612 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1613 &xx_change.has_action, FALSE
1617 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1618 &xx_change.action_type, CA_NO_ACTION
1622 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1623 &xx_change.action_mode, CA_MODE_UNDEFINED
1627 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1628 &xx_change.action_arg, CA_ARG_UNDEFINED
1633 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1634 &xx_change.action_element, EL_EMPTY_SPACE
1639 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1640 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1641 &xx_num_contents, 1, 1
1651 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1655 TYPE_STRING, CONF_VALUE_BYTES(1),
1656 &xx_ei.description[0], -1, NULL,
1657 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1658 &xx_default_description[0]
1663 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1664 &xx_ei.use_gfx_element, FALSE
1668 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1669 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1674 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1675 &xx_group.choice_mode, ANIM_RANDOM
1680 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1681 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1682 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1692 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1696 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1697 &xx_ei.use_gfx_element, FALSE
1701 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1702 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1712 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1716 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1717 &li.block_snap_field, TRUE
1721 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1722 &li.continuous_snapping, TRUE
1726 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1727 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1731 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1732 &li.use_start_element[0], FALSE
1736 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1737 &li.start_element[0], EL_PLAYER_1
1741 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1742 &li.use_artwork_element[0], FALSE
1746 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1747 &li.artwork_element[0], EL_PLAYER_1
1751 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1752 &li.use_explosion_element[0], FALSE
1756 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1757 &li.explosion_element[0], EL_PLAYER_1
1772 filetype_id_list[] =
1774 { LEVEL_FILE_TYPE_RND, "RND" },
1775 { LEVEL_FILE_TYPE_BD, "BD" },
1776 { LEVEL_FILE_TYPE_EM, "EM" },
1777 { LEVEL_FILE_TYPE_SP, "SP" },
1778 { LEVEL_FILE_TYPE_DX, "DX" },
1779 { LEVEL_FILE_TYPE_SB, "SB" },
1780 { LEVEL_FILE_TYPE_DC, "DC" },
1781 { LEVEL_FILE_TYPE_MM, "MM" },
1782 { LEVEL_FILE_TYPE_MM, "DF" },
1787 // ============================================================================
1788 // level file functions
1789 // ============================================================================
1791 static boolean check_special_flags(char *flag)
1793 if (strEqual(options.special_flags, flag) ||
1794 strEqual(leveldir_current->special_flags, flag))
1800 static struct DateInfo getCurrentDate(void)
1802 time_t epoch_seconds = time(NULL);
1803 struct tm *now = localtime(&epoch_seconds);
1804 struct DateInfo date;
1806 date.year = now->tm_year + 1900;
1807 date.month = now->tm_mon + 1;
1808 date.day = now->tm_mday;
1810 date.src = DATE_SRC_CLOCK;
1815 static void resetEventFlags(struct ElementChangeInfo *change)
1819 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1820 change->has_event[i] = FALSE;
1823 static void resetEventBits(void)
1827 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1828 xx_event_bits[i] = 0;
1831 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1835 /* important: only change event flag if corresponding event bit is set
1836 (this is because all xx_event_bits[] values are loaded separately,
1837 and all xx_event_bits[] values are set back to zero before loading
1838 another value xx_event_bits[x] (each value representing 32 flags)) */
1840 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1841 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1842 change->has_event[i] = TRUE;
1845 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1849 /* in contrast to the above function setEventFlagsFromEventBits(), it
1850 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1851 depending on the corresponding change->has_event[i] values here, as
1852 all xx_event_bits[] values are reset in resetEventBits() before */
1854 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1855 if (change->has_event[i])
1856 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1859 static char *getDefaultElementDescription(struct ElementInfo *ei)
1861 static char description[MAX_ELEMENT_NAME_LEN + 1];
1862 char *default_description = (ei->custom_description != NULL ?
1863 ei->custom_description :
1864 ei->editor_description);
1867 // always start with reliable default values
1868 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1869 description[i] = '\0';
1871 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1872 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1874 return &description[0];
1877 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1879 char *default_description = getDefaultElementDescription(ei);
1882 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1883 ei->description[i] = default_description[i];
1886 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1890 for (i = 0; conf[i].data_type != -1; i++)
1892 int default_value = conf[i].default_value;
1893 int data_type = conf[i].data_type;
1894 int conf_type = conf[i].conf_type;
1895 int byte_mask = conf_type & CONF_MASK_BYTES;
1897 if (byte_mask == CONF_MASK_MULTI_BYTES)
1899 int default_num_entities = conf[i].default_num_entities;
1900 int max_num_entities = conf[i].max_num_entities;
1902 *(int *)(conf[i].num_entities) = default_num_entities;
1904 if (data_type == TYPE_STRING)
1906 char *default_string = conf[i].default_string;
1907 char *string = (char *)(conf[i].value);
1909 strncpy(string, default_string, max_num_entities);
1911 else if (data_type == TYPE_ELEMENT_LIST)
1913 int *element_array = (int *)(conf[i].value);
1916 for (j = 0; j < max_num_entities; j++)
1917 element_array[j] = default_value;
1919 else if (data_type == TYPE_CONTENT_LIST)
1921 struct Content *content = (struct Content *)(conf[i].value);
1924 for (c = 0; c < max_num_entities; c++)
1925 for (y = 0; y < 3; y++)
1926 for (x = 0; x < 3; x++)
1927 content[c].e[x][y] = default_value;
1930 else // constant size configuration data (1, 2 or 4 bytes)
1932 if (data_type == TYPE_BOOLEAN)
1933 *(boolean *)(conf[i].value) = default_value;
1935 *(int *) (conf[i].value) = default_value;
1940 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1944 for (i = 0; conf[i].data_type != -1; i++)
1946 int data_type = conf[i].data_type;
1947 int conf_type = conf[i].conf_type;
1948 int byte_mask = conf_type & CONF_MASK_BYTES;
1950 if (byte_mask == CONF_MASK_MULTI_BYTES)
1952 int max_num_entities = conf[i].max_num_entities;
1954 if (data_type == TYPE_STRING)
1956 char *string = (char *)(conf[i].value);
1957 char *string_copy = (char *)(conf[i].value_copy);
1959 strncpy(string_copy, string, max_num_entities);
1961 else if (data_type == TYPE_ELEMENT_LIST)
1963 int *element_array = (int *)(conf[i].value);
1964 int *element_array_copy = (int *)(conf[i].value_copy);
1967 for (j = 0; j < max_num_entities; j++)
1968 element_array_copy[j] = element_array[j];
1970 else if (data_type == TYPE_CONTENT_LIST)
1972 struct Content *content = (struct Content *)(conf[i].value);
1973 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1976 for (c = 0; c < max_num_entities; c++)
1977 for (y = 0; y < 3; y++)
1978 for (x = 0; x < 3; x++)
1979 content_copy[c].e[x][y] = content[c].e[x][y];
1982 else // constant size configuration data (1, 2 or 4 bytes)
1984 if (data_type == TYPE_BOOLEAN)
1985 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1987 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1992 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1996 xx_ei = *ei_from; // copy element data into temporary buffer
1997 yy_ei = *ei_to; // copy element data into temporary buffer
1999 copyConfigFromConfigList(chunk_config_CUSX_base);
2004 // ---------- reinitialize and copy change pages ----------
2006 ei_to->num_change_pages = ei_from->num_change_pages;
2007 ei_to->current_change_page = ei_from->current_change_page;
2009 setElementChangePages(ei_to, ei_to->num_change_pages);
2011 for (i = 0; i < ei_to->num_change_pages; i++)
2012 ei_to->change_page[i] = ei_from->change_page[i];
2014 // ---------- copy group element info ----------
2015 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2016 *ei_to->group = *ei_from->group;
2018 // mark this custom element as modified
2019 ei_to->modified_settings = TRUE;
2022 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2024 int change_page_size = sizeof(struct ElementChangeInfo);
2026 ei->num_change_pages = MAX(1, change_pages);
2029 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2031 if (ei->current_change_page >= ei->num_change_pages)
2032 ei->current_change_page = ei->num_change_pages - 1;
2034 ei->change = &ei->change_page[ei->current_change_page];
2037 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2039 xx_change = *change; // copy change data into temporary buffer
2041 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2043 *change = xx_change;
2045 resetEventFlags(change);
2047 change->direct_action = 0;
2048 change->other_action = 0;
2050 change->pre_change_function = NULL;
2051 change->change_function = NULL;
2052 change->post_change_function = NULL;
2055 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2059 li = *level; // copy level data into temporary buffer
2060 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2061 *level = li; // copy temporary buffer back to level data
2063 setLevelInfoToDefaults_BD();
2064 setLevelInfoToDefaults_EM();
2065 setLevelInfoToDefaults_SP();
2066 setLevelInfoToDefaults_MM();
2068 level->native_bd_level = &native_bd_level;
2069 level->native_em_level = &native_em_level;
2070 level->native_sp_level = &native_sp_level;
2071 level->native_mm_level = &native_mm_level;
2073 level->file_version = FILE_VERSION_ACTUAL;
2074 level->game_version = GAME_VERSION_ACTUAL;
2076 level->creation_date = getCurrentDate();
2078 level->encoding_16bit_field = TRUE;
2079 level->encoding_16bit_yamyam = TRUE;
2080 level->encoding_16bit_amoeba = TRUE;
2082 // clear level name and level author string buffers
2083 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2084 level->name[i] = '\0';
2085 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2086 level->author[i] = '\0';
2088 // set level name and level author to default values
2089 strcpy(level->name, NAMELESS_LEVEL_NAME);
2090 strcpy(level->author, ANONYMOUS_NAME);
2092 // set level playfield to playable default level with player and exit
2093 for (x = 0; x < MAX_LEV_FIELDX; x++)
2094 for (y = 0; y < MAX_LEV_FIELDY; y++)
2095 level->field[x][y] = EL_SAND;
2097 level->field[0][0] = EL_PLAYER_1;
2098 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2100 BorderElement = EL_STEELWALL;
2102 // detect custom elements when loading them
2103 level->file_has_custom_elements = FALSE;
2105 // set all bug compatibility flags to "false" => do not emulate this bug
2106 level->use_action_after_change_bug = FALSE;
2108 if (leveldir_current)
2110 // try to determine better author name than 'anonymous'
2111 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2113 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2114 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2118 switch (LEVELCLASS(leveldir_current))
2120 case LEVELCLASS_TUTORIAL:
2121 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2124 case LEVELCLASS_CONTRIB:
2125 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2126 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2129 case LEVELCLASS_PRIVATE:
2130 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2131 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2135 // keep default value
2142 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2144 static boolean clipboard_elements_initialized = FALSE;
2147 InitElementPropertiesStatic();
2149 li = *level; // copy level data into temporary buffer
2150 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2151 *level = li; // copy temporary buffer back to level data
2153 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2156 struct ElementInfo *ei = &element_info[element];
2158 if (element == EL_MM_GRAY_BALL)
2160 struct LevelInfo_MM *level_mm = level->native_mm_level;
2163 for (j = 0; j < level->num_mm_ball_contents; j++)
2164 level->mm_ball_content[j] =
2165 map_element_MM_to_RND(level_mm->ball_content[j]);
2168 // never initialize clipboard elements after the very first time
2169 // (to be able to use clipboard elements between several levels)
2170 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2173 if (IS_ENVELOPE(element))
2175 int envelope_nr = element - EL_ENVELOPE_1;
2177 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2179 level->envelope[envelope_nr] = xx_envelope;
2182 if (IS_CUSTOM_ELEMENT(element) ||
2183 IS_GROUP_ELEMENT(element) ||
2184 IS_INTERNAL_ELEMENT(element))
2186 xx_ei = *ei; // copy element data into temporary buffer
2188 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2193 setElementChangePages(ei, 1);
2194 setElementChangeInfoToDefaults(ei->change);
2196 if (IS_CUSTOM_ELEMENT(element) ||
2197 IS_GROUP_ELEMENT(element))
2199 setElementDescriptionToDefault(ei);
2201 ei->modified_settings = FALSE;
2204 if (IS_CUSTOM_ELEMENT(element) ||
2205 IS_INTERNAL_ELEMENT(element))
2207 // internal values used in level editor
2209 ei->access_type = 0;
2210 ei->access_layer = 0;
2211 ei->access_protected = 0;
2212 ei->walk_to_action = 0;
2213 ei->smash_targets = 0;
2216 ei->can_explode_by_fire = FALSE;
2217 ei->can_explode_smashed = FALSE;
2218 ei->can_explode_impact = FALSE;
2220 ei->current_change_page = 0;
2223 if (IS_GROUP_ELEMENT(element) ||
2224 IS_INTERNAL_ELEMENT(element))
2226 struct ElementGroupInfo *group;
2228 // initialize memory for list of elements in group
2229 if (ei->group == NULL)
2230 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2234 xx_group = *group; // copy group data into temporary buffer
2236 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2241 if (IS_EMPTY_ELEMENT(element) ||
2242 IS_INTERNAL_ELEMENT(element))
2244 xx_ei = *ei; // copy element data into temporary buffer
2246 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2252 clipboard_elements_initialized = TRUE;
2255 static void setLevelInfoToDefaults(struct LevelInfo *level,
2256 boolean level_info_only,
2257 boolean reset_file_status)
2259 setLevelInfoToDefaults_Level(level);
2261 if (!level_info_only)
2262 setLevelInfoToDefaults_Elements(level);
2264 if (reset_file_status)
2266 level->no_valid_file = FALSE;
2267 level->no_level_file = FALSE;
2270 level->changed = FALSE;
2273 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2275 level_file_info->nr = 0;
2276 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2277 level_file_info->packed = FALSE;
2279 setString(&level_file_info->basename, NULL);
2280 setString(&level_file_info->filename, NULL);
2283 int getMappedElement_SB(int, boolean);
2285 static void ActivateLevelTemplate(void)
2289 if (check_special_flags("load_xsb_to_ces"))
2291 // fill smaller playfields with padding "beyond border wall" elements
2292 if (level.fieldx < level_template.fieldx ||
2293 level.fieldy < level_template.fieldy)
2295 short field[level.fieldx][level.fieldy];
2296 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2297 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2298 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2299 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2301 // copy old playfield (which is smaller than the visible area)
2302 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2303 field[x][y] = level.field[x][y];
2305 // fill new, larger playfield with "beyond border wall" elements
2306 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2307 level.field[x][y] = getMappedElement_SB('_', TRUE);
2309 // copy the old playfield to the middle of the new playfield
2310 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2311 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2313 level.fieldx = new_fieldx;
2314 level.fieldy = new_fieldy;
2318 // Currently there is no special action needed to activate the template
2319 // data, because 'element_info' property settings overwrite the original
2320 // level data, while all other variables do not change.
2322 // Exception: 'from_level_template' elements in the original level playfield
2323 // are overwritten with the corresponding elements at the same position in
2324 // playfield from the level template.
2326 for (x = 0; x < level.fieldx; x++)
2327 for (y = 0; y < level.fieldy; y++)
2328 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2329 level.field[x][y] = level_template.field[x][y];
2331 if (check_special_flags("load_xsb_to_ces"))
2333 struct LevelInfo level_backup = level;
2335 // overwrite all individual level settings from template level settings
2336 level = level_template;
2338 // restore level file info
2339 level.file_info = level_backup.file_info;
2341 // restore playfield size
2342 level.fieldx = level_backup.fieldx;
2343 level.fieldy = level_backup.fieldy;
2345 // restore playfield content
2346 for (x = 0; x < level.fieldx; x++)
2347 for (y = 0; y < level.fieldy; y++)
2348 level.field[x][y] = level_backup.field[x][y];
2350 // restore name and author from individual level
2351 strcpy(level.name, level_backup.name);
2352 strcpy(level.author, level_backup.author);
2354 // restore flag "use_custom_template"
2355 level.use_custom_template = level_backup.use_custom_template;
2359 static boolean checkForPackageFromBasename_BD(char *basename)
2361 // check for native BD level file extensions
2362 if (!strSuffixLower(basename, ".bd") &&
2363 !strSuffixLower(basename, ".bdr") &&
2364 !strSuffixLower(basename, ".brc") &&
2365 !strSuffixLower(basename, ".gds"))
2368 // check for standard single-level BD files (like "001.bd")
2369 if (strSuffixLower(basename, ".bd") &&
2370 strlen(basename) == 6 &&
2371 basename[0] >= '0' && basename[0] <= '9' &&
2372 basename[1] >= '0' && basename[1] <= '9' &&
2373 basename[2] >= '0' && basename[2] <= '9')
2376 // this is a level package in native BD file format
2380 static char *getLevelFilenameFromBasename(char *basename)
2382 static char *filename = NULL;
2384 checked_free(filename);
2386 filename = getPath2(getCurrentLevelDir(), basename);
2391 static int getFileTypeFromBasename(char *basename)
2393 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2395 static char *filename = NULL;
2396 struct stat file_status;
2398 // ---------- try to determine file type from filename ----------
2400 // check for typical filename of a Supaplex level package file
2401 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2402 return LEVEL_FILE_TYPE_SP;
2404 // check for typical filename of a Diamond Caves II level package file
2405 if (strSuffixLower(basename, ".dc") ||
2406 strSuffixLower(basename, ".dc2"))
2407 return LEVEL_FILE_TYPE_DC;
2409 // check for typical filename of a Sokoban level package file
2410 if (strSuffixLower(basename, ".xsb") &&
2411 strchr(basename, '%') == NULL)
2412 return LEVEL_FILE_TYPE_SB;
2414 // check for typical filename of a Boulder Dash (GDash) level package file
2415 if (checkForPackageFromBasename_BD(basename))
2416 return LEVEL_FILE_TYPE_BD;
2418 // ---------- try to determine file type from filesize ----------
2420 checked_free(filename);
2421 filename = getPath2(getCurrentLevelDir(), basename);
2423 if (stat(filename, &file_status) == 0)
2425 // check for typical filesize of a Supaplex level package file
2426 if (file_status.st_size == 170496)
2427 return LEVEL_FILE_TYPE_SP;
2430 return LEVEL_FILE_TYPE_UNKNOWN;
2433 static int getFileTypeFromMagicBytes(char *filename, int type)
2437 if ((file = openFile(filename, MODE_READ)))
2439 char chunk_name[CHUNK_ID_LEN + 1];
2441 getFileChunkBE(file, chunk_name, NULL);
2443 if (strEqual(chunk_name, "MMII") ||
2444 strEqual(chunk_name, "MIRR"))
2445 type = LEVEL_FILE_TYPE_MM;
2453 static boolean checkForPackageFromBasename(char *basename)
2455 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2456 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2458 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2461 static char *getSingleLevelBasenameExt(int nr, char *extension)
2463 static char basename[MAX_FILENAME_LEN];
2466 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2468 sprintf(basename, "%03d.%s", nr, extension);
2473 static char *getSingleLevelBasename(int nr)
2475 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2478 static char *getPackedLevelBasename(int type)
2480 static char basename[MAX_FILENAME_LEN];
2481 char *directory = getCurrentLevelDir();
2483 DirectoryEntry *dir_entry;
2485 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2487 if ((dir = openDirectory(directory)) == NULL)
2489 Warn("cannot read current level directory '%s'", directory);
2494 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2496 char *entry_basename = dir_entry->basename;
2497 int entry_type = getFileTypeFromBasename(entry_basename);
2499 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2501 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2504 strcpy(basename, entry_basename);
2511 closeDirectory(dir);
2516 static char *getSingleLevelFilename(int nr)
2518 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2521 #if ENABLE_UNUSED_CODE
2522 static char *getPackedLevelFilename(int type)
2524 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2528 char *getDefaultLevelFilename(int nr)
2530 return getSingleLevelFilename(nr);
2533 #if ENABLE_UNUSED_CODE
2534 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2538 lfi->packed = FALSE;
2540 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2541 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2545 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2546 int type, char *format, ...)
2548 static char basename[MAX_FILENAME_LEN];
2551 va_start(ap, format);
2552 vsprintf(basename, format, ap);
2556 lfi->packed = FALSE;
2558 setString(&lfi->basename, basename);
2559 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2562 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2568 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2569 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2572 static int getFiletypeFromID(char *filetype_id)
2574 char *filetype_id_lower;
2575 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2578 if (filetype_id == NULL)
2579 return LEVEL_FILE_TYPE_UNKNOWN;
2581 filetype_id_lower = getStringToLower(filetype_id);
2583 for (i = 0; filetype_id_list[i].id != NULL; i++)
2585 char *id_lower = getStringToLower(filetype_id_list[i].id);
2587 if (strEqual(filetype_id_lower, id_lower))
2588 filetype = filetype_id_list[i].filetype;
2592 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2596 free(filetype_id_lower);
2601 char *getLocalLevelTemplateFilename(void)
2603 return getDefaultLevelFilename(-1);
2606 char *getGlobalLevelTemplateFilename(void)
2608 // global variable "leveldir_current" must be modified in the loop below
2609 LevelDirTree *leveldir_current_last = leveldir_current;
2610 char *filename = NULL;
2612 // check for template level in path from current to topmost tree node
2614 while (leveldir_current != NULL)
2616 filename = getDefaultLevelFilename(-1);
2618 if (fileExists(filename))
2621 leveldir_current = leveldir_current->node_parent;
2624 // restore global variable "leveldir_current" modified in above loop
2625 leveldir_current = leveldir_current_last;
2630 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2634 // special case: level number is negative => check for level template file
2637 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2638 getSingleLevelBasename(-1));
2640 // replace local level template filename with global template filename
2641 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2643 // no fallback if template file not existing
2647 // special case: check for file name/pattern specified in "levelinfo.conf"
2648 if (leveldir_current->level_filename != NULL)
2650 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2652 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2653 leveldir_current->level_filename, nr);
2655 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2657 if (fileExists(lfi->filename))
2660 else if (leveldir_current->level_filetype != NULL)
2662 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2664 // check for specified native level file with standard file name
2665 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2666 "%03d.%s", nr, LEVELFILE_EXTENSION);
2667 if (fileExists(lfi->filename))
2671 // check for native Rocks'n'Diamonds level file
2672 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2673 "%03d.%s", nr, LEVELFILE_EXTENSION);
2674 if (fileExists(lfi->filename))
2677 // check for native Boulder Dash level file
2678 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2679 if (fileExists(lfi->filename))
2682 // check for Emerald Mine level file (V1)
2683 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2684 'a' + (nr / 10) % 26, '0' + nr % 10);
2685 if (fileExists(lfi->filename))
2687 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2688 'A' + (nr / 10) % 26, '0' + nr % 10);
2689 if (fileExists(lfi->filename))
2692 // check for Emerald Mine level file (V2 to V5)
2693 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2694 if (fileExists(lfi->filename))
2697 // check for Emerald Mine level file (V6 / single mode)
2698 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2699 if (fileExists(lfi->filename))
2701 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2702 if (fileExists(lfi->filename))
2705 // check for Emerald Mine level file (V6 / teamwork mode)
2706 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2707 if (fileExists(lfi->filename))
2709 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2710 if (fileExists(lfi->filename))
2713 // check for various packed level file formats
2714 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2715 if (fileExists(lfi->filename))
2718 // no known level file found -- use default values (and fail later)
2719 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2720 "%03d.%s", nr, LEVELFILE_EXTENSION);
2723 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2725 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2726 lfi->type = getFileTypeFromBasename(lfi->basename);
2728 if (lfi->type == LEVEL_FILE_TYPE_RND)
2729 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2732 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2734 // always start with reliable default values
2735 setFileInfoToDefaults(level_file_info);
2737 level_file_info->nr = nr; // set requested level number
2739 determineLevelFileInfo_Filename(level_file_info);
2740 determineLevelFileInfo_Filetype(level_file_info);
2743 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2744 struct LevelFileInfo *lfi_to)
2746 lfi_to->nr = lfi_from->nr;
2747 lfi_to->type = lfi_from->type;
2748 lfi_to->packed = lfi_from->packed;
2750 setString(&lfi_to->basename, lfi_from->basename);
2751 setString(&lfi_to->filename, lfi_from->filename);
2754 // ----------------------------------------------------------------------------
2755 // functions for loading R'n'D level
2756 // ----------------------------------------------------------------------------
2758 int getMappedElement(int element)
2760 // remap some (historic, now obsolete) elements
2764 case EL_PLAYER_OBSOLETE:
2765 element = EL_PLAYER_1;
2768 case EL_KEY_OBSOLETE:
2772 case EL_EM_KEY_1_FILE_OBSOLETE:
2773 element = EL_EM_KEY_1;
2776 case EL_EM_KEY_2_FILE_OBSOLETE:
2777 element = EL_EM_KEY_2;
2780 case EL_EM_KEY_3_FILE_OBSOLETE:
2781 element = EL_EM_KEY_3;
2784 case EL_EM_KEY_4_FILE_OBSOLETE:
2785 element = EL_EM_KEY_4;
2788 case EL_ENVELOPE_OBSOLETE:
2789 element = EL_ENVELOPE_1;
2797 if (element >= NUM_FILE_ELEMENTS)
2799 Warn("invalid level element %d", element);
2801 element = EL_UNKNOWN;
2809 static int getMappedElementByVersion(int element, int game_version)
2811 // remap some elements due to certain game version
2813 if (game_version <= VERSION_IDENT(2,2,0,0))
2815 // map game font elements
2816 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2817 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2818 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2819 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2822 if (game_version < VERSION_IDENT(3,0,0,0))
2824 // map Supaplex gravity tube elements
2825 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2826 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2827 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2828 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2835 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2837 level->file_version = getFileVersion(file);
2838 level->game_version = getFileVersion(file);
2843 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2845 level->creation_date.year = getFile16BitBE(file);
2846 level->creation_date.month = getFile8Bit(file);
2847 level->creation_date.day = getFile8Bit(file);
2849 level->creation_date.src = DATE_SRC_LEVELFILE;
2854 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2856 int initial_player_stepsize;
2857 int initial_player_gravity;
2860 level->fieldx = getFile8Bit(file);
2861 level->fieldy = getFile8Bit(file);
2863 level->time = getFile16BitBE(file);
2864 level->gems_needed = getFile16BitBE(file);
2866 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2867 level->name[i] = getFile8Bit(file);
2868 level->name[MAX_LEVEL_NAME_LEN] = 0;
2870 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2871 level->score[i] = getFile8Bit(file);
2873 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2874 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2875 for (y = 0; y < 3; y++)
2876 for (x = 0; x < 3; x++)
2877 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2879 level->amoeba_speed = getFile8Bit(file);
2880 level->time_magic_wall = getFile8Bit(file);
2881 level->time_wheel = getFile8Bit(file);
2882 level->amoeba_content = getMappedElement(getFile8Bit(file));
2884 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2887 for (i = 0; i < MAX_PLAYERS; i++)
2888 level->initial_player_stepsize[i] = initial_player_stepsize;
2890 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2892 for (i = 0; i < MAX_PLAYERS; i++)
2893 level->initial_player_gravity[i] = initial_player_gravity;
2895 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2896 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2898 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2900 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2901 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2902 level->can_move_into_acid_bits = getFile32BitBE(file);
2903 level->dont_collide_with_bits = getFile8Bit(file);
2905 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2906 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2908 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2909 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2910 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2912 level->game_engine_type = getFile8Bit(file);
2914 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2919 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2923 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2924 level->name[i] = getFile8Bit(file);
2925 level->name[MAX_LEVEL_NAME_LEN] = 0;
2930 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2934 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2935 level->author[i] = getFile8Bit(file);
2936 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2941 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2944 int chunk_size_expected = level->fieldx * level->fieldy;
2946 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2947 stored with 16-bit encoding (and should be twice as big then).
2948 Even worse, playfield data was stored 16-bit when only yamyam content
2949 contained 16-bit elements and vice versa. */
2951 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2952 chunk_size_expected *= 2;
2954 if (chunk_size_expected != chunk_size)
2956 ReadUnusedBytesFromFile(file, chunk_size);
2957 return chunk_size_expected;
2960 for (y = 0; y < level->fieldy; y++)
2961 for (x = 0; x < level->fieldx; x++)
2962 level->field[x][y] =
2963 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2968 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2971 int header_size = 4;
2972 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2973 int chunk_size_expected = header_size + content_size;
2975 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2976 stored with 16-bit encoding (and should be twice as big then).
2977 Even worse, playfield data was stored 16-bit when only yamyam content
2978 contained 16-bit elements and vice versa. */
2980 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2981 chunk_size_expected += content_size;
2983 if (chunk_size_expected != chunk_size)
2985 ReadUnusedBytesFromFile(file, chunk_size);
2986 return chunk_size_expected;
2990 level->num_yamyam_contents = getFile8Bit(file);
2994 // correct invalid number of content fields -- should never happen
2995 if (level->num_yamyam_contents < 1 ||
2996 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2997 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2999 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3000 for (y = 0; y < 3; y++)
3001 for (x = 0; x < 3; x++)
3002 level->yamyam_content[i].e[x][y] =
3003 getMappedElement(level->encoding_16bit_field ?
3004 getFile16BitBE(file) : getFile8Bit(file));
3008 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3013 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3015 element = getMappedElement(getFile16BitBE(file));
3016 num_contents = getFile8Bit(file);
3018 getFile8Bit(file); // content x size (unused)
3019 getFile8Bit(file); // content y size (unused)
3021 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3023 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3024 for (y = 0; y < 3; y++)
3025 for (x = 0; x < 3; x++)
3026 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3028 // correct invalid number of content fields -- should never happen
3029 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3030 num_contents = STD_ELEMENT_CONTENTS;
3032 if (element == EL_YAMYAM)
3034 level->num_yamyam_contents = num_contents;
3036 for (i = 0; i < num_contents; i++)
3037 for (y = 0; y < 3; y++)
3038 for (x = 0; x < 3; x++)
3039 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3041 else if (element == EL_BD_AMOEBA)
3043 level->amoeba_content = content_array[0][0][0];
3047 Warn("cannot load content for element '%d'", element);
3053 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3059 int chunk_size_expected;
3061 element = getMappedElement(getFile16BitBE(file));
3062 if (!IS_ENVELOPE(element))
3063 element = EL_ENVELOPE_1;
3065 envelope_nr = element - EL_ENVELOPE_1;
3067 envelope_len = getFile16BitBE(file);
3069 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3070 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3072 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3074 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3075 if (chunk_size_expected != chunk_size)
3077 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3078 return chunk_size_expected;
3081 for (i = 0; i < envelope_len; i++)
3082 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3087 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3089 int num_changed_custom_elements = getFile16BitBE(file);
3090 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3093 if (chunk_size_expected != chunk_size)
3095 ReadUnusedBytesFromFile(file, chunk_size - 2);
3096 return chunk_size_expected;
3099 for (i = 0; i < num_changed_custom_elements; i++)
3101 int element = getMappedElement(getFile16BitBE(file));
3102 int properties = getFile32BitBE(file);
3104 if (IS_CUSTOM_ELEMENT(element))
3105 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3107 Warn("invalid custom element number %d", element);
3109 // older game versions that wrote level files with CUS1 chunks used
3110 // different default push delay values (not yet stored in level file)
3111 element_info[element].push_delay_fixed = 2;
3112 element_info[element].push_delay_random = 8;
3115 level->file_has_custom_elements = TRUE;
3120 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3122 int num_changed_custom_elements = getFile16BitBE(file);
3123 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3126 if (chunk_size_expected != chunk_size)
3128 ReadUnusedBytesFromFile(file, chunk_size - 2);
3129 return chunk_size_expected;
3132 for (i = 0; i < num_changed_custom_elements; i++)
3134 int element = getMappedElement(getFile16BitBE(file));
3135 int custom_target_element = getMappedElement(getFile16BitBE(file));
3137 if (IS_CUSTOM_ELEMENT(element))
3138 element_info[element].change->target_element = custom_target_element;
3140 Warn("invalid custom element number %d", element);
3143 level->file_has_custom_elements = TRUE;
3148 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3150 int num_changed_custom_elements = getFile16BitBE(file);
3151 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3154 if (chunk_size_expected != chunk_size)
3156 ReadUnusedBytesFromFile(file, chunk_size - 2);
3157 return chunk_size_expected;
3160 for (i = 0; i < num_changed_custom_elements; i++)
3162 int element = getMappedElement(getFile16BitBE(file));
3163 struct ElementInfo *ei = &element_info[element];
3164 unsigned int event_bits;
3166 if (!IS_CUSTOM_ELEMENT(element))
3168 Warn("invalid custom element number %d", element);
3170 element = EL_INTERNAL_DUMMY;
3173 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3174 ei->description[j] = getFile8Bit(file);
3175 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3177 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3179 // some free bytes for future properties and padding
3180 ReadUnusedBytesFromFile(file, 7);
3182 ei->use_gfx_element = getFile8Bit(file);
3183 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3185 ei->collect_score_initial = getFile8Bit(file);
3186 ei->collect_count_initial = getFile8Bit(file);
3188 ei->push_delay_fixed = getFile16BitBE(file);
3189 ei->push_delay_random = getFile16BitBE(file);
3190 ei->move_delay_fixed = getFile16BitBE(file);
3191 ei->move_delay_random = getFile16BitBE(file);
3193 ei->move_pattern = getFile16BitBE(file);
3194 ei->move_direction_initial = getFile8Bit(file);
3195 ei->move_stepsize = getFile8Bit(file);
3197 for (y = 0; y < 3; y++)
3198 for (x = 0; x < 3; x++)
3199 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3201 // bits 0 - 31 of "has_event[]"
3202 event_bits = getFile32BitBE(file);
3203 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3204 if (event_bits & (1u << j))
3205 ei->change->has_event[j] = TRUE;
3207 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3209 ei->change->delay_fixed = getFile16BitBE(file);
3210 ei->change->delay_random = getFile16BitBE(file);
3211 ei->change->delay_frames = getFile16BitBE(file);
3213 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3215 ei->change->explode = getFile8Bit(file);
3216 ei->change->use_target_content = getFile8Bit(file);
3217 ei->change->only_if_complete = getFile8Bit(file);
3218 ei->change->use_random_replace = getFile8Bit(file);
3220 ei->change->random_percentage = getFile8Bit(file);
3221 ei->change->replace_when = getFile8Bit(file);
3223 for (y = 0; y < 3; y++)
3224 for (x = 0; x < 3; x++)
3225 ei->change->target_content.e[x][y] =
3226 getMappedElement(getFile16BitBE(file));
3228 ei->slippery_type = getFile8Bit(file);
3230 // some free bytes for future properties and padding
3231 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3233 // mark that this custom element has been modified
3234 ei->modified_settings = TRUE;
3237 level->file_has_custom_elements = TRUE;
3242 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3244 struct ElementInfo *ei;
3245 int chunk_size_expected;
3249 // ---------- custom element base property values (96 bytes) ----------------
3251 element = getMappedElement(getFile16BitBE(file));
3253 if (!IS_CUSTOM_ELEMENT(element))
3255 Warn("invalid custom element number %d", element);
3257 ReadUnusedBytesFromFile(file, chunk_size - 2);
3262 ei = &element_info[element];
3264 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3265 ei->description[i] = getFile8Bit(file);
3266 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3268 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3270 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3272 ei->num_change_pages = getFile8Bit(file);
3274 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3275 if (chunk_size_expected != chunk_size)
3277 ReadUnusedBytesFromFile(file, chunk_size - 43);
3278 return chunk_size_expected;
3281 ei->ce_value_fixed_initial = getFile16BitBE(file);
3282 ei->ce_value_random_initial = getFile16BitBE(file);
3283 ei->use_last_ce_value = getFile8Bit(file);
3285 ei->use_gfx_element = getFile8Bit(file);
3286 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3288 ei->collect_score_initial = getFile8Bit(file);
3289 ei->collect_count_initial = getFile8Bit(file);
3291 ei->drop_delay_fixed = getFile8Bit(file);
3292 ei->push_delay_fixed = getFile8Bit(file);
3293 ei->drop_delay_random = getFile8Bit(file);
3294 ei->push_delay_random = getFile8Bit(file);
3295 ei->move_delay_fixed = getFile16BitBE(file);
3296 ei->move_delay_random = getFile16BitBE(file);
3298 // bits 0 - 15 of "move_pattern" ...
3299 ei->move_pattern = getFile16BitBE(file);
3300 ei->move_direction_initial = getFile8Bit(file);
3301 ei->move_stepsize = getFile8Bit(file);
3303 ei->slippery_type = getFile8Bit(file);
3305 for (y = 0; y < 3; y++)
3306 for (x = 0; x < 3; x++)
3307 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3309 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3310 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3311 ei->move_leave_type = getFile8Bit(file);
3313 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3314 ei->move_pattern |= (getFile16BitBE(file) << 16);
3316 ei->access_direction = getFile8Bit(file);
3318 ei->explosion_delay = getFile8Bit(file);
3319 ei->ignition_delay = getFile8Bit(file);
3320 ei->explosion_type = getFile8Bit(file);
3322 // some free bytes for future custom property values and padding
3323 ReadUnusedBytesFromFile(file, 1);
3325 // ---------- change page property values (48 bytes) ------------------------
3327 setElementChangePages(ei, ei->num_change_pages);
3329 for (i = 0; i < ei->num_change_pages; i++)
3331 struct ElementChangeInfo *change = &ei->change_page[i];
3332 unsigned int event_bits;
3334 // always start with reliable default values
3335 setElementChangeInfoToDefaults(change);
3337 // bits 0 - 31 of "has_event[]" ...
3338 event_bits = getFile32BitBE(file);
3339 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3340 if (event_bits & (1u << j))
3341 change->has_event[j] = TRUE;
3343 change->target_element = getMappedElement(getFile16BitBE(file));
3345 change->delay_fixed = getFile16BitBE(file);
3346 change->delay_random = getFile16BitBE(file);
3347 change->delay_frames = getFile16BitBE(file);
3349 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3351 change->explode = getFile8Bit(file);
3352 change->use_target_content = getFile8Bit(file);
3353 change->only_if_complete = getFile8Bit(file);
3354 change->use_random_replace = getFile8Bit(file);
3356 change->random_percentage = getFile8Bit(file);
3357 change->replace_when = getFile8Bit(file);
3359 for (y = 0; y < 3; y++)
3360 for (x = 0; x < 3; x++)
3361 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3363 change->can_change = getFile8Bit(file);
3365 change->trigger_side = getFile8Bit(file);
3367 change->trigger_player = getFile8Bit(file);
3368 change->trigger_page = getFile8Bit(file);
3370 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3371 CH_PAGE_ANY : (1 << change->trigger_page));
3373 change->has_action = getFile8Bit(file);
3374 change->action_type = getFile8Bit(file);
3375 change->action_mode = getFile8Bit(file);
3376 change->action_arg = getFile16BitBE(file);
3378 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3379 event_bits = getFile8Bit(file);
3380 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3381 if (event_bits & (1u << (j - 32)))
3382 change->has_event[j] = TRUE;
3385 // mark this custom element as modified
3386 ei->modified_settings = TRUE;
3388 level->file_has_custom_elements = TRUE;
3393 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3395 struct ElementInfo *ei;
3396 struct ElementGroupInfo *group;
3400 element = getMappedElement(getFile16BitBE(file));
3402 if (!IS_GROUP_ELEMENT(element))
3404 Warn("invalid group element number %d", element);
3406 ReadUnusedBytesFromFile(file, chunk_size - 2);
3411 ei = &element_info[element];
3413 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3414 ei->description[i] = getFile8Bit(file);
3415 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3417 group = element_info[element].group;
3419 group->num_elements = getFile8Bit(file);
3421 ei->use_gfx_element = getFile8Bit(file);
3422 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3424 group->choice_mode = getFile8Bit(file);
3426 // some free bytes for future values and padding
3427 ReadUnusedBytesFromFile(file, 3);
3429 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3430 group->element[i] = getMappedElement(getFile16BitBE(file));
3432 // mark this group element as modified
3433 element_info[element].modified_settings = TRUE;
3435 level->file_has_custom_elements = TRUE;
3440 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3441 int element, int real_element)
3443 int micro_chunk_size = 0;
3444 int conf_type = getFile8Bit(file);
3445 int byte_mask = conf_type & CONF_MASK_BYTES;
3446 boolean element_found = FALSE;
3449 micro_chunk_size += 1;
3451 if (byte_mask == CONF_MASK_MULTI_BYTES)
3453 int num_bytes = getFile16BitBE(file);
3454 byte *buffer = checked_malloc(num_bytes);
3456 ReadBytesFromFile(file, buffer, num_bytes);
3458 for (i = 0; conf[i].data_type != -1; i++)
3460 if (conf[i].element == element &&
3461 conf[i].conf_type == conf_type)
3463 int data_type = conf[i].data_type;
3464 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3465 int max_num_entities = conf[i].max_num_entities;
3467 if (num_entities > max_num_entities)
3469 Warn("truncating number of entities for element %d from %d to %d",
3470 element, num_entities, max_num_entities);
3472 num_entities = max_num_entities;
3475 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3476 data_type == TYPE_CONTENT_LIST))
3478 // for element and content lists, zero entities are not allowed
3479 Warn("found empty list of entities for element %d", element);
3481 // do not set "num_entities" here to prevent reading behind buffer
3483 *(int *)(conf[i].num_entities) = 1; // at least one is required
3487 *(int *)(conf[i].num_entities) = num_entities;
3490 element_found = TRUE;
3492 if (data_type == TYPE_STRING)
3494 char *string = (char *)(conf[i].value);
3497 for (j = 0; j < max_num_entities; j++)
3498 string[j] = (j < num_entities ? buffer[j] : '\0');
3500 else if (data_type == TYPE_ELEMENT_LIST)
3502 int *element_array = (int *)(conf[i].value);
3505 for (j = 0; j < num_entities; j++)
3507 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3509 else if (data_type == TYPE_CONTENT_LIST)
3511 struct Content *content= (struct Content *)(conf[i].value);
3514 for (c = 0; c < num_entities; c++)
3515 for (y = 0; y < 3; y++)
3516 for (x = 0; x < 3; x++)
3517 content[c].e[x][y] =
3518 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3521 element_found = FALSE;
3527 checked_free(buffer);
3529 micro_chunk_size += 2 + num_bytes;
3531 else // constant size configuration data (1, 2 or 4 bytes)
3533 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3534 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3535 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3537 for (i = 0; conf[i].data_type != -1; i++)
3539 if (conf[i].element == element &&
3540 conf[i].conf_type == conf_type)
3542 int data_type = conf[i].data_type;
3544 if (data_type == TYPE_ELEMENT)
3545 value = getMappedElement(value);
3547 if (data_type == TYPE_BOOLEAN)
3548 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3550 *(int *) (conf[i].value) = value;
3552 element_found = TRUE;
3558 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3563 char *error_conf_chunk_bytes =
3564 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3565 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3566 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3567 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3568 int error_element = real_element;
3570 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3571 error_conf_chunk_bytes, error_conf_chunk_token,
3572 error_element, EL_NAME(error_element));
3575 return micro_chunk_size;
3578 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3580 int real_chunk_size = 0;
3582 li = *level; // copy level data into temporary buffer
3584 while (!checkEndOfFile(file))
3586 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3588 if (real_chunk_size >= chunk_size)
3592 *level = li; // copy temporary buffer back to level data
3594 return real_chunk_size;
3597 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3599 int real_chunk_size = 0;
3601 li = *level; // copy level data into temporary buffer
3603 while (!checkEndOfFile(file))
3605 int element = getMappedElement(getFile16BitBE(file));
3607 real_chunk_size += 2;
3608 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3610 if (real_chunk_size >= chunk_size)
3614 *level = li; // copy temporary buffer back to level data
3616 return real_chunk_size;
3619 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3621 int real_chunk_size = 0;
3623 li = *level; // copy level data into temporary buffer
3625 while (!checkEndOfFile(file))
3627 int element = getMappedElement(getFile16BitBE(file));
3629 real_chunk_size += 2;
3630 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3632 if (real_chunk_size >= chunk_size)
3636 *level = li; // copy temporary buffer back to level data
3638 return real_chunk_size;
3641 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3643 int element = getMappedElement(getFile16BitBE(file));
3644 int envelope_nr = element - EL_ENVELOPE_1;
3645 int real_chunk_size = 2;
3647 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3649 while (!checkEndOfFile(file))
3651 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3654 if (real_chunk_size >= chunk_size)
3658 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3660 return real_chunk_size;
3663 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3665 int element = getMappedElement(getFile16BitBE(file));
3666 int real_chunk_size = 2;
3667 struct ElementInfo *ei = &element_info[element];
3670 xx_ei = *ei; // copy element data into temporary buffer
3672 xx_ei.num_change_pages = -1;
3674 while (!checkEndOfFile(file))
3676 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3678 if (xx_ei.num_change_pages != -1)
3681 if (real_chunk_size >= chunk_size)
3687 if (ei->num_change_pages == -1)
3689 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3692 ei->num_change_pages = 1;
3694 setElementChangePages(ei, 1);
3695 setElementChangeInfoToDefaults(ei->change);
3697 return real_chunk_size;
3700 // initialize number of change pages stored for this custom element
3701 setElementChangePages(ei, ei->num_change_pages);
3702 for (i = 0; i < ei->num_change_pages; i++)
3703 setElementChangeInfoToDefaults(&ei->change_page[i]);
3705 // start with reading properties for the first change page
3706 xx_current_change_page = 0;
3708 while (!checkEndOfFile(file))
3710 // level file might contain invalid change page number
3711 if (xx_current_change_page >= ei->num_change_pages)
3714 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3716 xx_change = *change; // copy change data into temporary buffer
3718 resetEventBits(); // reset bits; change page might have changed
3720 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3723 *change = xx_change;
3725 setEventFlagsFromEventBits(change);
3727 if (real_chunk_size >= chunk_size)
3731 level->file_has_custom_elements = TRUE;
3733 return real_chunk_size;
3736 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3738 int element = getMappedElement(getFile16BitBE(file));
3739 int real_chunk_size = 2;
3740 struct ElementInfo *ei = &element_info[element];
3741 struct ElementGroupInfo *group = ei->group;
3746 xx_ei = *ei; // copy element data into temporary buffer
3747 xx_group = *group; // copy group data into temporary buffer
3749 while (!checkEndOfFile(file))
3751 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3754 if (real_chunk_size >= chunk_size)
3761 level->file_has_custom_elements = TRUE;
3763 return real_chunk_size;
3766 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3768 int element = getMappedElement(getFile16BitBE(file));
3769 int real_chunk_size = 2;
3770 struct ElementInfo *ei = &element_info[element];
3772 xx_ei = *ei; // copy element data into temporary buffer
3774 while (!checkEndOfFile(file))
3776 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3779 if (real_chunk_size >= chunk_size)
3785 level->file_has_custom_elements = TRUE;
3787 return real_chunk_size;
3790 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3791 struct LevelFileInfo *level_file_info,
3792 boolean level_info_only)
3794 char *filename = level_file_info->filename;
3795 char cookie[MAX_LINE_LEN];
3796 char chunk_name[CHUNK_ID_LEN + 1];
3800 if (!(file = openFile(filename, MODE_READ)))
3802 level->no_valid_file = TRUE;
3803 level->no_level_file = TRUE;
3805 if (level_info_only)
3808 Warn("cannot read level '%s' -- using empty level", filename);
3810 if (!setup.editor.use_template_for_new_levels)
3813 // if level file not found, try to initialize level data from template
3814 filename = getGlobalLevelTemplateFilename();
3816 if (!(file = openFile(filename, MODE_READ)))
3819 // default: for empty levels, use level template for custom elements
3820 level->use_custom_template = TRUE;
3822 level->no_valid_file = FALSE;
3825 getFileChunkBE(file, chunk_name, NULL);
3826 if (strEqual(chunk_name, "RND1"))
3828 getFile32BitBE(file); // not used
3830 getFileChunkBE(file, chunk_name, NULL);
3831 if (!strEqual(chunk_name, "CAVE"))
3833 level->no_valid_file = TRUE;
3835 Warn("unknown format of level file '%s'", filename);
3842 else // check for pre-2.0 file format with cookie string
3844 strcpy(cookie, chunk_name);
3845 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3847 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3848 cookie[strlen(cookie) - 1] = '\0';
3850 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3852 level->no_valid_file = TRUE;
3854 Warn("unknown format of level file '%s'", filename);
3861 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3863 level->no_valid_file = TRUE;
3865 Warn("unsupported version of level file '%s'", filename);
3872 // pre-2.0 level files have no game version, so use file version here
3873 level->game_version = level->file_version;
3876 if (level->file_version < FILE_VERSION_1_2)
3878 // level files from versions before 1.2.0 without chunk structure
3879 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3880 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3888 int (*loader)(File *, int, struct LevelInfo *);
3892 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3893 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3894 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3895 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3896 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3897 { "INFO", -1, LoadLevel_INFO },
3898 { "BODY", -1, LoadLevel_BODY },
3899 { "CONT", -1, LoadLevel_CONT },
3900 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3901 { "CNT3", -1, LoadLevel_CNT3 },
3902 { "CUS1", -1, LoadLevel_CUS1 },
3903 { "CUS2", -1, LoadLevel_CUS2 },
3904 { "CUS3", -1, LoadLevel_CUS3 },
3905 { "CUS4", -1, LoadLevel_CUS4 },
3906 { "GRP1", -1, LoadLevel_GRP1 },
3907 { "CONF", -1, LoadLevel_CONF },
3908 { "ELEM", -1, LoadLevel_ELEM },
3909 { "NOTE", -1, LoadLevel_NOTE },
3910 { "CUSX", -1, LoadLevel_CUSX },
3911 { "GRPX", -1, LoadLevel_GRPX },
3912 { "EMPX", -1, LoadLevel_EMPX },
3917 while (getFileChunkBE(file, chunk_name, &chunk_size))
3921 while (chunk_info[i].name != NULL &&
3922 !strEqual(chunk_name, chunk_info[i].name))
3925 if (chunk_info[i].name == NULL)
3927 Warn("unknown chunk '%s' in level file '%s'",
3928 chunk_name, filename);
3930 ReadUnusedBytesFromFile(file, chunk_size);
3932 else if (chunk_info[i].size != -1 &&
3933 chunk_info[i].size != chunk_size)
3935 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3936 chunk_size, chunk_name, filename);
3938 ReadUnusedBytesFromFile(file, chunk_size);
3942 // call function to load this level chunk
3943 int chunk_size_expected =
3944 (chunk_info[i].loader)(file, chunk_size, level);
3946 if (chunk_size_expected < 0)
3948 Warn("error reading chunk '%s' in level file '%s'",
3949 chunk_name, filename);
3954 // the size of some chunks cannot be checked before reading other
3955 // chunks first (like "HEAD" and "BODY") that contain some header
3956 // information, so check them here
3957 if (chunk_size_expected != chunk_size)
3959 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3960 chunk_size, chunk_name, filename);
3972 // ----------------------------------------------------------------------------
3973 // functions for loading BD level
3974 // ----------------------------------------------------------------------------
3976 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3978 struct LevelInfo_BD *level_bd = level->native_bd_level;
3979 GdCave *cave = NULL; // will be changed below
3980 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3981 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3984 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3986 // cave and map newly allocated when set to defaults above
3987 cave = level_bd->cave;
3990 cave->intermission = level->bd_intermission;
3993 cave->level_time[0] = level->time;
3994 cave->level_diamonds[0] = level->gems_needed;
3997 cave->scheduling = level->bd_scheduling_type;
3998 cave->pal_timing = level->bd_pal_timing;
3999 cave->level_speed[0] = level->bd_cycle_delay_ms;
4000 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4001 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4002 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4005 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4006 cave->diamond_value = level->score[SC_EMERALD];
4007 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4009 // compatibility settings
4010 cave->lineshift = level->bd_line_shifting_borders;
4011 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4012 cave->short_explosions = level->bd_short_explosions;
4013 cave->gravity_affects_all = level->bd_gravity_affects_all;
4015 // player properties
4016 cave->diagonal_movements = level->bd_diagonal_movements;
4017 cave->active_is_first_found = level->bd_topmost_player_active;
4018 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4019 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4020 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4021 cave->snap_element = map_element_RND_to_BD(level->bd_snap_element);
4023 // element properties
4024 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4025 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4026 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4027 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4028 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4029 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4030 cave->level_magic_wall_time[0] = level->time_magic_wall;
4031 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4032 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4033 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4034 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4035 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4036 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4037 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4038 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4039 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4040 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4041 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4042 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4043 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4045 cave->amoeba_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_content_too_big);
4046 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
4047 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
4048 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
4049 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
4050 cave->amoeba_2_looks_like = map_element_RND_to_BD(level->bd_amoeba_2_content_looks_like);
4052 cave->slime_predictable = level->bd_slime_is_predictable;
4053 cave->slime_correct_random = level->bd_slime_correct_random;
4054 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4055 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4056 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4057 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4059 cave->acid_eats_this = map_element_RND_to_BD(level->bd_acid_eats_element);
4060 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4061 cave->acid_turns_to = map_element_RND_to_BD(level->bd_acid_turns_to_element);
4063 cave->biter_delay_frame = level->bd_biter_move_delay;
4064 cave->biter_eat = map_element_RND_to_BD(level->bd_biter_eats_element);
4066 cave->bladder_converts_by = map_element_RND_to_BD(level->bd_bladder_converts_by_element);
4068 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4070 cave->replicators_active = level->bd_replicators_active;
4071 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4073 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4074 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4077 strncpy(cave->name, level->name, sizeof(GdString));
4078 cave->name[sizeof(GdString) - 1] = '\0';
4080 // playfield elements
4081 for (x = 0; x < cave->w; x++)
4082 for (y = 0; y < cave->h; y++)
4083 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
4086 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4088 struct LevelInfo_BD *level_bd = level->native_bd_level;
4089 GdCave *cave = level_bd->cave;
4090 int bd_level_nr = level_bd->level_nr;
4093 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4094 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4097 level->bd_intermission = cave->intermission;
4100 level->time = cave->level_time[bd_level_nr];
4101 level->gems_needed = cave->level_diamonds[bd_level_nr];
4104 level->bd_scheduling_type = cave->scheduling;
4105 level->bd_pal_timing = cave->pal_timing;
4106 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4107 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4108 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4109 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4112 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4113 level->score[SC_EMERALD] = cave->diamond_value;
4114 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4116 // compatibility settings
4117 level->bd_line_shifting_borders = cave->lineshift;
4118 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4119 level->bd_short_explosions = cave->short_explosions;
4120 level->bd_gravity_affects_all = cave->gravity_affects_all;
4122 // player properties
4123 level->bd_diagonal_movements = cave->diagonal_movements;
4124 level->bd_topmost_player_active = cave->active_is_first_found;
4125 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4126 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4127 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4128 level->bd_snap_element = map_element_BD_to_RND(cave->snap_element);
4130 // element properties
4131 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4132 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4133 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4134 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4135 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4136 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4137 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4138 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4139 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4140 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4141 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4142 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4143 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4144 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4145 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4146 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4147 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4148 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4149 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4150 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4152 level->bd_amoeba_content_too_big = map_element_BD_to_RND(cave->amoeba_too_big_effect);
4153 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4154 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4155 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4156 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4157 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(cave->amoeba_2_looks_like);
4159 level->bd_slime_is_predictable = cave->slime_predictable;
4160 level->bd_slime_correct_random = cave->slime_correct_random;
4161 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4162 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4163 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4164 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4166 level->bd_acid_eats_element = map_element_BD_to_RND(cave->acid_eats_this);
4167 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4168 level->bd_acid_turns_to_element = map_element_BD_to_RND(cave->acid_turns_to);
4170 level->bd_biter_move_delay = cave->biter_delay_frame;
4171 level->bd_biter_eats_element = map_element_BD_to_RND(cave->biter_eat);
4173 level->bd_bladder_converts_by_element = map_element_BD_to_RND(cave->bladder_converts_by);
4175 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4177 level->bd_replicators_active = cave->replicators_active;
4178 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4180 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4181 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4184 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4186 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4187 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4189 // playfield elements
4190 for (x = 0; x < level->fieldx; x++)
4191 for (y = 0; y < level->fieldy; y++)
4192 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
4194 checked_free(cave_name);
4197 static void setTapeInfoToDefaults(void);
4199 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4201 struct LevelInfo_BD *level_bd = level->native_bd_level;
4202 GdCave *cave = level_bd->cave;
4203 GdReplay *replay = level_bd->replay;
4209 // always start with reliable default values
4210 setTapeInfoToDefaults();
4212 tape.level_nr = level_nr; // (currently not used)
4213 tape.random_seed = replay->seed;
4215 TapeSetDateFromIsoDateString(replay->date);
4218 tape.pos[tape.counter].delay = 0;
4220 tape.bd_replay = TRUE;
4222 // all time calculations only used to display approximate tape time
4223 int cave_speed = cave->speed;
4224 int milliseconds_game = 0;
4225 int milliseconds_elapsed = 20;
4227 for (i = 0; i < replay->movements->len; i++)
4229 int replay_action = replay->movements->data[i];
4230 int tape_action = map_action_BD_to_RND(replay_action);
4231 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4232 boolean success = 0;
4236 success = TapeAddAction(action);
4238 milliseconds_game += milliseconds_elapsed;
4240 if (milliseconds_game >= cave_speed)
4242 milliseconds_game -= cave_speed;
4249 tape.pos[tape.counter].delay = 0;
4250 tape.pos[tape.counter].action[0] = 0;
4254 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4260 TapeHaltRecording();
4264 // ----------------------------------------------------------------------------
4265 // functions for loading EM level
4266 // ----------------------------------------------------------------------------
4268 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4270 static int ball_xy[8][2] =
4281 struct LevelInfo_EM *level_em = level->native_em_level;
4282 struct CAVE *cav = level_em->cav;
4285 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4286 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4288 cav->time_seconds = level->time;
4289 cav->gems_needed = level->gems_needed;
4291 cav->emerald_score = level->score[SC_EMERALD];
4292 cav->diamond_score = level->score[SC_DIAMOND];
4293 cav->alien_score = level->score[SC_ROBOT];
4294 cav->tank_score = level->score[SC_SPACESHIP];
4295 cav->bug_score = level->score[SC_BUG];
4296 cav->eater_score = level->score[SC_YAMYAM];
4297 cav->nut_score = level->score[SC_NUT];
4298 cav->dynamite_score = level->score[SC_DYNAMITE];
4299 cav->key_score = level->score[SC_KEY];
4300 cav->exit_score = level->score[SC_TIME_BONUS];
4302 cav->num_eater_arrays = level->num_yamyam_contents;
4304 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4305 for (y = 0; y < 3; y++)
4306 for (x = 0; x < 3; x++)
4307 cav->eater_array[i][y * 3 + x] =
4308 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4310 cav->amoeba_time = level->amoeba_speed;
4311 cav->wonderwall_time = level->time_magic_wall;
4312 cav->wheel_time = level->time_wheel;
4314 cav->android_move_time = level->android_move_time;
4315 cav->android_clone_time = level->android_clone_time;
4316 cav->ball_random = level->ball_random;
4317 cav->ball_active = level->ball_active_initial;
4318 cav->ball_time = level->ball_time;
4319 cav->num_ball_arrays = level->num_ball_contents;
4321 cav->lenses_score = level->lenses_score;
4322 cav->magnify_score = level->magnify_score;
4323 cav->slurp_score = level->slurp_score;
4325 cav->lenses_time = level->lenses_time;
4326 cav->magnify_time = level->magnify_time;
4328 cav->wind_time = 9999;
4329 cav->wind_direction =
4330 map_direction_RND_to_EM(level->wind_direction_initial);
4332 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4333 for (j = 0; j < 8; j++)
4334 cav->ball_array[i][j] =
4335 map_element_RND_to_EM_cave(level->ball_content[i].
4336 e[ball_xy[j][0]][ball_xy[j][1]]);
4338 map_android_clone_elements_RND_to_EM(level);
4340 // first fill the complete playfield with the empty space element
4341 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4342 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4343 cav->cave[x][y] = Cblank;
4345 // then copy the real level contents from level file into the playfield
4346 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4348 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4350 if (level->field[x][y] == EL_AMOEBA_DEAD)
4351 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4353 cav->cave[x][y] = new_element;
4356 for (i = 0; i < MAX_PLAYERS; i++)
4358 cav->player_x[i] = -1;
4359 cav->player_y[i] = -1;
4362 // initialize player positions and delete players from the playfield
4363 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4365 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4367 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4369 cav->player_x[player_nr] = x;
4370 cav->player_y[player_nr] = y;
4372 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4377 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4379 static int ball_xy[8][2] =
4390 struct LevelInfo_EM *level_em = level->native_em_level;
4391 struct CAVE *cav = level_em->cav;
4394 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4395 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4397 level->time = cav->time_seconds;
4398 level->gems_needed = cav->gems_needed;
4400 sprintf(level->name, "Level %d", level->file_info.nr);
4402 level->score[SC_EMERALD] = cav->emerald_score;
4403 level->score[SC_DIAMOND] = cav->diamond_score;
4404 level->score[SC_ROBOT] = cav->alien_score;
4405 level->score[SC_SPACESHIP] = cav->tank_score;
4406 level->score[SC_BUG] = cav->bug_score;
4407 level->score[SC_YAMYAM] = cav->eater_score;
4408 level->score[SC_NUT] = cav->nut_score;
4409 level->score[SC_DYNAMITE] = cav->dynamite_score;
4410 level->score[SC_KEY] = cav->key_score;
4411 level->score[SC_TIME_BONUS] = cav->exit_score;
4413 level->num_yamyam_contents = cav->num_eater_arrays;
4415 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4416 for (y = 0; y < 3; y++)
4417 for (x = 0; x < 3; x++)
4418 level->yamyam_content[i].e[x][y] =
4419 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4421 level->amoeba_speed = cav->amoeba_time;
4422 level->time_magic_wall = cav->wonderwall_time;
4423 level->time_wheel = cav->wheel_time;
4425 level->android_move_time = cav->android_move_time;
4426 level->android_clone_time = cav->android_clone_time;
4427 level->ball_random = cav->ball_random;
4428 level->ball_active_initial = cav->ball_active;
4429 level->ball_time = cav->ball_time;
4430 level->num_ball_contents = cav->num_ball_arrays;
4432 level->lenses_score = cav->lenses_score;
4433 level->magnify_score = cav->magnify_score;
4434 level->slurp_score = cav->slurp_score;
4436 level->lenses_time = cav->lenses_time;
4437 level->magnify_time = cav->magnify_time;
4439 level->wind_direction_initial =
4440 map_direction_EM_to_RND(cav->wind_direction);
4442 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4443 for (j = 0; j < 8; j++)
4444 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4445 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4447 map_android_clone_elements_EM_to_RND(level);
4449 // convert the playfield (some elements need special treatment)
4450 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4452 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4454 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4455 new_element = EL_AMOEBA_DEAD;
4457 level->field[x][y] = new_element;
4460 for (i = 0; i < MAX_PLAYERS; i++)
4462 // in case of all players set to the same field, use the first player
4463 int nr = MAX_PLAYERS - i - 1;
4464 int jx = cav->player_x[nr];
4465 int jy = cav->player_y[nr];
4467 if (jx != -1 && jy != -1)
4468 level->field[jx][jy] = EL_PLAYER_1 + nr;
4471 // time score is counted for each 10 seconds left in Emerald Mine levels
4472 level->time_score_base = 10;
4476 // ----------------------------------------------------------------------------
4477 // functions for loading SP level
4478 // ----------------------------------------------------------------------------
4480 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4482 struct LevelInfo_SP *level_sp = level->native_sp_level;
4483 LevelInfoType *header = &level_sp->header;
4486 level_sp->width = level->fieldx;
4487 level_sp->height = level->fieldy;
4489 for (x = 0; x < level->fieldx; x++)
4490 for (y = 0; y < level->fieldy; y++)
4491 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4493 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4495 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4496 header->LevelTitle[i] = level->name[i];
4497 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4499 header->InfotronsNeeded = level->gems_needed;
4501 header->SpecialPortCount = 0;
4503 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4505 boolean gravity_port_found = FALSE;
4506 boolean gravity_port_valid = FALSE;
4507 int gravity_port_flag;
4508 int gravity_port_base_element;
4509 int element = level->field[x][y];
4511 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4512 element <= EL_SP_GRAVITY_ON_PORT_UP)
4514 gravity_port_found = TRUE;
4515 gravity_port_valid = TRUE;
4516 gravity_port_flag = 1;
4517 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4519 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4520 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4522 gravity_port_found = TRUE;
4523 gravity_port_valid = TRUE;
4524 gravity_port_flag = 0;
4525 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4527 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4528 element <= EL_SP_GRAVITY_PORT_UP)
4530 // change R'n'D style gravity inverting special port to normal port
4531 // (there are no gravity inverting ports in native Supaplex engine)
4533 gravity_port_found = TRUE;
4534 gravity_port_valid = FALSE;
4535 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4538 if (gravity_port_found)
4540 if (gravity_port_valid &&
4541 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4543 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4545 port->PortLocation = (y * level->fieldx + x) * 2;
4546 port->Gravity = gravity_port_flag;
4548 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4550 header->SpecialPortCount++;
4554 // change special gravity port to normal port
4556 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4559 level_sp->playfield[x][y] = element - EL_SP_START;
4564 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4566 struct LevelInfo_SP *level_sp = level->native_sp_level;
4567 LevelInfoType *header = &level_sp->header;
4568 boolean num_invalid_elements = 0;
4571 level->fieldx = level_sp->width;
4572 level->fieldy = level_sp->height;
4574 for (x = 0; x < level->fieldx; x++)
4576 for (y = 0; y < level->fieldy; y++)
4578 int element_old = level_sp->playfield[x][y];
4579 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4581 if (element_new == EL_UNKNOWN)
4583 num_invalid_elements++;
4585 Debug("level:native:SP", "invalid element %d at position %d, %d",
4589 level->field[x][y] = element_new;
4593 if (num_invalid_elements > 0)
4594 Warn("found %d invalid elements%s", num_invalid_elements,
4595 (!options.debug ? " (use '--debug' for more details)" : ""));
4597 for (i = 0; i < MAX_PLAYERS; i++)
4598 level->initial_player_gravity[i] =
4599 (header->InitialGravity == 1 ? TRUE : FALSE);
4601 // skip leading spaces
4602 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4603 if (header->LevelTitle[i] != ' ')
4607 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4608 level->name[j] = header->LevelTitle[i];
4609 level->name[j] = '\0';
4611 // cut trailing spaces
4613 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4614 level->name[j - 1] = '\0';
4616 level->gems_needed = header->InfotronsNeeded;
4618 for (i = 0; i < header->SpecialPortCount; i++)
4620 SpecialPortType *port = &header->SpecialPort[i];
4621 int port_location = port->PortLocation;
4622 int gravity = port->Gravity;
4623 int port_x, port_y, port_element;
4625 port_x = (port_location / 2) % level->fieldx;
4626 port_y = (port_location / 2) / level->fieldx;
4628 if (port_x < 0 || port_x >= level->fieldx ||
4629 port_y < 0 || port_y >= level->fieldy)
4631 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4636 port_element = level->field[port_x][port_y];
4638 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4639 port_element > EL_SP_GRAVITY_PORT_UP)
4641 Warn("no special port at position (%d, %d)", port_x, port_y);
4646 // change previous (wrong) gravity inverting special port to either
4647 // gravity enabling special port or gravity disabling special port
4648 level->field[port_x][port_y] +=
4649 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4650 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4653 // change special gravity ports without database entries to normal ports
4654 for (x = 0; x < level->fieldx; x++)
4655 for (y = 0; y < level->fieldy; y++)
4656 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4657 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4658 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4660 level->time = 0; // no time limit
4661 level->amoeba_speed = 0;
4662 level->time_magic_wall = 0;
4663 level->time_wheel = 0;
4664 level->amoeba_content = EL_EMPTY;
4666 // original Supaplex does not use score values -- rate by playing time
4667 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4668 level->score[i] = 0;
4670 level->rate_time_over_score = TRUE;
4672 // there are no yamyams in supaplex levels
4673 for (i = 0; i < level->num_yamyam_contents; i++)
4674 for (x = 0; x < 3; x++)
4675 for (y = 0; y < 3; y++)
4676 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4679 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4681 struct LevelInfo_SP *level_sp = level->native_sp_level;
4682 struct DemoInfo_SP *demo = &level_sp->demo;
4685 // always start with reliable default values
4686 demo->is_available = FALSE;
4689 if (TAPE_IS_EMPTY(tape))
4692 demo->level_nr = tape.level_nr; // (currently not used)
4694 level_sp->header.DemoRandomSeed = tape.random_seed;
4698 for (i = 0; i < tape.length; i++)
4700 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4701 int demo_repeat = tape.pos[i].delay;
4702 int demo_entries = (demo_repeat + 15) / 16;
4704 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4706 Warn("tape truncated: size exceeds maximum SP demo size %d",
4712 for (j = 0; j < demo_repeat / 16; j++)
4713 demo->data[demo->length++] = 0xf0 | demo_action;
4715 if (demo_repeat % 16)
4716 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4719 demo->is_available = TRUE;
4722 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4724 struct LevelInfo_SP *level_sp = level->native_sp_level;
4725 struct DemoInfo_SP *demo = &level_sp->demo;
4726 char *filename = level->file_info.filename;
4729 // always start with reliable default values
4730 setTapeInfoToDefaults();
4732 if (!demo->is_available)
4735 tape.level_nr = demo->level_nr; // (currently not used)
4736 tape.random_seed = level_sp->header.DemoRandomSeed;
4738 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4741 tape.pos[tape.counter].delay = 0;
4743 for (i = 0; i < demo->length; i++)
4745 int demo_action = demo->data[i] & 0x0f;
4746 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4747 int tape_action = map_key_SP_to_RND(demo_action);
4748 int tape_repeat = demo_repeat + 1;
4749 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4750 boolean success = 0;
4753 for (j = 0; j < tape_repeat; j++)
4754 success = TapeAddAction(action);
4758 Warn("SP demo truncated: size exceeds maximum tape size %d",
4765 TapeHaltRecording();
4769 // ----------------------------------------------------------------------------
4770 // functions for loading MM level
4771 // ----------------------------------------------------------------------------
4773 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4775 struct LevelInfo_MM *level_mm = level->native_mm_level;
4778 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4779 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4781 level_mm->time = level->time;
4782 level_mm->kettles_needed = level->gems_needed;
4783 level_mm->auto_count_kettles = level->auto_count_gems;
4785 level_mm->mm_laser_red = level->mm_laser_red;
4786 level_mm->mm_laser_green = level->mm_laser_green;
4787 level_mm->mm_laser_blue = level->mm_laser_blue;
4789 level_mm->df_laser_red = level->df_laser_red;
4790 level_mm->df_laser_green = level->df_laser_green;
4791 level_mm->df_laser_blue = level->df_laser_blue;
4793 strcpy(level_mm->name, level->name);
4794 strcpy(level_mm->author, level->author);
4796 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4797 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4798 level_mm->score[SC_KEY] = level->score[SC_KEY];
4799 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4800 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4802 level_mm->amoeba_speed = level->amoeba_speed;
4803 level_mm->time_fuse = level->mm_time_fuse;
4804 level_mm->time_bomb = level->mm_time_bomb;
4805 level_mm->time_ball = level->mm_time_ball;
4806 level_mm->time_block = level->mm_time_block;
4808 level_mm->num_ball_contents = level->num_mm_ball_contents;
4809 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4810 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4811 level_mm->explode_ball = level->explode_mm_ball;
4813 for (i = 0; i < level->num_mm_ball_contents; i++)
4814 level_mm->ball_content[i] =
4815 map_element_RND_to_MM(level->mm_ball_content[i]);
4817 for (x = 0; x < level->fieldx; x++)
4818 for (y = 0; y < level->fieldy; y++)
4820 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4823 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4825 struct LevelInfo_MM *level_mm = level->native_mm_level;
4828 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4829 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4831 level->time = level_mm->time;
4832 level->gems_needed = level_mm->kettles_needed;
4833 level->auto_count_gems = level_mm->auto_count_kettles;
4835 level->mm_laser_red = level_mm->mm_laser_red;
4836 level->mm_laser_green = level_mm->mm_laser_green;
4837 level->mm_laser_blue = level_mm->mm_laser_blue;
4839 level->df_laser_red = level_mm->df_laser_red;
4840 level->df_laser_green = level_mm->df_laser_green;
4841 level->df_laser_blue = level_mm->df_laser_blue;
4843 strcpy(level->name, level_mm->name);
4845 // only overwrite author from 'levelinfo.conf' if author defined in level
4846 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4847 strcpy(level->author, level_mm->author);
4849 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4850 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4851 level->score[SC_KEY] = level_mm->score[SC_KEY];
4852 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4853 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4855 level->amoeba_speed = level_mm->amoeba_speed;
4856 level->mm_time_fuse = level_mm->time_fuse;
4857 level->mm_time_bomb = level_mm->time_bomb;
4858 level->mm_time_ball = level_mm->time_ball;
4859 level->mm_time_block = level_mm->time_block;
4861 level->num_mm_ball_contents = level_mm->num_ball_contents;
4862 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4863 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4864 level->explode_mm_ball = level_mm->explode_ball;
4866 for (i = 0; i < level->num_mm_ball_contents; i++)
4867 level->mm_ball_content[i] =
4868 map_element_MM_to_RND(level_mm->ball_content[i]);
4870 for (x = 0; x < level->fieldx; x++)
4871 for (y = 0; y < level->fieldy; y++)
4872 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4876 // ----------------------------------------------------------------------------
4877 // functions for loading DC level
4878 // ----------------------------------------------------------------------------
4880 #define DC_LEVEL_HEADER_SIZE 344
4882 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4885 static int last_data_encoded;
4889 int diff_hi, diff_lo;
4890 int data_hi, data_lo;
4891 unsigned short data_decoded;
4895 last_data_encoded = 0;
4902 diff = data_encoded - last_data_encoded;
4903 diff_hi = diff & ~0xff;
4904 diff_lo = diff & 0xff;
4908 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4909 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4910 data_hi = data_hi & 0xff00;
4912 data_decoded = data_hi | data_lo;
4914 last_data_encoded = data_encoded;
4916 offset1 = (offset1 + 1) % 31;
4917 offset2 = offset2 & 0xff;
4919 return data_decoded;
4922 static int getMappedElement_DC(int element)
4930 // 0x0117 - 0x036e: (?)
4933 // 0x042d - 0x0684: (?)
4949 element = EL_CRYSTAL;
4952 case 0x0e77: // quicksand (boulder)
4953 element = EL_QUICKSAND_FAST_FULL;
4956 case 0x0e99: // slow quicksand (boulder)
4957 element = EL_QUICKSAND_FULL;
4961 element = EL_EM_EXIT_OPEN;
4965 element = EL_EM_EXIT_CLOSED;
4969 element = EL_EM_STEEL_EXIT_OPEN;
4973 element = EL_EM_STEEL_EXIT_CLOSED;
4976 case 0x0f4f: // dynamite (lit 1)
4977 element = EL_EM_DYNAMITE_ACTIVE;
4980 case 0x0f57: // dynamite (lit 2)
4981 element = EL_EM_DYNAMITE_ACTIVE;
4984 case 0x0f5f: // dynamite (lit 3)
4985 element = EL_EM_DYNAMITE_ACTIVE;
4988 case 0x0f67: // dynamite (lit 4)
4989 element = EL_EM_DYNAMITE_ACTIVE;
4996 element = EL_AMOEBA_WET;
5000 element = EL_AMOEBA_DROP;
5004 element = EL_DC_MAGIC_WALL;
5008 element = EL_SPACESHIP_UP;
5012 element = EL_SPACESHIP_DOWN;
5016 element = EL_SPACESHIP_LEFT;
5020 element = EL_SPACESHIP_RIGHT;
5024 element = EL_BUG_UP;
5028 element = EL_BUG_DOWN;
5032 element = EL_BUG_LEFT;
5036 element = EL_BUG_RIGHT;
5040 element = EL_MOLE_UP;
5044 element = EL_MOLE_DOWN;
5048 element = EL_MOLE_LEFT;
5052 element = EL_MOLE_RIGHT;
5060 element = EL_YAMYAM_UP;
5064 element = EL_SWITCHGATE_OPEN;
5068 element = EL_SWITCHGATE_CLOSED;
5072 element = EL_DC_SWITCHGATE_SWITCH_UP;
5076 element = EL_TIMEGATE_CLOSED;
5079 case 0x144c: // conveyor belt switch (green)
5080 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5083 case 0x144f: // conveyor belt switch (red)
5084 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5087 case 0x1452: // conveyor belt switch (blue)
5088 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5092 element = EL_CONVEYOR_BELT_3_MIDDLE;
5096 element = EL_CONVEYOR_BELT_3_LEFT;
5100 element = EL_CONVEYOR_BELT_3_RIGHT;
5104 element = EL_CONVEYOR_BELT_1_MIDDLE;
5108 element = EL_CONVEYOR_BELT_1_LEFT;
5112 element = EL_CONVEYOR_BELT_1_RIGHT;
5116 element = EL_CONVEYOR_BELT_4_MIDDLE;
5120 element = EL_CONVEYOR_BELT_4_LEFT;
5124 element = EL_CONVEYOR_BELT_4_RIGHT;
5128 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5132 element = EL_EXPANDABLE_WALL_VERTICAL;
5136 element = EL_EXPANDABLE_WALL_ANY;
5139 case 0x14ce: // growing steel wall (left/right)
5140 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5143 case 0x14df: // growing steel wall (up/down)
5144 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5147 case 0x14e8: // growing steel wall (up/down/left/right)
5148 element = EL_EXPANDABLE_STEELWALL_ANY;
5152 element = EL_SHIELD_DEADLY;
5156 element = EL_EXTRA_TIME;
5164 element = EL_EMPTY_SPACE;
5167 case 0x1578: // quicksand (empty)
5168 element = EL_QUICKSAND_FAST_EMPTY;
5171 case 0x1579: // slow quicksand (empty)
5172 element = EL_QUICKSAND_EMPTY;
5182 element = EL_EM_DYNAMITE;
5185 case 0x15a1: // key (red)
5186 element = EL_EM_KEY_1;
5189 case 0x15a2: // key (yellow)
5190 element = EL_EM_KEY_2;
5193 case 0x15a3: // key (blue)
5194 element = EL_EM_KEY_4;
5197 case 0x15a4: // key (green)
5198 element = EL_EM_KEY_3;
5201 case 0x15a5: // key (white)
5202 element = EL_DC_KEY_WHITE;
5206 element = EL_WALL_SLIPPERY;
5213 case 0x15a8: // wall (not round)
5217 case 0x15a9: // (blue)
5218 element = EL_CHAR_A;
5221 case 0x15aa: // (blue)
5222 element = EL_CHAR_B;
5225 case 0x15ab: // (blue)
5226 element = EL_CHAR_C;
5229 case 0x15ac: // (blue)
5230 element = EL_CHAR_D;
5233 case 0x15ad: // (blue)
5234 element = EL_CHAR_E;
5237 case 0x15ae: // (blue)
5238 element = EL_CHAR_F;
5241 case 0x15af: // (blue)
5242 element = EL_CHAR_G;
5245 case 0x15b0: // (blue)
5246 element = EL_CHAR_H;
5249 case 0x15b1: // (blue)
5250 element = EL_CHAR_I;
5253 case 0x15b2: // (blue)
5254 element = EL_CHAR_J;
5257 case 0x15b3: // (blue)
5258 element = EL_CHAR_K;
5261 case 0x15b4: // (blue)
5262 element = EL_CHAR_L;
5265 case 0x15b5: // (blue)
5266 element = EL_CHAR_M;
5269 case 0x15b6: // (blue)
5270 element = EL_CHAR_N;
5273 case 0x15b7: // (blue)
5274 element = EL_CHAR_O;
5277 case 0x15b8: // (blue)
5278 element = EL_CHAR_P;
5281 case 0x15b9: // (blue)
5282 element = EL_CHAR_Q;
5285 case 0x15ba: // (blue)
5286 element = EL_CHAR_R;
5289 case 0x15bb: // (blue)
5290 element = EL_CHAR_S;
5293 case 0x15bc: // (blue)
5294 element = EL_CHAR_T;
5297 case 0x15bd: // (blue)
5298 element = EL_CHAR_U;
5301 case 0x15be: // (blue)
5302 element = EL_CHAR_V;
5305 case 0x15bf: // (blue)
5306 element = EL_CHAR_W;
5309 case 0x15c0: // (blue)
5310 element = EL_CHAR_X;
5313 case 0x15c1: // (blue)
5314 element = EL_CHAR_Y;
5317 case 0x15c2: // (blue)
5318 element = EL_CHAR_Z;
5321 case 0x15c3: // (blue)
5322 element = EL_CHAR_AUMLAUT;
5325 case 0x15c4: // (blue)
5326 element = EL_CHAR_OUMLAUT;
5329 case 0x15c5: // (blue)
5330 element = EL_CHAR_UUMLAUT;
5333 case 0x15c6: // (blue)
5334 element = EL_CHAR_0;
5337 case 0x15c7: // (blue)
5338 element = EL_CHAR_1;
5341 case 0x15c8: // (blue)
5342 element = EL_CHAR_2;
5345 case 0x15c9: // (blue)
5346 element = EL_CHAR_3;
5349 case 0x15ca: // (blue)
5350 element = EL_CHAR_4;
5353 case 0x15cb: // (blue)
5354 element = EL_CHAR_5;
5357 case 0x15cc: // (blue)
5358 element = EL_CHAR_6;
5361 case 0x15cd: // (blue)
5362 element = EL_CHAR_7;
5365 case 0x15ce: // (blue)
5366 element = EL_CHAR_8;
5369 case 0x15cf: // (blue)
5370 element = EL_CHAR_9;
5373 case 0x15d0: // (blue)
5374 element = EL_CHAR_PERIOD;
5377 case 0x15d1: // (blue)
5378 element = EL_CHAR_EXCLAM;
5381 case 0x15d2: // (blue)
5382 element = EL_CHAR_COLON;
5385 case 0x15d3: // (blue)
5386 element = EL_CHAR_LESS;
5389 case 0x15d4: // (blue)
5390 element = EL_CHAR_GREATER;
5393 case 0x15d5: // (blue)
5394 element = EL_CHAR_QUESTION;
5397 case 0x15d6: // (blue)
5398 element = EL_CHAR_COPYRIGHT;
5401 case 0x15d7: // (blue)
5402 element = EL_CHAR_UP;
5405 case 0x15d8: // (blue)
5406 element = EL_CHAR_DOWN;
5409 case 0x15d9: // (blue)
5410 element = EL_CHAR_BUTTON;
5413 case 0x15da: // (blue)
5414 element = EL_CHAR_PLUS;
5417 case 0x15db: // (blue)
5418 element = EL_CHAR_MINUS;
5421 case 0x15dc: // (blue)
5422 element = EL_CHAR_APOSTROPHE;
5425 case 0x15dd: // (blue)
5426 element = EL_CHAR_PARENLEFT;
5429 case 0x15de: // (blue)
5430 element = EL_CHAR_PARENRIGHT;
5433 case 0x15df: // (green)
5434 element = EL_CHAR_A;
5437 case 0x15e0: // (green)
5438 element = EL_CHAR_B;
5441 case 0x15e1: // (green)
5442 element = EL_CHAR_C;
5445 case 0x15e2: // (green)
5446 element = EL_CHAR_D;
5449 case 0x15e3: // (green)
5450 element = EL_CHAR_E;
5453 case 0x15e4: // (green)
5454 element = EL_CHAR_F;
5457 case 0x15e5: // (green)
5458 element = EL_CHAR_G;
5461 case 0x15e6: // (green)
5462 element = EL_CHAR_H;
5465 case 0x15e7: // (green)
5466 element = EL_CHAR_I;
5469 case 0x15e8: // (green)
5470 element = EL_CHAR_J;
5473 case 0x15e9: // (green)
5474 element = EL_CHAR_K;
5477 case 0x15ea: // (green)
5478 element = EL_CHAR_L;
5481 case 0x15eb: // (green)
5482 element = EL_CHAR_M;
5485 case 0x15ec: // (green)
5486 element = EL_CHAR_N;
5489 case 0x15ed: // (green)
5490 element = EL_CHAR_O;
5493 case 0x15ee: // (green)
5494 element = EL_CHAR_P;
5497 case 0x15ef: // (green)
5498 element = EL_CHAR_Q;
5501 case 0x15f0: // (green)
5502 element = EL_CHAR_R;
5505 case 0x15f1: // (green)
5506 element = EL_CHAR_S;
5509 case 0x15f2: // (green)
5510 element = EL_CHAR_T;
5513 case 0x15f3: // (green)
5514 element = EL_CHAR_U;
5517 case 0x15f4: // (green)
5518 element = EL_CHAR_V;
5521 case 0x15f5: // (green)
5522 element = EL_CHAR_W;
5525 case 0x15f6: // (green)
5526 element = EL_CHAR_X;
5529 case 0x15f7: // (green)
5530 element = EL_CHAR_Y;
5533 case 0x15f8: // (green)
5534 element = EL_CHAR_Z;
5537 case 0x15f9: // (green)
5538 element = EL_CHAR_AUMLAUT;
5541 case 0x15fa: // (green)
5542 element = EL_CHAR_OUMLAUT;
5545 case 0x15fb: // (green)
5546 element = EL_CHAR_UUMLAUT;
5549 case 0x15fc: // (green)
5550 element = EL_CHAR_0;
5553 case 0x15fd: // (green)
5554 element = EL_CHAR_1;
5557 case 0x15fe: // (green)
5558 element = EL_CHAR_2;
5561 case 0x15ff: // (green)
5562 element = EL_CHAR_3;
5565 case 0x1600: // (green)
5566 element = EL_CHAR_4;
5569 case 0x1601: // (green)
5570 element = EL_CHAR_5;
5573 case 0x1602: // (green)
5574 element = EL_CHAR_6;
5577 case 0x1603: // (green)
5578 element = EL_CHAR_7;
5581 case 0x1604: // (green)
5582 element = EL_CHAR_8;
5585 case 0x1605: // (green)
5586 element = EL_CHAR_9;
5589 case 0x1606: // (green)
5590 element = EL_CHAR_PERIOD;
5593 case 0x1607: // (green)
5594 element = EL_CHAR_EXCLAM;
5597 case 0x1608: // (green)
5598 element = EL_CHAR_COLON;
5601 case 0x1609: // (green)
5602 element = EL_CHAR_LESS;
5605 case 0x160a: // (green)
5606 element = EL_CHAR_GREATER;
5609 case 0x160b: // (green)
5610 element = EL_CHAR_QUESTION;
5613 case 0x160c: // (green)
5614 element = EL_CHAR_COPYRIGHT;
5617 case 0x160d: // (green)
5618 element = EL_CHAR_UP;
5621 case 0x160e: // (green)
5622 element = EL_CHAR_DOWN;
5625 case 0x160f: // (green)
5626 element = EL_CHAR_BUTTON;
5629 case 0x1610: // (green)
5630 element = EL_CHAR_PLUS;
5633 case 0x1611: // (green)
5634 element = EL_CHAR_MINUS;
5637 case 0x1612: // (green)
5638 element = EL_CHAR_APOSTROPHE;
5641 case 0x1613: // (green)
5642 element = EL_CHAR_PARENLEFT;
5645 case 0x1614: // (green)
5646 element = EL_CHAR_PARENRIGHT;
5649 case 0x1615: // (blue steel)
5650 element = EL_STEEL_CHAR_A;
5653 case 0x1616: // (blue steel)
5654 element = EL_STEEL_CHAR_B;
5657 case 0x1617: // (blue steel)
5658 element = EL_STEEL_CHAR_C;
5661 case 0x1618: // (blue steel)
5662 element = EL_STEEL_CHAR_D;
5665 case 0x1619: // (blue steel)
5666 element = EL_STEEL_CHAR_E;
5669 case 0x161a: // (blue steel)
5670 element = EL_STEEL_CHAR_F;
5673 case 0x161b: // (blue steel)
5674 element = EL_STEEL_CHAR_G;
5677 case 0x161c: // (blue steel)
5678 element = EL_STEEL_CHAR_H;
5681 case 0x161d: // (blue steel)
5682 element = EL_STEEL_CHAR_I;
5685 case 0x161e: // (blue steel)
5686 element = EL_STEEL_CHAR_J;
5689 case 0x161f: // (blue steel)
5690 element = EL_STEEL_CHAR_K;
5693 case 0x1620: // (blue steel)
5694 element = EL_STEEL_CHAR_L;
5697 case 0x1621: // (blue steel)
5698 element = EL_STEEL_CHAR_M;
5701 case 0x1622: // (blue steel)
5702 element = EL_STEEL_CHAR_N;
5705 case 0x1623: // (blue steel)
5706 element = EL_STEEL_CHAR_O;
5709 case 0x1624: // (blue steel)
5710 element = EL_STEEL_CHAR_P;
5713 case 0x1625: // (blue steel)
5714 element = EL_STEEL_CHAR_Q;
5717 case 0x1626: // (blue steel)
5718 element = EL_STEEL_CHAR_R;
5721 case 0x1627: // (blue steel)
5722 element = EL_STEEL_CHAR_S;
5725 case 0x1628: // (blue steel)
5726 element = EL_STEEL_CHAR_T;
5729 case 0x1629: // (blue steel)
5730 element = EL_STEEL_CHAR_U;
5733 case 0x162a: // (blue steel)
5734 element = EL_STEEL_CHAR_V;
5737 case 0x162b: // (blue steel)
5738 element = EL_STEEL_CHAR_W;
5741 case 0x162c: // (blue steel)
5742 element = EL_STEEL_CHAR_X;
5745 case 0x162d: // (blue steel)
5746 element = EL_STEEL_CHAR_Y;
5749 case 0x162e: // (blue steel)
5750 element = EL_STEEL_CHAR_Z;
5753 case 0x162f: // (blue steel)
5754 element = EL_STEEL_CHAR_AUMLAUT;
5757 case 0x1630: // (blue steel)
5758 element = EL_STEEL_CHAR_OUMLAUT;
5761 case 0x1631: // (blue steel)
5762 element = EL_STEEL_CHAR_UUMLAUT;
5765 case 0x1632: // (blue steel)
5766 element = EL_STEEL_CHAR_0;
5769 case 0x1633: // (blue steel)
5770 element = EL_STEEL_CHAR_1;
5773 case 0x1634: // (blue steel)
5774 element = EL_STEEL_CHAR_2;
5777 case 0x1635: // (blue steel)
5778 element = EL_STEEL_CHAR_3;
5781 case 0x1636: // (blue steel)
5782 element = EL_STEEL_CHAR_4;
5785 case 0x1637: // (blue steel)
5786 element = EL_STEEL_CHAR_5;
5789 case 0x1638: // (blue steel)
5790 element = EL_STEEL_CHAR_6;
5793 case 0x1639: // (blue steel)
5794 element = EL_STEEL_CHAR_7;
5797 case 0x163a: // (blue steel)
5798 element = EL_STEEL_CHAR_8;
5801 case 0x163b: // (blue steel)
5802 element = EL_STEEL_CHAR_9;
5805 case 0x163c: // (blue steel)
5806 element = EL_STEEL_CHAR_PERIOD;
5809 case 0x163d: // (blue steel)
5810 element = EL_STEEL_CHAR_EXCLAM;
5813 case 0x163e: // (blue steel)
5814 element = EL_STEEL_CHAR_COLON;
5817 case 0x163f: // (blue steel)
5818 element = EL_STEEL_CHAR_LESS;
5821 case 0x1640: // (blue steel)
5822 element = EL_STEEL_CHAR_GREATER;
5825 case 0x1641: // (blue steel)
5826 element = EL_STEEL_CHAR_QUESTION;
5829 case 0x1642: // (blue steel)
5830 element = EL_STEEL_CHAR_COPYRIGHT;
5833 case 0x1643: // (blue steel)
5834 element = EL_STEEL_CHAR_UP;
5837 case 0x1644: // (blue steel)
5838 element = EL_STEEL_CHAR_DOWN;
5841 case 0x1645: // (blue steel)
5842 element = EL_STEEL_CHAR_BUTTON;
5845 case 0x1646: // (blue steel)
5846 element = EL_STEEL_CHAR_PLUS;
5849 case 0x1647: // (blue steel)
5850 element = EL_STEEL_CHAR_MINUS;
5853 case 0x1648: // (blue steel)
5854 element = EL_STEEL_CHAR_APOSTROPHE;
5857 case 0x1649: // (blue steel)
5858 element = EL_STEEL_CHAR_PARENLEFT;
5861 case 0x164a: // (blue steel)
5862 element = EL_STEEL_CHAR_PARENRIGHT;
5865 case 0x164b: // (green steel)
5866 element = EL_STEEL_CHAR_A;
5869 case 0x164c: // (green steel)
5870 element = EL_STEEL_CHAR_B;
5873 case 0x164d: // (green steel)
5874 element = EL_STEEL_CHAR_C;
5877 case 0x164e: // (green steel)
5878 element = EL_STEEL_CHAR_D;
5881 case 0x164f: // (green steel)
5882 element = EL_STEEL_CHAR_E;
5885 case 0x1650: // (green steel)
5886 element = EL_STEEL_CHAR_F;
5889 case 0x1651: // (green steel)
5890 element = EL_STEEL_CHAR_G;
5893 case 0x1652: // (green steel)
5894 element = EL_STEEL_CHAR_H;
5897 case 0x1653: // (green steel)
5898 element = EL_STEEL_CHAR_I;
5901 case 0x1654: // (green steel)
5902 element = EL_STEEL_CHAR_J;
5905 case 0x1655: // (green steel)
5906 element = EL_STEEL_CHAR_K;
5909 case 0x1656: // (green steel)
5910 element = EL_STEEL_CHAR_L;
5913 case 0x1657: // (green steel)
5914 element = EL_STEEL_CHAR_M;
5917 case 0x1658: // (green steel)
5918 element = EL_STEEL_CHAR_N;
5921 case 0x1659: // (green steel)
5922 element = EL_STEEL_CHAR_O;
5925 case 0x165a: // (green steel)
5926 element = EL_STEEL_CHAR_P;
5929 case 0x165b: // (green steel)
5930 element = EL_STEEL_CHAR_Q;
5933 case 0x165c: // (green steel)
5934 element = EL_STEEL_CHAR_R;
5937 case 0x165d: // (green steel)
5938 element = EL_STEEL_CHAR_S;
5941 case 0x165e: // (green steel)
5942 element = EL_STEEL_CHAR_T;
5945 case 0x165f: // (green steel)
5946 element = EL_STEEL_CHAR_U;
5949 case 0x1660: // (green steel)
5950 element = EL_STEEL_CHAR_V;
5953 case 0x1661: // (green steel)
5954 element = EL_STEEL_CHAR_W;
5957 case 0x1662: // (green steel)
5958 element = EL_STEEL_CHAR_X;
5961 case 0x1663: // (green steel)
5962 element = EL_STEEL_CHAR_Y;
5965 case 0x1664: // (green steel)
5966 element = EL_STEEL_CHAR_Z;
5969 case 0x1665: // (green steel)
5970 element = EL_STEEL_CHAR_AUMLAUT;
5973 case 0x1666: // (green steel)
5974 element = EL_STEEL_CHAR_OUMLAUT;
5977 case 0x1667: // (green steel)
5978 element = EL_STEEL_CHAR_UUMLAUT;
5981 case 0x1668: // (green steel)
5982 element = EL_STEEL_CHAR_0;
5985 case 0x1669: // (green steel)
5986 element = EL_STEEL_CHAR_1;
5989 case 0x166a: // (green steel)
5990 element = EL_STEEL_CHAR_2;
5993 case 0x166b: // (green steel)
5994 element = EL_STEEL_CHAR_3;
5997 case 0x166c: // (green steel)
5998 element = EL_STEEL_CHAR_4;
6001 case 0x166d: // (green steel)
6002 element = EL_STEEL_CHAR_5;
6005 case 0x166e: // (green steel)
6006 element = EL_STEEL_CHAR_6;
6009 case 0x166f: // (green steel)
6010 element = EL_STEEL_CHAR_7;
6013 case 0x1670: // (green steel)
6014 element = EL_STEEL_CHAR_8;
6017 case 0x1671: // (green steel)
6018 element = EL_STEEL_CHAR_9;
6021 case 0x1672: // (green steel)
6022 element = EL_STEEL_CHAR_PERIOD;
6025 case 0x1673: // (green steel)
6026 element = EL_STEEL_CHAR_EXCLAM;
6029 case 0x1674: // (green steel)
6030 element = EL_STEEL_CHAR_COLON;
6033 case 0x1675: // (green steel)
6034 element = EL_STEEL_CHAR_LESS;
6037 case 0x1676: // (green steel)
6038 element = EL_STEEL_CHAR_GREATER;
6041 case 0x1677: // (green steel)
6042 element = EL_STEEL_CHAR_QUESTION;
6045 case 0x1678: // (green steel)
6046 element = EL_STEEL_CHAR_COPYRIGHT;
6049 case 0x1679: // (green steel)
6050 element = EL_STEEL_CHAR_UP;
6053 case 0x167a: // (green steel)
6054 element = EL_STEEL_CHAR_DOWN;
6057 case 0x167b: // (green steel)
6058 element = EL_STEEL_CHAR_BUTTON;
6061 case 0x167c: // (green steel)
6062 element = EL_STEEL_CHAR_PLUS;
6065 case 0x167d: // (green steel)
6066 element = EL_STEEL_CHAR_MINUS;
6069 case 0x167e: // (green steel)
6070 element = EL_STEEL_CHAR_APOSTROPHE;
6073 case 0x167f: // (green steel)
6074 element = EL_STEEL_CHAR_PARENLEFT;
6077 case 0x1680: // (green steel)
6078 element = EL_STEEL_CHAR_PARENRIGHT;
6081 case 0x1681: // gate (red)
6082 element = EL_EM_GATE_1;
6085 case 0x1682: // secret gate (red)
6086 element = EL_EM_GATE_1_GRAY;
6089 case 0x1683: // gate (yellow)
6090 element = EL_EM_GATE_2;
6093 case 0x1684: // secret gate (yellow)
6094 element = EL_EM_GATE_2_GRAY;
6097 case 0x1685: // gate (blue)
6098 element = EL_EM_GATE_4;
6101 case 0x1686: // secret gate (blue)
6102 element = EL_EM_GATE_4_GRAY;
6105 case 0x1687: // gate (green)
6106 element = EL_EM_GATE_3;
6109 case 0x1688: // secret gate (green)
6110 element = EL_EM_GATE_3_GRAY;
6113 case 0x1689: // gate (white)
6114 element = EL_DC_GATE_WHITE;
6117 case 0x168a: // secret gate (white)
6118 element = EL_DC_GATE_WHITE_GRAY;
6121 case 0x168b: // secret gate (no key)
6122 element = EL_DC_GATE_FAKE_GRAY;
6126 element = EL_ROBOT_WHEEL;
6130 element = EL_DC_TIMEGATE_SWITCH;
6134 element = EL_ACID_POOL_BOTTOM;
6138 element = EL_ACID_POOL_TOPLEFT;
6142 element = EL_ACID_POOL_TOPRIGHT;
6146 element = EL_ACID_POOL_BOTTOMLEFT;
6150 element = EL_ACID_POOL_BOTTOMRIGHT;
6154 element = EL_STEELWALL;
6158 element = EL_STEELWALL_SLIPPERY;
6161 case 0x1695: // steel wall (not round)
6162 element = EL_STEELWALL;
6165 case 0x1696: // steel wall (left)
6166 element = EL_DC_STEELWALL_1_LEFT;
6169 case 0x1697: // steel wall (bottom)
6170 element = EL_DC_STEELWALL_1_BOTTOM;
6173 case 0x1698: // steel wall (right)
6174 element = EL_DC_STEELWALL_1_RIGHT;
6177 case 0x1699: // steel wall (top)
6178 element = EL_DC_STEELWALL_1_TOP;
6181 case 0x169a: // steel wall (left/bottom)
6182 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6185 case 0x169b: // steel wall (right/bottom)
6186 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6189 case 0x169c: // steel wall (right/top)
6190 element = EL_DC_STEELWALL_1_TOPRIGHT;
6193 case 0x169d: // steel wall (left/top)
6194 element = EL_DC_STEELWALL_1_TOPLEFT;
6197 case 0x169e: // steel wall (right/bottom small)
6198 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6201 case 0x169f: // steel wall (left/bottom small)
6202 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6205 case 0x16a0: // steel wall (right/top small)
6206 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6209 case 0x16a1: // steel wall (left/top small)
6210 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6213 case 0x16a2: // steel wall (left/right)
6214 element = EL_DC_STEELWALL_1_VERTICAL;
6217 case 0x16a3: // steel wall (top/bottom)
6218 element = EL_DC_STEELWALL_1_HORIZONTAL;
6221 case 0x16a4: // steel wall 2 (left end)
6222 element = EL_DC_STEELWALL_2_LEFT;
6225 case 0x16a5: // steel wall 2 (right end)
6226 element = EL_DC_STEELWALL_2_RIGHT;
6229 case 0x16a6: // steel wall 2 (top end)
6230 element = EL_DC_STEELWALL_2_TOP;
6233 case 0x16a7: // steel wall 2 (bottom end)
6234 element = EL_DC_STEELWALL_2_BOTTOM;
6237 case 0x16a8: // steel wall 2 (left/right)
6238 element = EL_DC_STEELWALL_2_HORIZONTAL;
6241 case 0x16a9: // steel wall 2 (up/down)
6242 element = EL_DC_STEELWALL_2_VERTICAL;
6245 case 0x16aa: // steel wall 2 (mid)
6246 element = EL_DC_STEELWALL_2_MIDDLE;
6250 element = EL_SIGN_EXCLAMATION;
6254 element = EL_SIGN_RADIOACTIVITY;
6258 element = EL_SIGN_STOP;
6262 element = EL_SIGN_WHEELCHAIR;
6266 element = EL_SIGN_PARKING;
6270 element = EL_SIGN_NO_ENTRY;
6274 element = EL_SIGN_HEART;
6278 element = EL_SIGN_GIVE_WAY;
6282 element = EL_SIGN_ENTRY_FORBIDDEN;
6286 element = EL_SIGN_EMERGENCY_EXIT;
6290 element = EL_SIGN_YIN_YANG;
6294 element = EL_WALL_EMERALD;
6298 element = EL_WALL_DIAMOND;
6302 element = EL_WALL_PEARL;
6306 element = EL_WALL_CRYSTAL;
6310 element = EL_INVISIBLE_WALL;
6314 element = EL_INVISIBLE_STEELWALL;
6318 // EL_INVISIBLE_SAND
6321 element = EL_LIGHT_SWITCH;
6325 element = EL_ENVELOPE_1;
6329 if (element >= 0x0117 && element <= 0x036e) // (?)
6330 element = EL_DIAMOND;
6331 else if (element >= 0x042d && element <= 0x0684) // (?)
6332 element = EL_EMERALD;
6333 else if (element >= 0x157c && element <= 0x158b)
6335 else if (element >= 0x1590 && element <= 0x159f)
6336 element = EL_DC_LANDMINE;
6337 else if (element >= 0x16bc && element <= 0x16cb)
6338 element = EL_INVISIBLE_SAND;
6341 Warn("unknown Diamond Caves element 0x%04x", element);
6343 element = EL_UNKNOWN;
6348 return getMappedElement(element);
6351 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6353 byte header[DC_LEVEL_HEADER_SIZE];
6355 int envelope_header_pos = 62;
6356 int envelope_content_pos = 94;
6357 int level_name_pos = 251;
6358 int level_author_pos = 292;
6359 int envelope_header_len;
6360 int envelope_content_len;
6362 int level_author_len;
6364 int num_yamyam_contents;
6367 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6369 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6371 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6373 header[i * 2 + 0] = header_word >> 8;
6374 header[i * 2 + 1] = header_word & 0xff;
6377 // read some values from level header to check level decoding integrity
6378 fieldx = header[6] | (header[7] << 8);
6379 fieldy = header[8] | (header[9] << 8);
6380 num_yamyam_contents = header[60] | (header[61] << 8);
6382 // do some simple sanity checks to ensure that level was correctly decoded
6383 if (fieldx < 1 || fieldx > 256 ||
6384 fieldy < 1 || fieldy > 256 ||
6385 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6387 level->no_valid_file = TRUE;
6389 Warn("cannot decode level from stream -- using empty level");
6394 // maximum envelope header size is 31 bytes
6395 envelope_header_len = header[envelope_header_pos];
6396 // maximum envelope content size is 110 (156?) bytes
6397 envelope_content_len = header[envelope_content_pos];
6399 // maximum level title size is 40 bytes
6400 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6401 // maximum level author size is 30 (51?) bytes
6402 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6406 for (i = 0; i < envelope_header_len; i++)
6407 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6408 level->envelope[0].text[envelope_size++] =
6409 header[envelope_header_pos + 1 + i];
6411 if (envelope_header_len > 0 && envelope_content_len > 0)
6413 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6414 level->envelope[0].text[envelope_size++] = '\n';
6415 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6416 level->envelope[0].text[envelope_size++] = '\n';
6419 for (i = 0; i < envelope_content_len; i++)
6420 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6421 level->envelope[0].text[envelope_size++] =
6422 header[envelope_content_pos + 1 + i];
6424 level->envelope[0].text[envelope_size] = '\0';
6426 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6427 level->envelope[0].ysize = 10;
6428 level->envelope[0].autowrap = TRUE;
6429 level->envelope[0].centered = TRUE;
6431 for (i = 0; i < level_name_len; i++)
6432 level->name[i] = header[level_name_pos + 1 + i];
6433 level->name[level_name_len] = '\0';
6435 for (i = 0; i < level_author_len; i++)
6436 level->author[i] = header[level_author_pos + 1 + i];
6437 level->author[level_author_len] = '\0';
6439 num_yamyam_contents = header[60] | (header[61] << 8);
6440 level->num_yamyam_contents =
6441 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6443 for (i = 0; i < num_yamyam_contents; i++)
6445 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6447 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6448 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6450 if (i < MAX_ELEMENT_CONTENTS)
6451 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6455 fieldx = header[6] | (header[7] << 8);
6456 fieldy = header[8] | (header[9] << 8);
6457 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6458 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6460 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6462 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6463 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6465 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6466 level->field[x][y] = getMappedElement_DC(element_dc);
6469 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6470 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6471 level->field[x][y] = EL_PLAYER_1;
6473 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6474 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6475 level->field[x][y] = EL_PLAYER_2;
6477 level->gems_needed = header[18] | (header[19] << 8);
6479 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6480 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6481 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6482 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6483 level->score[SC_NUT] = header[28] | (header[29] << 8);
6484 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6485 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6486 level->score[SC_BUG] = header[34] | (header[35] << 8);
6487 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6488 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6489 level->score[SC_KEY] = header[40] | (header[41] << 8);
6490 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6492 level->time = header[44] | (header[45] << 8);
6494 level->amoeba_speed = header[46] | (header[47] << 8);
6495 level->time_light = header[48] | (header[49] << 8);
6496 level->time_timegate = header[50] | (header[51] << 8);
6497 level->time_wheel = header[52] | (header[53] << 8);
6498 level->time_magic_wall = header[54] | (header[55] << 8);
6499 level->extra_time = header[56] | (header[57] << 8);
6500 level->shield_normal_time = header[58] | (header[59] << 8);
6502 // shield and extra time elements do not have a score
6503 level->score[SC_SHIELD] = 0;
6504 level->extra_time_score = 0;
6506 // set time for normal and deadly shields to the same value
6507 level->shield_deadly_time = level->shield_normal_time;
6509 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6510 // can slip down from flat walls, like normal walls and steel walls
6511 level->em_slippery_gems = TRUE;
6513 // time score is counted for each 10 seconds left in Diamond Caves levels
6514 level->time_score_base = 10;
6517 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6518 struct LevelFileInfo *level_file_info,
6519 boolean level_info_only)
6521 char *filename = level_file_info->filename;
6523 int num_magic_bytes = 8;
6524 char magic_bytes[num_magic_bytes + 1];
6525 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6527 if (!(file = openFile(filename, MODE_READ)))
6529 level->no_valid_file = TRUE;
6531 if (!level_info_only)
6532 Warn("cannot read level '%s' -- using empty level", filename);
6537 // fseek(file, 0x0000, SEEK_SET);
6539 if (level_file_info->packed)
6541 // read "magic bytes" from start of file
6542 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6543 magic_bytes[0] = '\0';
6545 // check "magic bytes" for correct file format
6546 if (!strPrefix(magic_bytes, "DC2"))
6548 level->no_valid_file = TRUE;
6550 Warn("unknown DC level file '%s' -- using empty level", filename);
6555 if (strPrefix(magic_bytes, "DC2Win95") ||
6556 strPrefix(magic_bytes, "DC2Win98"))
6558 int position_first_level = 0x00fa;
6559 int extra_bytes = 4;
6562 // advance file stream to first level inside the level package
6563 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6565 // each block of level data is followed by block of non-level data
6566 num_levels_to_skip *= 2;
6568 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6569 while (num_levels_to_skip >= 0)
6571 // advance file stream to next level inside the level package
6572 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6574 level->no_valid_file = TRUE;
6576 Warn("cannot fseek in file '%s' -- using empty level", filename);
6581 // skip apparently unused extra bytes following each level
6582 ReadUnusedBytesFromFile(file, extra_bytes);
6584 // read size of next level in level package
6585 skip_bytes = getFile32BitLE(file);
6587 num_levels_to_skip--;
6592 level->no_valid_file = TRUE;
6594 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6600 LoadLevelFromFileStream_DC(file, level);
6606 // ----------------------------------------------------------------------------
6607 // functions for loading SB level
6608 // ----------------------------------------------------------------------------
6610 int getMappedElement_SB(int element_ascii, boolean use_ces)
6618 sb_element_mapping[] =
6620 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6621 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6622 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6623 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6624 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6625 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6626 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6627 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6634 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6635 if (element_ascii == sb_element_mapping[i].ascii)
6636 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6638 return EL_UNDEFINED;
6641 static void SetLevelSettings_SB(struct LevelInfo *level)
6645 level->use_step_counter = TRUE;
6648 level->score[SC_TIME_BONUS] = 0;
6649 level->time_score_base = 1;
6650 level->rate_time_over_score = TRUE;
6653 level->auto_exit_sokoban = TRUE;
6656 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6657 struct LevelFileInfo *level_file_info,
6658 boolean level_info_only)
6660 char *filename = level_file_info->filename;
6661 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6662 char last_comment[MAX_LINE_LEN];
6663 char level_name[MAX_LINE_LEN];
6666 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6667 boolean read_continued_line = FALSE;
6668 boolean reading_playfield = FALSE;
6669 boolean got_valid_playfield_line = FALSE;
6670 boolean invalid_playfield_char = FALSE;
6671 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6672 int file_level_nr = 0;
6673 int x = 0, y = 0; // initialized to make compilers happy
6675 last_comment[0] = '\0';
6676 level_name[0] = '\0';
6678 if (!(file = openFile(filename, MODE_READ)))
6680 level->no_valid_file = TRUE;
6682 if (!level_info_only)
6683 Warn("cannot read level '%s' -- using empty level", filename);
6688 while (!checkEndOfFile(file))
6690 // level successfully read, but next level may follow here
6691 if (!got_valid_playfield_line && reading_playfield)
6693 // read playfield from single level file -- skip remaining file
6694 if (!level_file_info->packed)
6697 if (file_level_nr >= num_levels_to_skip)
6702 last_comment[0] = '\0';
6703 level_name[0] = '\0';
6705 reading_playfield = FALSE;
6708 got_valid_playfield_line = FALSE;
6710 // read next line of input file
6711 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6714 // cut trailing line break (this can be newline and/or carriage return)
6715 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6716 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6719 // copy raw input line for later use (mainly debugging output)
6720 strcpy(line_raw, line);
6722 if (read_continued_line)
6724 // append new line to existing line, if there is enough space
6725 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6726 strcat(previous_line, line_ptr);
6728 strcpy(line, previous_line); // copy storage buffer to line
6730 read_continued_line = FALSE;
6733 // if the last character is '\', continue at next line
6734 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6736 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6737 strcpy(previous_line, line); // copy line to storage buffer
6739 read_continued_line = TRUE;
6745 if (line[0] == '\0')
6748 // extract comment text from comment line
6751 for (line_ptr = line; *line_ptr; line_ptr++)
6752 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6755 strcpy(last_comment, line_ptr);
6760 // extract level title text from line containing level title
6761 if (line[0] == '\'')
6763 strcpy(level_name, &line[1]);
6765 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6766 level_name[strlen(level_name) - 1] = '\0';
6771 // skip lines containing only spaces (or empty lines)
6772 for (line_ptr = line; *line_ptr; line_ptr++)
6773 if (*line_ptr != ' ')
6775 if (*line_ptr == '\0')
6778 // at this point, we have found a line containing part of a playfield
6780 got_valid_playfield_line = TRUE;
6782 if (!reading_playfield)
6784 reading_playfield = TRUE;
6785 invalid_playfield_char = FALSE;
6787 for (x = 0; x < MAX_LEV_FIELDX; x++)
6788 for (y = 0; y < MAX_LEV_FIELDY; y++)
6789 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6794 // start with topmost tile row
6798 // skip playfield line if larger row than allowed
6799 if (y >= MAX_LEV_FIELDY)
6802 // start with leftmost tile column
6805 // read playfield elements from line
6806 for (line_ptr = line; *line_ptr; line_ptr++)
6808 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6810 // stop parsing playfield line if larger column than allowed
6811 if (x >= MAX_LEV_FIELDX)
6814 if (mapped_sb_element == EL_UNDEFINED)
6816 invalid_playfield_char = TRUE;
6821 level->field[x][y] = mapped_sb_element;
6823 // continue with next tile column
6826 level->fieldx = MAX(x, level->fieldx);
6829 if (invalid_playfield_char)
6831 // if first playfield line, treat invalid lines as comment lines
6833 reading_playfield = FALSE;
6838 // continue with next tile row
6846 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6847 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6849 if (!reading_playfield)
6851 level->no_valid_file = TRUE;
6853 Warn("cannot read level '%s' -- using empty level", filename);
6858 if (*level_name != '\0')
6860 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6861 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6863 else if (*last_comment != '\0')
6865 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6866 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6870 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6873 // set all empty fields beyond the border walls to invisible steel wall
6874 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6876 if ((x == 0 || x == level->fieldx - 1 ||
6877 y == 0 || y == level->fieldy - 1) &&
6878 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6879 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6880 level->field, level->fieldx, level->fieldy);
6883 // set special level settings for Sokoban levels
6884 SetLevelSettings_SB(level);
6886 if (load_xsb_to_ces)
6888 // special global settings can now be set in level template
6889 level->use_custom_template = TRUE;
6894 // -------------------------------------------------------------------------
6895 // functions for handling native levels
6896 // -------------------------------------------------------------------------
6898 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6899 struct LevelFileInfo *level_file_info,
6900 boolean level_info_only)
6904 // determine position of requested level inside level package
6905 if (level_file_info->packed)
6906 pos = level_file_info->nr - leveldir_current->first_level;
6908 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6909 level->no_valid_file = TRUE;
6912 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6913 struct LevelFileInfo *level_file_info,
6914 boolean level_info_only)
6916 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6917 level->no_valid_file = TRUE;
6920 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6921 struct LevelFileInfo *level_file_info,
6922 boolean level_info_only)
6926 // determine position of requested level inside level package
6927 if (level_file_info->packed)
6928 pos = level_file_info->nr - leveldir_current->first_level;
6930 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6931 level->no_valid_file = TRUE;
6934 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6935 struct LevelFileInfo *level_file_info,
6936 boolean level_info_only)
6938 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6939 level->no_valid_file = TRUE;
6942 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6944 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6945 CopyNativeLevel_RND_to_BD(level);
6946 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6947 CopyNativeLevel_RND_to_EM(level);
6948 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6949 CopyNativeLevel_RND_to_SP(level);
6950 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6951 CopyNativeLevel_RND_to_MM(level);
6954 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6956 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6957 CopyNativeLevel_BD_to_RND(level);
6958 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6959 CopyNativeLevel_EM_to_RND(level);
6960 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6961 CopyNativeLevel_SP_to_RND(level);
6962 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6963 CopyNativeLevel_MM_to_RND(level);
6966 void SaveNativeLevel(struct LevelInfo *level)
6968 // saving native level files only supported for some game engines
6969 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6970 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6973 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6974 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6975 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6976 char *filename = getLevelFilenameFromBasename(basename);
6978 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6981 boolean success = FALSE;
6983 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6985 CopyNativeLevel_RND_to_BD(level);
6986 // CopyNativeTape_RND_to_BD(level);
6988 success = SaveNativeLevel_BD(filename);
6990 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6992 CopyNativeLevel_RND_to_SP(level);
6993 CopyNativeTape_RND_to_SP(level);
6995 success = SaveNativeLevel_SP(filename);
6999 Request("Native level file saved!", REQ_CONFIRM);
7001 Request("Failed to save native level file!", REQ_CONFIRM);
7005 // ----------------------------------------------------------------------------
7006 // functions for loading generic level
7007 // ----------------------------------------------------------------------------
7009 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7010 struct LevelFileInfo *level_file_info,
7011 boolean level_info_only)
7013 // always start with reliable default values
7014 setLevelInfoToDefaults(level, level_info_only, TRUE);
7016 switch (level_file_info->type)
7018 case LEVEL_FILE_TYPE_RND:
7019 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7022 case LEVEL_FILE_TYPE_BD:
7023 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7024 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7027 case LEVEL_FILE_TYPE_EM:
7028 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7029 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7032 case LEVEL_FILE_TYPE_SP:
7033 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7034 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7037 case LEVEL_FILE_TYPE_MM:
7038 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7039 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7042 case LEVEL_FILE_TYPE_DC:
7043 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7046 case LEVEL_FILE_TYPE_SB:
7047 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7051 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7055 // if level file is invalid, restore level structure to default values
7056 if (level->no_valid_file)
7057 setLevelInfoToDefaults(level, level_info_only, FALSE);
7059 if (check_special_flags("use_native_bd_game_engine"))
7060 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7062 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7063 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7065 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7066 CopyNativeLevel_Native_to_RND(level);
7069 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7071 static struct LevelFileInfo level_file_info;
7073 // always start with reliable default values
7074 setFileInfoToDefaults(&level_file_info);
7076 level_file_info.nr = 0; // unknown level number
7077 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7079 setString(&level_file_info.filename, filename);
7081 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7084 static void LoadLevel_InitVersion(struct LevelInfo *level)
7088 if (leveldir_current == NULL) // only when dumping level
7091 // all engine modifications also valid for levels which use latest engine
7092 if (level->game_version < VERSION_IDENT(3,2,0,5))
7094 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7095 level->time_score_base = 10;
7098 if (leveldir_current->latest_engine)
7100 // ---------- use latest game engine --------------------------------------
7102 /* For all levels which are forced to use the latest game engine version
7103 (normally all but user contributed, private and undefined levels), set
7104 the game engine version to the actual version; this allows for actual
7105 corrections in the game engine to take effect for existing, converted
7106 levels (from "classic" or other existing games) to make the emulation
7107 of the corresponding game more accurate, while (hopefully) not breaking
7108 existing levels created from other players. */
7110 level->game_version = GAME_VERSION_ACTUAL;
7112 /* Set special EM style gems behaviour: EM style gems slip down from
7113 normal, steel and growing wall. As this is a more fundamental change,
7114 it seems better to set the default behaviour to "off" (as it is more
7115 natural) and make it configurable in the level editor (as a property
7116 of gem style elements). Already existing converted levels (neither
7117 private nor contributed levels) are changed to the new behaviour. */
7119 if (level->file_version < FILE_VERSION_2_0)
7120 level->em_slippery_gems = TRUE;
7125 // ---------- use game engine the level was created with --------------------
7127 /* For all levels which are not forced to use the latest game engine
7128 version (normally user contributed, private and undefined levels),
7129 use the version of the game engine the levels were created for.
7131 Since 2.0.1, the game engine version is now directly stored
7132 in the level file (chunk "VERS"), so there is no need anymore
7133 to set the game version from the file version (except for old,
7134 pre-2.0 levels, where the game version is still taken from the
7135 file format version used to store the level -- see above). */
7137 // player was faster than enemies in 1.0.0 and before
7138 if (level->file_version == FILE_VERSION_1_0)
7139 for (i = 0; i < MAX_PLAYERS; i++)
7140 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7142 // default behaviour for EM style gems was "slippery" only in 2.0.1
7143 if (level->game_version == VERSION_IDENT(2,0,1,0))
7144 level->em_slippery_gems = TRUE;
7146 // springs could be pushed over pits before (pre-release version) 2.2.0
7147 if (level->game_version < VERSION_IDENT(2,2,0,0))
7148 level->use_spring_bug = TRUE;
7150 if (level->game_version < VERSION_IDENT(3,2,0,5))
7152 // time orb caused limited time in endless time levels before 3.2.0-5
7153 level->use_time_orb_bug = TRUE;
7155 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7156 level->block_snap_field = FALSE;
7158 // extra time score was same value as time left score before 3.2.0-5
7159 level->extra_time_score = level->score[SC_TIME_BONUS];
7162 if (level->game_version < VERSION_IDENT(3,2,0,7))
7164 // default behaviour for snapping was "not continuous" before 3.2.0-7
7165 level->continuous_snapping = FALSE;
7168 // only few elements were able to actively move into acid before 3.1.0
7169 // trigger settings did not exist before 3.1.0; set to default "any"
7170 if (level->game_version < VERSION_IDENT(3,1,0,0))
7172 // correct "can move into acid" settings (all zero in old levels)
7174 level->can_move_into_acid_bits = 0; // nothing can move into acid
7175 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7177 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7178 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7179 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7180 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7182 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7183 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7185 // correct trigger settings (stored as zero == "none" in old levels)
7187 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7189 int element = EL_CUSTOM_START + i;
7190 struct ElementInfo *ei = &element_info[element];
7192 for (j = 0; j < ei->num_change_pages; j++)
7194 struct ElementChangeInfo *change = &ei->change_page[j];
7196 change->trigger_player = CH_PLAYER_ANY;
7197 change->trigger_page = CH_PAGE_ANY;
7202 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7204 int element = EL_CUSTOM_256;
7205 struct ElementInfo *ei = &element_info[element];
7206 struct ElementChangeInfo *change = &ei->change_page[0];
7208 /* This is needed to fix a problem that was caused by a bugfix in function
7209 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7210 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7211 not replace walkable elements, but instead just placed the player on it,
7212 without placing the Sokoban field under the player). Unfortunately, this
7213 breaks "Snake Bite" style levels when the snake is halfway through a door
7214 that just closes (the snake head is still alive and can be moved in this
7215 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7216 player (without Sokoban element) which then gets killed as designed). */
7218 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7219 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7220 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7221 change->target_element = EL_PLAYER_1;
7224 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7225 if (level->game_version < VERSION_IDENT(3,2,5,0))
7227 /* This is needed to fix a problem that was caused by a bugfix in function
7228 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7229 corrects the behaviour when a custom element changes to another custom
7230 element with a higher element number that has change actions defined.
7231 Normally, only one change per frame is allowed for custom elements.
7232 Therefore, it is checked if a custom element already changed in the
7233 current frame; if it did, subsequent changes are suppressed.
7234 Unfortunately, this is only checked for element changes, but not for
7235 change actions, which are still executed. As the function above loops
7236 through all custom elements from lower to higher, an element change
7237 resulting in a lower CE number won't be checked again, while a target
7238 element with a higher number will also be checked, and potential change
7239 actions will get executed for this CE, too (which is wrong), while
7240 further changes are ignored (which is correct). As this bugfix breaks
7241 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7242 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7243 behaviour for existing levels and tapes that make use of this bug */
7245 level->use_action_after_change_bug = TRUE;
7248 // not centering level after relocating player was default only in 3.2.3
7249 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7250 level->shifted_relocation = TRUE;
7252 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7253 if (level->game_version < VERSION_IDENT(3,2,6,0))
7254 level->em_explodes_by_fire = TRUE;
7256 // levels were solved by the first player entering an exit up to 4.1.0.0
7257 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7258 level->solved_by_one_player = TRUE;
7260 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7261 if (level->game_version < VERSION_IDENT(4,1,1,1))
7262 level->use_life_bugs = TRUE;
7264 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7265 if (level->game_version < VERSION_IDENT(4,1,1,1))
7266 level->sb_objects_needed = FALSE;
7268 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7269 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7270 level->finish_dig_collect = FALSE;
7272 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7273 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7274 level->keep_walkable_ce = TRUE;
7277 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7279 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7282 // check if this level is (not) a Sokoban level
7283 for (y = 0; y < level->fieldy; y++)
7284 for (x = 0; x < level->fieldx; x++)
7285 if (!IS_SB_ELEMENT(Tile[x][y]))
7286 is_sokoban_level = FALSE;
7288 if (is_sokoban_level)
7290 // set special level settings for Sokoban levels
7291 SetLevelSettings_SB(level);
7295 static void LoadLevel_InitSettings(struct LevelInfo *level)
7297 // adjust level settings for (non-native) Sokoban-style levels
7298 LoadLevel_InitSettings_SB(level);
7300 // rename levels with title "nameless level" or if renaming is forced
7301 if (leveldir_current->empty_level_name != NULL &&
7302 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7303 leveldir_current->force_level_name))
7304 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7305 leveldir_current->empty_level_name, level_nr);
7308 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7312 // map elements that have changed in newer versions
7313 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7314 level->game_version);
7315 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7316 for (x = 0; x < 3; x++)
7317 for (y = 0; y < 3; y++)
7318 level->yamyam_content[i].e[x][y] =
7319 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7320 level->game_version);
7324 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7328 // map custom element change events that have changed in newer versions
7329 // (these following values were accidentally changed in version 3.0.1)
7330 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7331 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7333 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7335 int element = EL_CUSTOM_START + i;
7337 // order of checking and copying events to be mapped is important
7338 // (do not change the start and end value -- they are constant)
7339 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7341 if (HAS_CHANGE_EVENT(element, j - 2))
7343 SET_CHANGE_EVENT(element, j - 2, FALSE);
7344 SET_CHANGE_EVENT(element, j, TRUE);
7348 // order of checking and copying events to be mapped is important
7349 // (do not change the start and end value -- they are constant)
7350 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7352 if (HAS_CHANGE_EVENT(element, j - 1))
7354 SET_CHANGE_EVENT(element, j - 1, FALSE);
7355 SET_CHANGE_EVENT(element, j, TRUE);
7361 // initialize "can_change" field for old levels with only one change page
7362 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7364 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7366 int element = EL_CUSTOM_START + i;
7368 if (CAN_CHANGE(element))
7369 element_info[element].change->can_change = TRUE;
7373 // correct custom element values (for old levels without these options)
7374 if (level->game_version < VERSION_IDENT(3,1,1,0))
7376 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7378 int element = EL_CUSTOM_START + i;
7379 struct ElementInfo *ei = &element_info[element];
7381 if (ei->access_direction == MV_NO_DIRECTION)
7382 ei->access_direction = MV_ALL_DIRECTIONS;
7386 // correct custom element values (fix invalid values for all versions)
7389 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7391 int element = EL_CUSTOM_START + i;
7392 struct ElementInfo *ei = &element_info[element];
7394 for (j = 0; j < ei->num_change_pages; j++)
7396 struct ElementChangeInfo *change = &ei->change_page[j];
7398 if (change->trigger_player == CH_PLAYER_NONE)
7399 change->trigger_player = CH_PLAYER_ANY;
7401 if (change->trigger_side == CH_SIDE_NONE)
7402 change->trigger_side = CH_SIDE_ANY;
7407 // initialize "can_explode" field for old levels which did not store this
7408 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7409 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7411 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7413 int element = EL_CUSTOM_START + i;
7415 if (EXPLODES_1X1_OLD(element))
7416 element_info[element].explosion_type = EXPLODES_1X1;
7418 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7419 EXPLODES_SMASHED(element) ||
7420 EXPLODES_IMPACT(element)));
7424 // correct previously hard-coded move delay values for maze runner style
7425 if (level->game_version < VERSION_IDENT(3,1,1,0))
7427 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7429 int element = EL_CUSTOM_START + i;
7431 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7433 // previously hard-coded and therefore ignored
7434 element_info[element].move_delay_fixed = 9;
7435 element_info[element].move_delay_random = 0;
7440 // set some other uninitialized values of custom elements in older levels
7441 if (level->game_version < VERSION_IDENT(3,1,0,0))
7443 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7445 int element = EL_CUSTOM_START + i;
7447 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7449 element_info[element].explosion_delay = 17;
7450 element_info[element].ignition_delay = 8;
7454 // set mouse click change events to work for left/middle/right mouse button
7455 if (level->game_version < VERSION_IDENT(4,2,3,0))
7457 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7459 int element = EL_CUSTOM_START + i;
7460 struct ElementInfo *ei = &element_info[element];
7462 for (j = 0; j < ei->num_change_pages; j++)
7464 struct ElementChangeInfo *change = &ei->change_page[j];
7466 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7467 change->has_event[CE_PRESSED_BY_MOUSE] ||
7468 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7469 change->has_event[CE_MOUSE_PRESSED_ON_X])
7470 change->trigger_side = CH_SIDE_ANY;
7476 static void LoadLevel_InitElements(struct LevelInfo *level)
7478 LoadLevel_InitStandardElements(level);
7480 if (level->file_has_custom_elements)
7481 LoadLevel_InitCustomElements(level);
7483 // initialize element properties for level editor etc.
7484 InitElementPropertiesEngine(level->game_version);
7485 InitElementPropertiesGfxElement();
7488 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7492 // map elements that have changed in newer versions
7493 for (y = 0; y < level->fieldy; y++)
7494 for (x = 0; x < level->fieldx; x++)
7495 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7496 level->game_version);
7498 // clear unused playfield data (nicer if level gets resized in editor)
7499 for (x = 0; x < MAX_LEV_FIELDX; x++)
7500 for (y = 0; y < MAX_LEV_FIELDY; y++)
7501 if (x >= level->fieldx || y >= level->fieldy)
7502 level->field[x][y] = EL_EMPTY;
7504 // copy elements to runtime playfield array
7505 for (x = 0; x < MAX_LEV_FIELDX; x++)
7506 for (y = 0; y < MAX_LEV_FIELDY; y++)
7507 Tile[x][y] = level->field[x][y];
7509 // initialize level size variables for faster access
7510 lev_fieldx = level->fieldx;
7511 lev_fieldy = level->fieldy;
7513 // determine border element for this level
7514 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7515 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7520 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7522 struct LevelFileInfo *level_file_info = &level->file_info;
7524 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7525 CopyNativeLevel_RND_to_Native(level);
7528 static void LoadLevelTemplate_LoadAndInit(void)
7530 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7532 LoadLevel_InitVersion(&level_template);
7533 LoadLevel_InitElements(&level_template);
7534 LoadLevel_InitSettings(&level_template);
7536 ActivateLevelTemplate();
7539 void LoadLevelTemplate(int nr)
7541 if (!fileExists(getGlobalLevelTemplateFilename()))
7543 Warn("no level template found for this level");
7548 setLevelFileInfo(&level_template.file_info, nr);
7550 LoadLevelTemplate_LoadAndInit();
7553 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7555 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7557 LoadLevelTemplate_LoadAndInit();
7560 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7562 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7564 if (level.use_custom_template)
7566 if (network_level != NULL)
7567 LoadNetworkLevelTemplate(network_level);
7569 LoadLevelTemplate(-1);
7572 LoadLevel_InitVersion(&level);
7573 LoadLevel_InitElements(&level);
7574 LoadLevel_InitPlayfield(&level);
7575 LoadLevel_InitSettings(&level);
7577 LoadLevel_InitNativeEngines(&level);
7580 void LoadLevel(int nr)
7582 SetLevelSetInfo(leveldir_current->identifier, nr);
7584 setLevelFileInfo(&level.file_info, nr);
7586 LoadLevel_LoadAndInit(NULL);
7589 void LoadLevelInfoOnly(int nr)
7591 setLevelFileInfo(&level.file_info, nr);
7593 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7596 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7598 SetLevelSetInfo(network_level->leveldir_identifier,
7599 network_level->file_info.nr);
7601 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7603 LoadLevel_LoadAndInit(network_level);
7606 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7610 chunk_size += putFileVersion(file, level->file_version);
7611 chunk_size += putFileVersion(file, level->game_version);
7616 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7620 chunk_size += putFile16BitBE(file, level->creation_date.year);
7621 chunk_size += putFile8Bit(file, level->creation_date.month);
7622 chunk_size += putFile8Bit(file, level->creation_date.day);
7627 #if ENABLE_HISTORIC_CHUNKS
7628 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7632 putFile8Bit(file, level->fieldx);
7633 putFile8Bit(file, level->fieldy);
7635 putFile16BitBE(file, level->time);
7636 putFile16BitBE(file, level->gems_needed);
7638 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7639 putFile8Bit(file, level->name[i]);
7641 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7642 putFile8Bit(file, level->score[i]);
7644 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7645 for (y = 0; y < 3; y++)
7646 for (x = 0; x < 3; x++)
7647 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7648 level->yamyam_content[i].e[x][y]));
7649 putFile8Bit(file, level->amoeba_speed);
7650 putFile8Bit(file, level->time_magic_wall);
7651 putFile8Bit(file, level->time_wheel);
7652 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7653 level->amoeba_content));
7654 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7655 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7656 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7657 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7659 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7661 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7662 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7663 putFile32BitBE(file, level->can_move_into_acid_bits);
7664 putFile8Bit(file, level->dont_collide_with_bits);
7666 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7667 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7669 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7670 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7671 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7673 putFile8Bit(file, level->game_engine_type);
7675 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7679 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7684 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7685 chunk_size += putFile8Bit(file, level->name[i]);
7690 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7695 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7696 chunk_size += putFile8Bit(file, level->author[i]);
7701 #if ENABLE_HISTORIC_CHUNKS
7702 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7707 for (y = 0; y < level->fieldy; y++)
7708 for (x = 0; x < level->fieldx; x++)
7709 if (level->encoding_16bit_field)
7710 chunk_size += putFile16BitBE(file, level->field[x][y]);
7712 chunk_size += putFile8Bit(file, level->field[x][y]);
7718 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7723 for (y = 0; y < level->fieldy; y++)
7724 for (x = 0; x < level->fieldx; x++)
7725 chunk_size += putFile16BitBE(file, level->field[x][y]);
7730 #if ENABLE_HISTORIC_CHUNKS
7731 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7735 putFile8Bit(file, EL_YAMYAM);
7736 putFile8Bit(file, level->num_yamyam_contents);
7737 putFile8Bit(file, 0);
7738 putFile8Bit(file, 0);
7740 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7741 for (y = 0; y < 3; y++)
7742 for (x = 0; x < 3; x++)
7743 if (level->encoding_16bit_field)
7744 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7746 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7750 #if ENABLE_HISTORIC_CHUNKS
7751 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7754 int num_contents, content_xsize, content_ysize;
7755 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7757 if (element == EL_YAMYAM)
7759 num_contents = level->num_yamyam_contents;
7763 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7764 for (y = 0; y < 3; y++)
7765 for (x = 0; x < 3; x++)
7766 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7768 else if (element == EL_BD_AMOEBA)
7774 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7775 for (y = 0; y < 3; y++)
7776 for (x = 0; x < 3; x++)
7777 content_array[i][x][y] = EL_EMPTY;
7778 content_array[0][0][0] = level->amoeba_content;
7782 // chunk header already written -- write empty chunk data
7783 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7785 Warn("cannot save content for element '%d'", element);
7790 putFile16BitBE(file, element);
7791 putFile8Bit(file, num_contents);
7792 putFile8Bit(file, content_xsize);
7793 putFile8Bit(file, content_ysize);
7795 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7797 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7798 for (y = 0; y < 3; y++)
7799 for (x = 0; x < 3; x++)
7800 putFile16BitBE(file, content_array[i][x][y]);
7804 #if ENABLE_HISTORIC_CHUNKS
7805 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7807 int envelope_nr = element - EL_ENVELOPE_1;
7808 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7812 chunk_size += putFile16BitBE(file, element);
7813 chunk_size += putFile16BitBE(file, envelope_len);
7814 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7815 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7817 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7818 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7820 for (i = 0; i < envelope_len; i++)
7821 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7827 #if ENABLE_HISTORIC_CHUNKS
7828 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7829 int num_changed_custom_elements)
7833 putFile16BitBE(file, num_changed_custom_elements);
7835 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7837 int element = EL_CUSTOM_START + i;
7839 struct ElementInfo *ei = &element_info[element];
7841 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7843 if (check < num_changed_custom_elements)
7845 putFile16BitBE(file, element);
7846 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7853 if (check != num_changed_custom_elements) // should not happen
7854 Warn("inconsistent number of custom element properties");
7858 #if ENABLE_HISTORIC_CHUNKS
7859 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7860 int num_changed_custom_elements)
7864 putFile16BitBE(file, num_changed_custom_elements);
7866 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7868 int element = EL_CUSTOM_START + i;
7870 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7872 if (check < num_changed_custom_elements)
7874 putFile16BitBE(file, element);
7875 putFile16BitBE(file, element_info[element].change->target_element);
7882 if (check != num_changed_custom_elements) // should not happen
7883 Warn("inconsistent number of custom target elements");
7887 #if ENABLE_HISTORIC_CHUNKS
7888 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7889 int num_changed_custom_elements)
7891 int i, j, x, y, check = 0;
7893 putFile16BitBE(file, num_changed_custom_elements);
7895 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7897 int element = EL_CUSTOM_START + i;
7898 struct ElementInfo *ei = &element_info[element];
7900 if (ei->modified_settings)
7902 if (check < num_changed_custom_elements)
7904 putFile16BitBE(file, element);
7906 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7907 putFile8Bit(file, ei->description[j]);
7909 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7911 // some free bytes for future properties and padding
7912 WriteUnusedBytesToFile(file, 7);
7914 putFile8Bit(file, ei->use_gfx_element);
7915 putFile16BitBE(file, ei->gfx_element_initial);
7917 putFile8Bit(file, ei->collect_score_initial);
7918 putFile8Bit(file, ei->collect_count_initial);
7920 putFile16BitBE(file, ei->push_delay_fixed);
7921 putFile16BitBE(file, ei->push_delay_random);
7922 putFile16BitBE(file, ei->move_delay_fixed);
7923 putFile16BitBE(file, ei->move_delay_random);
7925 putFile16BitBE(file, ei->move_pattern);
7926 putFile8Bit(file, ei->move_direction_initial);
7927 putFile8Bit(file, ei->move_stepsize);
7929 for (y = 0; y < 3; y++)
7930 for (x = 0; x < 3; x++)
7931 putFile16BitBE(file, ei->content.e[x][y]);
7933 putFile32BitBE(file, ei->change->events);
7935 putFile16BitBE(file, ei->change->target_element);
7937 putFile16BitBE(file, ei->change->delay_fixed);
7938 putFile16BitBE(file, ei->change->delay_random);
7939 putFile16BitBE(file, ei->change->delay_frames);
7941 putFile16BitBE(file, ei->change->initial_trigger_element);
7943 putFile8Bit(file, ei->change->explode);
7944 putFile8Bit(file, ei->change->use_target_content);
7945 putFile8Bit(file, ei->change->only_if_complete);
7946 putFile8Bit(file, ei->change->use_random_replace);
7948 putFile8Bit(file, ei->change->random_percentage);
7949 putFile8Bit(file, ei->change->replace_when);
7951 for (y = 0; y < 3; y++)
7952 for (x = 0; x < 3; x++)
7953 putFile16BitBE(file, ei->change->content.e[x][y]);
7955 putFile8Bit(file, ei->slippery_type);
7957 // some free bytes for future properties and padding
7958 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7965 if (check != num_changed_custom_elements) // should not happen
7966 Warn("inconsistent number of custom element properties");
7970 #if ENABLE_HISTORIC_CHUNKS
7971 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7973 struct ElementInfo *ei = &element_info[element];
7976 // ---------- custom element base property values (96 bytes) ----------------
7978 putFile16BitBE(file, element);
7980 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7981 putFile8Bit(file, ei->description[i]);
7983 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7985 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7987 putFile8Bit(file, ei->num_change_pages);
7989 putFile16BitBE(file, ei->ce_value_fixed_initial);
7990 putFile16BitBE(file, ei->ce_value_random_initial);
7991 putFile8Bit(file, ei->use_last_ce_value);
7993 putFile8Bit(file, ei->use_gfx_element);
7994 putFile16BitBE(file, ei->gfx_element_initial);
7996 putFile8Bit(file, ei->collect_score_initial);
7997 putFile8Bit(file, ei->collect_count_initial);
7999 putFile8Bit(file, ei->drop_delay_fixed);
8000 putFile8Bit(file, ei->push_delay_fixed);
8001 putFile8Bit(file, ei->drop_delay_random);
8002 putFile8Bit(file, ei->push_delay_random);
8003 putFile16BitBE(file, ei->move_delay_fixed);
8004 putFile16BitBE(file, ei->move_delay_random);
8006 // bits 0 - 15 of "move_pattern" ...
8007 putFile16BitBE(file, ei->move_pattern & 0xffff);
8008 putFile8Bit(file, ei->move_direction_initial);
8009 putFile8Bit(file, ei->move_stepsize);
8011 putFile8Bit(file, ei->slippery_type);
8013 for (y = 0; y < 3; y++)
8014 for (x = 0; x < 3; x++)
8015 putFile16BitBE(file, ei->content.e[x][y]);
8017 putFile16BitBE(file, ei->move_enter_element);
8018 putFile16BitBE(file, ei->move_leave_element);
8019 putFile8Bit(file, ei->move_leave_type);
8021 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8022 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8024 putFile8Bit(file, ei->access_direction);
8026 putFile8Bit(file, ei->explosion_delay);
8027 putFile8Bit(file, ei->ignition_delay);
8028 putFile8Bit(file, ei->explosion_type);
8030 // some free bytes for future custom property values and padding
8031 WriteUnusedBytesToFile(file, 1);
8033 // ---------- change page property values (48 bytes) ------------------------
8035 for (i = 0; i < ei->num_change_pages; i++)
8037 struct ElementChangeInfo *change = &ei->change_page[i];
8038 unsigned int event_bits;
8040 // bits 0 - 31 of "has_event[]" ...
8042 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8043 if (change->has_event[j])
8044 event_bits |= (1u << j);
8045 putFile32BitBE(file, event_bits);
8047 putFile16BitBE(file, change->target_element);
8049 putFile16BitBE(file, change->delay_fixed);
8050 putFile16BitBE(file, change->delay_random);
8051 putFile16BitBE(file, change->delay_frames);
8053 putFile16BitBE(file, change->initial_trigger_element);
8055 putFile8Bit(file, change->explode);
8056 putFile8Bit(file, change->use_target_content);
8057 putFile8Bit(file, change->only_if_complete);
8058 putFile8Bit(file, change->use_random_replace);
8060 putFile8Bit(file, change->random_percentage);
8061 putFile8Bit(file, change->replace_when);
8063 for (y = 0; y < 3; y++)
8064 for (x = 0; x < 3; x++)
8065 putFile16BitBE(file, change->target_content.e[x][y]);
8067 putFile8Bit(file, change->can_change);
8069 putFile8Bit(file, change->trigger_side);
8071 putFile8Bit(file, change->trigger_player);
8072 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8073 log_2(change->trigger_page)));
8075 putFile8Bit(file, change->has_action);
8076 putFile8Bit(file, change->action_type);
8077 putFile8Bit(file, change->action_mode);
8078 putFile16BitBE(file, change->action_arg);
8080 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8082 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8083 if (change->has_event[j])
8084 event_bits |= (1u << (j - 32));
8085 putFile8Bit(file, event_bits);
8090 #if ENABLE_HISTORIC_CHUNKS
8091 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8093 struct ElementInfo *ei = &element_info[element];
8094 struct ElementGroupInfo *group = ei->group;
8097 putFile16BitBE(file, element);
8099 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8100 putFile8Bit(file, ei->description[i]);
8102 putFile8Bit(file, group->num_elements);
8104 putFile8Bit(file, ei->use_gfx_element);
8105 putFile16BitBE(file, ei->gfx_element_initial);
8107 putFile8Bit(file, group->choice_mode);
8109 // some free bytes for future values and padding
8110 WriteUnusedBytesToFile(file, 3);
8112 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8113 putFile16BitBE(file, group->element[i]);
8117 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8118 boolean write_element)
8120 int save_type = entry->save_type;
8121 int data_type = entry->data_type;
8122 int conf_type = entry->conf_type;
8123 int byte_mask = conf_type & CONF_MASK_BYTES;
8124 int element = entry->element;
8125 int default_value = entry->default_value;
8127 boolean modified = FALSE;
8129 if (byte_mask != CONF_MASK_MULTI_BYTES)
8131 void *value_ptr = entry->value;
8132 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8135 // check if any settings have been modified before saving them
8136 if (value != default_value)
8139 // do not save if explicitly told or if unmodified default settings
8140 if ((save_type == SAVE_CONF_NEVER) ||
8141 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8145 num_bytes += putFile16BitBE(file, element);
8147 num_bytes += putFile8Bit(file, conf_type);
8148 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8149 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8150 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8153 else if (data_type == TYPE_STRING)
8155 char *default_string = entry->default_string;
8156 char *string = (char *)(entry->value);
8157 int string_length = strlen(string);
8160 // check if any settings have been modified before saving them
8161 if (!strEqual(string, default_string))
8164 // do not save if explicitly told or if unmodified default settings
8165 if ((save_type == SAVE_CONF_NEVER) ||
8166 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8170 num_bytes += putFile16BitBE(file, element);
8172 num_bytes += putFile8Bit(file, conf_type);
8173 num_bytes += putFile16BitBE(file, string_length);
8175 for (i = 0; i < string_length; i++)
8176 num_bytes += putFile8Bit(file, string[i]);
8178 else if (data_type == TYPE_ELEMENT_LIST)
8180 int *element_array = (int *)(entry->value);
8181 int num_elements = *(int *)(entry->num_entities);
8184 // check if any settings have been modified before saving them
8185 for (i = 0; i < num_elements; i++)
8186 if (element_array[i] != default_value)
8189 // do not save if explicitly told or if unmodified default settings
8190 if ((save_type == SAVE_CONF_NEVER) ||
8191 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8195 num_bytes += putFile16BitBE(file, element);
8197 num_bytes += putFile8Bit(file, conf_type);
8198 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8200 for (i = 0; i < num_elements; i++)
8201 num_bytes += putFile16BitBE(file, element_array[i]);
8203 else if (data_type == TYPE_CONTENT_LIST)
8205 struct Content *content = (struct Content *)(entry->value);
8206 int num_contents = *(int *)(entry->num_entities);
8209 // check if any settings have been modified before saving them
8210 for (i = 0; i < num_contents; i++)
8211 for (y = 0; y < 3; y++)
8212 for (x = 0; x < 3; x++)
8213 if (content[i].e[x][y] != default_value)
8216 // do not save if explicitly told or if unmodified default settings
8217 if ((save_type == SAVE_CONF_NEVER) ||
8218 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8222 num_bytes += putFile16BitBE(file, element);
8224 num_bytes += putFile8Bit(file, conf_type);
8225 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8227 for (i = 0; i < num_contents; i++)
8228 for (y = 0; y < 3; y++)
8229 for (x = 0; x < 3; x++)
8230 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8236 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8241 li = *level; // copy level data into temporary buffer
8243 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8244 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8249 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8254 li = *level; // copy level data into temporary buffer
8256 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8257 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8262 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8264 int envelope_nr = element - EL_ENVELOPE_1;
8268 chunk_size += putFile16BitBE(file, element);
8270 // copy envelope data into temporary buffer
8271 xx_envelope = level->envelope[envelope_nr];
8273 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8274 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8279 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8281 struct ElementInfo *ei = &element_info[element];
8285 chunk_size += putFile16BitBE(file, element);
8287 xx_ei = *ei; // copy element data into temporary buffer
8289 // set default description string for this specific element
8290 strcpy(xx_default_description, getDefaultElementDescription(ei));
8292 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8293 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8295 for (i = 0; i < ei->num_change_pages; i++)
8297 struct ElementChangeInfo *change = &ei->change_page[i];
8299 xx_current_change_page = i;
8301 xx_change = *change; // copy change data into temporary buffer
8304 setEventBitsFromEventFlags(change);
8306 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8307 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8314 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8316 struct ElementInfo *ei = &element_info[element];
8317 struct ElementGroupInfo *group = ei->group;
8321 chunk_size += putFile16BitBE(file, element);
8323 xx_ei = *ei; // copy element data into temporary buffer
8324 xx_group = *group; // copy group data into temporary buffer
8326 // set default description string for this specific element
8327 strcpy(xx_default_description, getDefaultElementDescription(ei));
8329 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8330 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8335 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8337 struct ElementInfo *ei = &element_info[element];
8341 chunk_size += putFile16BitBE(file, element);
8343 xx_ei = *ei; // copy element data into temporary buffer
8345 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8346 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8351 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8352 boolean save_as_template)
8358 if (!(file = fopen(filename, MODE_WRITE)))
8360 Warn("cannot save level file '%s'", filename);
8365 level->file_version = FILE_VERSION_ACTUAL;
8366 level->game_version = GAME_VERSION_ACTUAL;
8368 level->creation_date = getCurrentDate();
8370 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8371 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8373 chunk_size = SaveLevel_VERS(NULL, level);
8374 putFileChunkBE(file, "VERS", chunk_size);
8375 SaveLevel_VERS(file, level);
8377 chunk_size = SaveLevel_DATE(NULL, level);
8378 putFileChunkBE(file, "DATE", chunk_size);
8379 SaveLevel_DATE(file, level);
8381 chunk_size = SaveLevel_NAME(NULL, level);
8382 putFileChunkBE(file, "NAME", chunk_size);
8383 SaveLevel_NAME(file, level);
8385 chunk_size = SaveLevel_AUTH(NULL, level);
8386 putFileChunkBE(file, "AUTH", chunk_size);
8387 SaveLevel_AUTH(file, level);
8389 chunk_size = SaveLevel_INFO(NULL, level);
8390 putFileChunkBE(file, "INFO", chunk_size);
8391 SaveLevel_INFO(file, level);
8393 chunk_size = SaveLevel_BODY(NULL, level);
8394 putFileChunkBE(file, "BODY", chunk_size);
8395 SaveLevel_BODY(file, level);
8397 chunk_size = SaveLevel_ELEM(NULL, level);
8398 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8400 putFileChunkBE(file, "ELEM", chunk_size);
8401 SaveLevel_ELEM(file, level);
8404 for (i = 0; i < NUM_ENVELOPES; i++)
8406 int element = EL_ENVELOPE_1 + i;
8408 chunk_size = SaveLevel_NOTE(NULL, level, element);
8409 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8411 putFileChunkBE(file, "NOTE", chunk_size);
8412 SaveLevel_NOTE(file, level, element);
8416 // if not using template level, check for non-default custom/group elements
8417 if (!level->use_custom_template || save_as_template)
8419 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8421 int element = EL_CUSTOM_START + i;
8423 chunk_size = SaveLevel_CUSX(NULL, level, element);
8424 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8426 putFileChunkBE(file, "CUSX", chunk_size);
8427 SaveLevel_CUSX(file, level, element);
8431 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8433 int element = EL_GROUP_START + i;
8435 chunk_size = SaveLevel_GRPX(NULL, level, element);
8436 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8438 putFileChunkBE(file, "GRPX", chunk_size);
8439 SaveLevel_GRPX(file, level, element);
8443 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8445 int element = GET_EMPTY_ELEMENT(i);
8447 chunk_size = SaveLevel_EMPX(NULL, level, element);
8448 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8450 putFileChunkBE(file, "EMPX", chunk_size);
8451 SaveLevel_EMPX(file, level, element);
8458 SetFilePermissions(filename, PERMS_PRIVATE);
8461 void SaveLevel(int nr)
8463 char *filename = getDefaultLevelFilename(nr);
8465 SaveLevelFromFilename(&level, filename, FALSE);
8468 void SaveLevelTemplate(void)
8470 char *filename = getLocalLevelTemplateFilename();
8472 SaveLevelFromFilename(&level, filename, TRUE);
8475 boolean SaveLevelChecked(int nr)
8477 char *filename = getDefaultLevelFilename(nr);
8478 boolean new_level = !fileExists(filename);
8479 boolean level_saved = FALSE;
8481 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8486 Request("Level saved!", REQ_CONFIRM);
8494 void DumpLevel(struct LevelInfo *level)
8496 if (level->no_level_file || level->no_valid_file)
8498 Warn("cannot dump -- no valid level file found");
8504 Print("Level xxx (file version %08d, game version %08d)\n",
8505 level->file_version, level->game_version);
8508 Print("Level author: '%s'\n", level->author);
8509 Print("Level title: '%s'\n", level->name);
8511 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8513 Print("Level time: %d seconds\n", level->time);
8514 Print("Gems needed: %d\n", level->gems_needed);
8516 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8517 Print("Time for wheel: %d seconds\n", level->time_wheel);
8518 Print("Time for light: %d seconds\n", level->time_light);
8519 Print("Time for timegate: %d seconds\n", level->time_timegate);
8521 Print("Amoeba speed: %d\n", level->amoeba_speed);
8524 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8525 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8526 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8527 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8528 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8529 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8535 for (i = 0; i < NUM_ENVELOPES; i++)
8537 char *text = level->envelope[i].text;
8538 int text_len = strlen(text);
8539 boolean has_text = FALSE;
8541 for (j = 0; j < text_len; j++)
8542 if (text[j] != ' ' && text[j] != '\n')
8548 Print("Envelope %d:\n'%s'\n", i + 1, text);
8556 void DumpLevels(void)
8558 static LevelDirTree *dumplevel_leveldir = NULL;
8560 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8561 global.dumplevel_leveldir);
8563 if (dumplevel_leveldir == NULL)
8564 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8566 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8567 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8568 Fail("no such level number: %d", global.dumplevel_level_nr);
8570 leveldir_current = dumplevel_leveldir;
8572 LoadLevel(global.dumplevel_level_nr);
8579 // ============================================================================
8580 // tape file functions
8581 // ============================================================================
8583 static void setTapeInfoToDefaults(void)
8587 // always start with reliable default values (empty tape)
8590 // default values (also for pre-1.2 tapes) with only the first player
8591 tape.player_participates[0] = TRUE;
8592 for (i = 1; i < MAX_PLAYERS; i++)
8593 tape.player_participates[i] = FALSE;
8595 // at least one (default: the first) player participates in every tape
8596 tape.num_participating_players = 1;
8598 tape.property_bits = TAPE_PROPERTY_NONE;
8600 tape.level_nr = level_nr;
8602 tape.changed = FALSE;
8603 tape.solved = FALSE;
8605 tape.recording = FALSE;
8606 tape.playing = FALSE;
8607 tape.pausing = FALSE;
8609 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8610 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8612 tape.no_info_chunk = TRUE;
8613 tape.no_valid_file = FALSE;
8616 static int getTapePosSize(struct TapeInfo *tape)
8618 int tape_pos_size = 0;
8620 if (tape->use_key_actions)
8621 tape_pos_size += tape->num_participating_players;
8623 if (tape->use_mouse_actions)
8624 tape_pos_size += 3; // x and y position and mouse button mask
8626 tape_pos_size += 1; // tape action delay value
8628 return tape_pos_size;
8631 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8633 tape->use_key_actions = FALSE;
8634 tape->use_mouse_actions = FALSE;
8636 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8637 tape->use_key_actions = TRUE;
8639 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8640 tape->use_mouse_actions = TRUE;
8643 static int getTapeActionValue(struct TapeInfo *tape)
8645 return (tape->use_key_actions &&
8646 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8647 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8648 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8649 TAPE_ACTIONS_DEFAULT);
8652 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8654 tape->file_version = getFileVersion(file);
8655 tape->game_version = getFileVersion(file);
8660 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8664 tape->random_seed = getFile32BitBE(file);
8665 tape->date = getFile32BitBE(file);
8666 tape->length = getFile32BitBE(file);
8668 // read header fields that are new since version 1.2
8669 if (tape->file_version >= FILE_VERSION_1_2)
8671 byte store_participating_players = getFile8Bit(file);
8674 // since version 1.2, tapes store which players participate in the tape
8675 tape->num_participating_players = 0;
8676 for (i = 0; i < MAX_PLAYERS; i++)
8678 tape->player_participates[i] = FALSE;
8680 if (store_participating_players & (1 << i))
8682 tape->player_participates[i] = TRUE;
8683 tape->num_participating_players++;
8687 setTapeActionFlags(tape, getFile8Bit(file));
8689 tape->property_bits = getFile8Bit(file);
8690 tape->solved = getFile8Bit(file);
8692 engine_version = getFileVersion(file);
8693 if (engine_version > 0)
8694 tape->engine_version = engine_version;
8696 tape->engine_version = tape->game_version;
8702 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8704 tape->scr_fieldx = getFile8Bit(file);
8705 tape->scr_fieldy = getFile8Bit(file);
8710 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8712 char *level_identifier = NULL;
8713 int level_identifier_size;
8716 tape->no_info_chunk = FALSE;
8718 level_identifier_size = getFile16BitBE(file);
8720 level_identifier = checked_malloc(level_identifier_size);
8722 for (i = 0; i < level_identifier_size; i++)
8723 level_identifier[i] = getFile8Bit(file);
8725 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8726 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8728 checked_free(level_identifier);
8730 tape->level_nr = getFile16BitBE(file);
8732 chunk_size = 2 + level_identifier_size + 2;
8737 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8740 int tape_pos_size = getTapePosSize(tape);
8741 int chunk_size_expected = tape_pos_size * tape->length;
8743 if (chunk_size_expected != chunk_size)
8745 ReadUnusedBytesFromFile(file, chunk_size);
8746 return chunk_size_expected;
8749 for (i = 0; i < tape->length; i++)
8751 if (i >= MAX_TAPE_LEN)
8753 Warn("tape truncated -- size exceeds maximum tape size %d",
8756 // tape too large; read and ignore remaining tape data from this chunk
8757 for (;i < tape->length; i++)
8758 ReadUnusedBytesFromFile(file, tape_pos_size);
8763 if (tape->use_key_actions)
8765 for (j = 0; j < MAX_PLAYERS; j++)
8767 tape->pos[i].action[j] = MV_NONE;
8769 if (tape->player_participates[j])
8770 tape->pos[i].action[j] = getFile8Bit(file);
8774 if (tape->use_mouse_actions)
8776 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8777 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8778 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8781 tape->pos[i].delay = getFile8Bit(file);
8783 if (tape->file_version == FILE_VERSION_1_0)
8785 // eliminate possible diagonal moves in old tapes
8786 // this is only for backward compatibility
8788 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8789 byte action = tape->pos[i].action[0];
8790 int k, num_moves = 0;
8792 for (k = 0; k < 4; k++)
8794 if (action & joy_dir[k])
8796 tape->pos[i + num_moves].action[0] = joy_dir[k];
8798 tape->pos[i + num_moves].delay = 0;
8807 tape->length += num_moves;
8810 else if (tape->file_version < FILE_VERSION_2_0)
8812 // convert pre-2.0 tapes to new tape format
8814 if (tape->pos[i].delay > 1)
8817 tape->pos[i + 1] = tape->pos[i];
8818 tape->pos[i + 1].delay = 1;
8821 for (j = 0; j < MAX_PLAYERS; j++)
8822 tape->pos[i].action[j] = MV_NONE;
8823 tape->pos[i].delay--;
8830 if (checkEndOfFile(file))
8834 if (i != tape->length)
8835 chunk_size = tape_pos_size * i;
8840 static void LoadTape_SokobanSolution(char *filename)
8843 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8845 if (!(file = openFile(filename, MODE_READ)))
8847 tape.no_valid_file = TRUE;
8852 while (!checkEndOfFile(file))
8854 unsigned char c = getByteFromFile(file);
8856 if (checkEndOfFile(file))
8863 tape.pos[tape.length].action[0] = MV_UP;
8864 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8870 tape.pos[tape.length].action[0] = MV_DOWN;
8871 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8877 tape.pos[tape.length].action[0] = MV_LEFT;
8878 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8884 tape.pos[tape.length].action[0] = MV_RIGHT;
8885 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8893 // ignore white-space characters
8897 tape.no_valid_file = TRUE;
8899 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8907 if (tape.no_valid_file)
8910 tape.length_frames = GetTapeLengthFrames();
8911 tape.length_seconds = GetTapeLengthSeconds();
8914 void LoadTapeFromFilename(char *filename)
8916 char cookie[MAX_LINE_LEN];
8917 char chunk_name[CHUNK_ID_LEN + 1];
8921 // always start with reliable default values
8922 setTapeInfoToDefaults();
8924 if (strSuffix(filename, ".sln"))
8926 LoadTape_SokobanSolution(filename);
8931 if (!(file = openFile(filename, MODE_READ)))
8933 tape.no_valid_file = TRUE;
8938 getFileChunkBE(file, chunk_name, NULL);
8939 if (strEqual(chunk_name, "RND1"))
8941 getFile32BitBE(file); // not used
8943 getFileChunkBE(file, chunk_name, NULL);
8944 if (!strEqual(chunk_name, "TAPE"))
8946 tape.no_valid_file = TRUE;
8948 Warn("unknown format of tape file '%s'", filename);
8955 else // check for pre-2.0 file format with cookie string
8957 strcpy(cookie, chunk_name);
8958 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8960 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8961 cookie[strlen(cookie) - 1] = '\0';
8963 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8965 tape.no_valid_file = TRUE;
8967 Warn("unknown format of tape file '%s'", filename);
8974 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8976 tape.no_valid_file = TRUE;
8978 Warn("unsupported version of tape file '%s'", filename);
8985 // pre-2.0 tape files have no game version, so use file version here
8986 tape.game_version = tape.file_version;
8989 if (tape.file_version < FILE_VERSION_1_2)
8991 // tape files from versions before 1.2.0 without chunk structure
8992 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8993 LoadTape_BODY(file, 2 * tape.length, &tape);
9001 int (*loader)(File *, int, struct TapeInfo *);
9005 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9006 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9007 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9008 { "INFO", -1, LoadTape_INFO },
9009 { "BODY", -1, LoadTape_BODY },
9013 while (getFileChunkBE(file, chunk_name, &chunk_size))
9017 while (chunk_info[i].name != NULL &&
9018 !strEqual(chunk_name, chunk_info[i].name))
9021 if (chunk_info[i].name == NULL)
9023 Warn("unknown chunk '%s' in tape file '%s'",
9024 chunk_name, filename);
9026 ReadUnusedBytesFromFile(file, chunk_size);
9028 else if (chunk_info[i].size != -1 &&
9029 chunk_info[i].size != chunk_size)
9031 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9032 chunk_size, chunk_name, filename);
9034 ReadUnusedBytesFromFile(file, chunk_size);
9038 // call function to load this tape chunk
9039 int chunk_size_expected =
9040 (chunk_info[i].loader)(file, chunk_size, &tape);
9042 // the size of some chunks cannot be checked before reading other
9043 // chunks first (like "HEAD" and "BODY") that contain some header
9044 // information, so check them here
9045 if (chunk_size_expected != chunk_size)
9047 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9048 chunk_size, chunk_name, filename);
9056 tape.length_frames = GetTapeLengthFrames();
9057 tape.length_seconds = GetTapeLengthSeconds();
9060 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9062 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9064 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9065 tape.engine_version);
9069 void LoadTape(int nr)
9071 char *filename = getTapeFilename(nr);
9073 LoadTapeFromFilename(filename);
9076 void LoadSolutionTape(int nr)
9078 char *filename = getSolutionTapeFilename(nr);
9080 LoadTapeFromFilename(filename);
9082 if (TAPE_IS_EMPTY(tape))
9084 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9085 level.native_bd_level->replay != NULL)
9086 CopyNativeTape_BD_to_RND(&level);
9087 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9088 level.native_sp_level->demo.is_available)
9089 CopyNativeTape_SP_to_RND(&level);
9093 void LoadScoreTape(char *score_tape_basename, int nr)
9095 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9097 LoadTapeFromFilename(filename);
9100 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9102 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9104 LoadTapeFromFilename(filename);
9107 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9109 // chunk required for team mode tapes with non-default screen size
9110 return (tape->num_participating_players > 1 &&
9111 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9112 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9115 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9117 putFileVersion(file, tape->file_version);
9118 putFileVersion(file, tape->game_version);
9121 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9124 byte store_participating_players = 0;
9126 // set bits for participating players for compact storage
9127 for (i = 0; i < MAX_PLAYERS; i++)
9128 if (tape->player_participates[i])
9129 store_participating_players |= (1 << i);
9131 putFile32BitBE(file, tape->random_seed);
9132 putFile32BitBE(file, tape->date);
9133 putFile32BitBE(file, tape->length);
9135 putFile8Bit(file, store_participating_players);
9137 putFile8Bit(file, getTapeActionValue(tape));
9139 putFile8Bit(file, tape->property_bits);
9140 putFile8Bit(file, tape->solved);
9142 putFileVersion(file, tape->engine_version);
9145 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9147 putFile8Bit(file, tape->scr_fieldx);
9148 putFile8Bit(file, tape->scr_fieldy);
9151 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9153 int level_identifier_size = strlen(tape->level_identifier) + 1;
9156 putFile16BitBE(file, level_identifier_size);
9158 for (i = 0; i < level_identifier_size; i++)
9159 putFile8Bit(file, tape->level_identifier[i]);
9161 putFile16BitBE(file, tape->level_nr);
9164 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9168 for (i = 0; i < tape->length; i++)
9170 if (tape->use_key_actions)
9172 for (j = 0; j < MAX_PLAYERS; j++)
9173 if (tape->player_participates[j])
9174 putFile8Bit(file, tape->pos[i].action[j]);
9177 if (tape->use_mouse_actions)
9179 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9180 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9181 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9184 putFile8Bit(file, tape->pos[i].delay);
9188 void SaveTapeToFilename(char *filename)
9192 int info_chunk_size;
9193 int body_chunk_size;
9195 if (!(file = fopen(filename, MODE_WRITE)))
9197 Warn("cannot save level recording file '%s'", filename);
9202 tape_pos_size = getTapePosSize(&tape);
9204 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9205 body_chunk_size = tape_pos_size * tape.length;
9207 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9208 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9210 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9211 SaveTape_VERS(file, &tape);
9213 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9214 SaveTape_HEAD(file, &tape);
9216 if (checkSaveTape_SCRN(&tape))
9218 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9219 SaveTape_SCRN(file, &tape);
9222 putFileChunkBE(file, "INFO", info_chunk_size);
9223 SaveTape_INFO(file, &tape);
9225 putFileChunkBE(file, "BODY", body_chunk_size);
9226 SaveTape_BODY(file, &tape);
9230 SetFilePermissions(filename, PERMS_PRIVATE);
9233 static void SaveTapeExt(char *filename)
9237 tape.file_version = FILE_VERSION_ACTUAL;
9238 tape.game_version = GAME_VERSION_ACTUAL;
9240 tape.num_participating_players = 0;
9242 // count number of participating players
9243 for (i = 0; i < MAX_PLAYERS; i++)
9244 if (tape.player_participates[i])
9245 tape.num_participating_players++;
9247 SaveTapeToFilename(filename);
9249 tape.changed = FALSE;
9252 void SaveTape(int nr)
9254 char *filename = getTapeFilename(nr);
9256 InitTapeDirectory(leveldir_current->subdir);
9258 SaveTapeExt(filename);
9261 void SaveScoreTape(int nr)
9263 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9265 // used instead of "leveldir_current->subdir" (for network games)
9266 InitScoreTapeDirectory(levelset.identifier, nr);
9268 SaveTapeExt(filename);
9271 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9272 unsigned int req_state_added)
9274 char *filename = getTapeFilename(nr);
9275 boolean new_tape = !fileExists(filename);
9276 boolean tape_saved = FALSE;
9278 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9283 Request(msg_saved, REQ_CONFIRM | req_state_added);
9291 boolean SaveTapeChecked(int nr)
9293 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9296 boolean SaveTapeChecked_LevelSolved(int nr)
9298 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9299 "Level solved! Tape saved!", REQ_STAY_OPEN);
9302 void DumpTape(struct TapeInfo *tape)
9304 int tape_frame_counter;
9307 if (tape->no_valid_file)
9309 Warn("cannot dump -- no valid tape file found");
9316 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9317 tape->level_nr, tape->file_version, tape->game_version);
9318 Print(" (effective engine version %08d)\n",
9319 tape->engine_version);
9320 Print("Level series identifier: '%s'\n", tape->level_identifier);
9322 Print("Solution tape: %s\n",
9323 tape->solved ? "yes" :
9324 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9326 Print("Special tape properties: ");
9327 if (tape->property_bits == TAPE_PROPERTY_NONE)
9329 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9330 Print("[em_random_bug]");
9331 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9332 Print("[game_speed]");
9333 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9335 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9336 Print("[single_step]");
9337 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9338 Print("[snapshot]");
9339 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9340 Print("[replayed]");
9341 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9342 Print("[tas_keys]");
9343 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9344 Print("[small_graphics]");
9347 int year2 = tape->date / 10000;
9348 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9349 int month_index_raw = (tape->date / 100) % 100;
9350 int month_index = month_index_raw % 12; // prevent invalid index
9351 int month = month_index + 1;
9352 int day = tape->date % 100;
9354 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9358 tape_frame_counter = 0;
9360 for (i = 0; i < tape->length; i++)
9362 if (i >= MAX_TAPE_LEN)
9367 for (j = 0; j < MAX_PLAYERS; j++)
9369 if (tape->player_participates[j])
9371 int action = tape->pos[i].action[j];
9373 Print("%d:%02x ", j, action);
9374 Print("[%c%c%c%c|%c%c] - ",
9375 (action & JOY_LEFT ? '<' : ' '),
9376 (action & JOY_RIGHT ? '>' : ' '),
9377 (action & JOY_UP ? '^' : ' '),
9378 (action & JOY_DOWN ? 'v' : ' '),
9379 (action & JOY_BUTTON_1 ? '1' : ' '),
9380 (action & JOY_BUTTON_2 ? '2' : ' '));
9384 Print("(%03d) ", tape->pos[i].delay);
9385 Print("[%05d]\n", tape_frame_counter);
9387 tape_frame_counter += tape->pos[i].delay;
9393 void DumpTapes(void)
9395 static LevelDirTree *dumptape_leveldir = NULL;
9397 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9398 global.dumptape_leveldir);
9400 if (dumptape_leveldir == NULL)
9401 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9403 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9404 global.dumptape_level_nr > dumptape_leveldir->last_level)
9405 Fail("no such level number: %d", global.dumptape_level_nr);
9407 leveldir_current = dumptape_leveldir;
9409 if (options.mytapes)
9410 LoadTape(global.dumptape_level_nr);
9412 LoadSolutionTape(global.dumptape_level_nr);
9420 // ============================================================================
9421 // score file functions
9422 // ============================================================================
9424 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9428 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9430 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9431 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9432 scores->entry[i].score = 0;
9433 scores->entry[i].time = 0;
9435 scores->entry[i].id = -1;
9436 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9437 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9438 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9439 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9440 strcpy(scores->entry[i].country_code, "??");
9443 scores->num_entries = 0;
9444 scores->last_added = -1;
9445 scores->last_added_local = -1;
9447 scores->updated = FALSE;
9448 scores->uploaded = FALSE;
9449 scores->tape_downloaded = FALSE;
9450 scores->force_last_added = FALSE;
9452 // The following values are intentionally not reset here:
9456 // - continue_playing
9457 // - continue_on_return
9460 static void setScoreInfoToDefaults(void)
9462 setScoreInfoToDefaultsExt(&scores);
9465 static void setServerScoreInfoToDefaults(void)
9467 setScoreInfoToDefaultsExt(&server_scores);
9470 static void LoadScore_OLD(int nr)
9473 char *filename = getScoreFilename(nr);
9474 char cookie[MAX_LINE_LEN];
9475 char line[MAX_LINE_LEN];
9479 if (!(file = fopen(filename, MODE_READ)))
9482 // check file identifier
9483 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9485 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9486 cookie[strlen(cookie) - 1] = '\0';
9488 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9490 Warn("unknown format of score file '%s'", filename);
9497 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9499 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9500 Warn("fscanf() failed; %s", strerror(errno));
9502 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9505 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9506 line[strlen(line) - 1] = '\0';
9508 for (line_ptr = line; *line_ptr; line_ptr++)
9510 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9512 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9513 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9522 static void ConvertScore_OLD(void)
9524 // only convert score to time for levels that rate playing time over score
9525 if (!level.rate_time_over_score)
9528 // convert old score to playing time for score-less levels (like Supaplex)
9529 int time_final_max = 999;
9532 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9534 int score = scores.entry[i].score;
9536 if (score > 0 && score < time_final_max)
9537 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9541 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9543 scores->file_version = getFileVersion(file);
9544 scores->game_version = getFileVersion(file);
9549 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9551 char *level_identifier = NULL;
9552 int level_identifier_size;
9555 level_identifier_size = getFile16BitBE(file);
9557 level_identifier = checked_malloc(level_identifier_size);
9559 for (i = 0; i < level_identifier_size; i++)
9560 level_identifier[i] = getFile8Bit(file);
9562 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9563 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9565 checked_free(level_identifier);
9567 scores->level_nr = getFile16BitBE(file);
9568 scores->num_entries = getFile16BitBE(file);
9570 chunk_size = 2 + level_identifier_size + 2 + 2;
9575 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9579 for (i = 0; i < scores->num_entries; i++)
9581 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9582 scores->entry[i].name[j] = getFile8Bit(file);
9584 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9587 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9592 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9596 for (i = 0; i < scores->num_entries; i++)
9597 scores->entry[i].score = getFile16BitBE(file);
9599 chunk_size = scores->num_entries * 2;
9604 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9608 for (i = 0; i < scores->num_entries; i++)
9609 scores->entry[i].score = getFile32BitBE(file);
9611 chunk_size = scores->num_entries * 4;
9616 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9620 for (i = 0; i < scores->num_entries; i++)
9621 scores->entry[i].time = getFile32BitBE(file);
9623 chunk_size = scores->num_entries * 4;
9628 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9632 for (i = 0; i < scores->num_entries; i++)
9634 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9635 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9637 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9640 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9645 void LoadScore(int nr)
9647 char *filename = getScoreFilename(nr);
9648 char cookie[MAX_LINE_LEN];
9649 char chunk_name[CHUNK_ID_LEN + 1];
9651 boolean old_score_file_format = FALSE;
9654 // always start with reliable default values
9655 setScoreInfoToDefaults();
9657 if (!(file = openFile(filename, MODE_READ)))
9660 getFileChunkBE(file, chunk_name, NULL);
9661 if (strEqual(chunk_name, "RND1"))
9663 getFile32BitBE(file); // not used
9665 getFileChunkBE(file, chunk_name, NULL);
9666 if (!strEqual(chunk_name, "SCOR"))
9668 Warn("unknown format of score file '%s'", filename);
9675 else // check for old file format with cookie string
9677 strcpy(cookie, chunk_name);
9678 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9680 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9681 cookie[strlen(cookie) - 1] = '\0';
9683 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9685 Warn("unknown format of score file '%s'", filename);
9692 old_score_file_format = TRUE;
9695 if (old_score_file_format)
9697 // score files from versions before 4.2.4.0 without chunk structure
9700 // convert score to time, if possible (mainly for Supaplex levels)
9709 int (*loader)(File *, int, struct ScoreInfo *);
9713 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9714 { "INFO", -1, LoadScore_INFO },
9715 { "NAME", -1, LoadScore_NAME },
9716 { "SCOR", -1, LoadScore_SCOR },
9717 { "SC4R", -1, LoadScore_SC4R },
9718 { "TIME", -1, LoadScore_TIME },
9719 { "TAPE", -1, LoadScore_TAPE },
9724 while (getFileChunkBE(file, chunk_name, &chunk_size))
9728 while (chunk_info[i].name != NULL &&
9729 !strEqual(chunk_name, chunk_info[i].name))
9732 if (chunk_info[i].name == NULL)
9734 Warn("unknown chunk '%s' in score file '%s'",
9735 chunk_name, filename);
9737 ReadUnusedBytesFromFile(file, chunk_size);
9739 else if (chunk_info[i].size != -1 &&
9740 chunk_info[i].size != chunk_size)
9742 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9743 chunk_size, chunk_name, filename);
9745 ReadUnusedBytesFromFile(file, chunk_size);
9749 // call function to load this score chunk
9750 int chunk_size_expected =
9751 (chunk_info[i].loader)(file, chunk_size, &scores);
9753 // the size of some chunks cannot be checked before reading other
9754 // chunks first (like "HEAD" and "BODY") that contain some header
9755 // information, so check them here
9756 if (chunk_size_expected != chunk_size)
9758 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9759 chunk_size, chunk_name, filename);
9768 #if ENABLE_HISTORIC_CHUNKS
9769 void SaveScore_OLD(int nr)
9772 char *filename = getScoreFilename(nr);
9775 // used instead of "leveldir_current->subdir" (for network games)
9776 InitScoreDirectory(levelset.identifier);
9778 if (!(file = fopen(filename, MODE_WRITE)))
9780 Warn("cannot save score for level %d", nr);
9785 fprintf(file, "%s\n\n", SCORE_COOKIE);
9787 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9788 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9792 SetFilePermissions(filename, PERMS_PRIVATE);
9796 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9798 putFileVersion(file, scores->file_version);
9799 putFileVersion(file, scores->game_version);
9802 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9804 int level_identifier_size = strlen(scores->level_identifier) + 1;
9807 putFile16BitBE(file, level_identifier_size);
9809 for (i = 0; i < level_identifier_size; i++)
9810 putFile8Bit(file, scores->level_identifier[i]);
9812 putFile16BitBE(file, scores->level_nr);
9813 putFile16BitBE(file, scores->num_entries);
9816 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9820 for (i = 0; i < scores->num_entries; i++)
9822 int name_size = strlen(scores->entry[i].name);
9824 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9825 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9829 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9833 for (i = 0; i < scores->num_entries; i++)
9834 putFile16BitBE(file, scores->entry[i].score);
9837 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9841 for (i = 0; i < scores->num_entries; i++)
9842 putFile32BitBE(file, scores->entry[i].score);
9845 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9849 for (i = 0; i < scores->num_entries; i++)
9850 putFile32BitBE(file, scores->entry[i].time);
9853 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9857 for (i = 0; i < scores->num_entries; i++)
9859 int size = strlen(scores->entry[i].tape_basename);
9861 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9862 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9866 static void SaveScoreToFilename(char *filename)
9869 int info_chunk_size;
9870 int name_chunk_size;
9871 int scor_chunk_size;
9872 int sc4r_chunk_size;
9873 int time_chunk_size;
9874 int tape_chunk_size;
9875 boolean has_large_score_values;
9878 if (!(file = fopen(filename, MODE_WRITE)))
9880 Warn("cannot save score file '%s'", filename);
9885 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9886 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9887 scor_chunk_size = scores.num_entries * 2;
9888 sc4r_chunk_size = scores.num_entries * 4;
9889 time_chunk_size = scores.num_entries * 4;
9890 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9892 has_large_score_values = FALSE;
9893 for (i = 0; i < scores.num_entries; i++)
9894 if (scores.entry[i].score > 0xffff)
9895 has_large_score_values = TRUE;
9897 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9898 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9900 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9901 SaveScore_VERS(file, &scores);
9903 putFileChunkBE(file, "INFO", info_chunk_size);
9904 SaveScore_INFO(file, &scores);
9906 putFileChunkBE(file, "NAME", name_chunk_size);
9907 SaveScore_NAME(file, &scores);
9909 if (has_large_score_values)
9911 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9912 SaveScore_SC4R(file, &scores);
9916 putFileChunkBE(file, "SCOR", scor_chunk_size);
9917 SaveScore_SCOR(file, &scores);
9920 putFileChunkBE(file, "TIME", time_chunk_size);
9921 SaveScore_TIME(file, &scores);
9923 putFileChunkBE(file, "TAPE", tape_chunk_size);
9924 SaveScore_TAPE(file, &scores);
9928 SetFilePermissions(filename, PERMS_PRIVATE);
9931 void SaveScore(int nr)
9933 char *filename = getScoreFilename(nr);
9936 // used instead of "leveldir_current->subdir" (for network games)
9937 InitScoreDirectory(levelset.identifier);
9939 scores.file_version = FILE_VERSION_ACTUAL;
9940 scores.game_version = GAME_VERSION_ACTUAL;
9942 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9943 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9944 scores.level_nr = level_nr;
9946 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9947 if (scores.entry[i].score == 0 &&
9948 scores.entry[i].time == 0 &&
9949 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9952 scores.num_entries = i;
9954 if (scores.num_entries == 0)
9957 SaveScoreToFilename(filename);
9960 static void LoadServerScoreFromCache(int nr)
9962 struct ScoreEntry score_entry;
9971 { &score_entry.score, FALSE, 0 },
9972 { &score_entry.time, FALSE, 0 },
9973 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9974 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9975 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9976 { &score_entry.id, FALSE, 0 },
9977 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9978 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9979 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9980 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9984 char *filename = getScoreCacheFilename(nr);
9985 SetupFileHash *score_hash = loadSetupFileHash(filename);
9988 server_scores.num_entries = 0;
9990 if (score_hash == NULL)
9993 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9995 score_entry = server_scores.entry[i];
9997 for (j = 0; score_mapping[j].value != NULL; j++)
10001 sprintf(token, "%02d.%d", i, j);
10003 char *value = getHashEntry(score_hash, token);
10008 if (score_mapping[j].is_string)
10010 char *score_value = (char *)score_mapping[j].value;
10011 int value_size = score_mapping[j].string_size;
10013 strncpy(score_value, value, value_size);
10014 score_value[value_size] = '\0';
10018 int *score_value = (int *)score_mapping[j].value;
10020 *score_value = atoi(value);
10023 server_scores.num_entries = i + 1;
10026 server_scores.entry[i] = score_entry;
10029 freeSetupFileHash(score_hash);
10032 void LoadServerScore(int nr, boolean download_score)
10034 if (!setup.use_api_server)
10037 // always start with reliable default values
10038 setServerScoreInfoToDefaults();
10040 // 1st step: load server scores from cache file (which may not exist)
10041 // (this should prevent reading it while the thread is writing to it)
10042 LoadServerScoreFromCache(nr);
10044 if (download_score && runtime.use_api_server)
10046 // 2nd step: download server scores from score server to cache file
10047 // (as thread, as it might time out if the server is not reachable)
10048 ApiGetScoreAsThread(nr);
10052 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10054 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10056 // if score tape not uploaded, ask for uploading missing tapes later
10057 if (!setup.has_remaining_tapes)
10058 setup.ask_for_remaining_tapes = TRUE;
10060 setup.provide_uploading_tapes = TRUE;
10061 setup.has_remaining_tapes = TRUE;
10063 SaveSetup_ServerSetup();
10066 void SaveServerScore(int nr, boolean tape_saved)
10068 if (!runtime.use_api_server)
10070 PrepareScoreTapesForUpload(leveldir_current->subdir);
10075 ApiAddScoreAsThread(nr, tape_saved, NULL);
10078 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10079 char *score_tape_filename)
10081 if (!runtime.use_api_server)
10084 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10087 void LoadLocalAndServerScore(int nr, boolean download_score)
10089 int last_added_local = scores.last_added_local;
10090 boolean force_last_added = scores.force_last_added;
10092 // needed if only showing server scores
10093 setScoreInfoToDefaults();
10095 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10098 // restore last added local score entry (before merging server scores)
10099 scores.last_added = scores.last_added_local = last_added_local;
10101 if (setup.use_api_server &&
10102 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10104 // load server scores from cache file and trigger update from server
10105 LoadServerScore(nr, download_score);
10107 // merge local scores with scores from server
10108 MergeServerScore();
10111 if (force_last_added)
10112 scores.force_last_added = force_last_added;
10116 // ============================================================================
10117 // setup file functions
10118 // ============================================================================
10120 #define TOKEN_STR_PLAYER_PREFIX "player_"
10123 static struct TokenInfo global_setup_tokens[] =
10127 &setup.player_name, "player_name"
10131 &setup.multiple_users, "multiple_users"
10135 &setup.sound, "sound"
10139 &setup.sound_loops, "repeating_sound_loops"
10143 &setup.sound_music, "background_music"
10147 &setup.sound_simple, "simple_sound_effects"
10151 &setup.toons, "toons"
10155 &setup.global_animations, "global_animations"
10159 &setup.scroll_delay, "scroll_delay"
10163 &setup.forced_scroll_delay, "forced_scroll_delay"
10167 &setup.scroll_delay_value, "scroll_delay_value"
10171 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10175 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10179 &setup.fade_screens, "fade_screens"
10183 &setup.autorecord, "automatic_tape_recording"
10187 &setup.autorecord_after_replay, "autorecord_after_replay"
10191 &setup.auto_pause_on_start, "auto_pause_on_start"
10195 &setup.show_titlescreen, "show_titlescreen"
10199 &setup.quick_doors, "quick_doors"
10203 &setup.team_mode, "team_mode"
10207 &setup.handicap, "handicap"
10211 &setup.skip_levels, "skip_levels"
10215 &setup.increment_levels, "increment_levels"
10219 &setup.auto_play_next_level, "auto_play_next_level"
10223 &setup.count_score_after_game, "count_score_after_game"
10227 &setup.show_scores_after_game, "show_scores_after_game"
10231 &setup.time_limit, "time_limit"
10235 &setup.fullscreen, "fullscreen"
10239 &setup.window_scaling_percent, "window_scaling_percent"
10243 &setup.window_scaling_quality, "window_scaling_quality"
10247 &setup.screen_rendering_mode, "screen_rendering_mode"
10251 &setup.vsync_mode, "vsync_mode"
10255 &setup.ask_on_escape, "ask_on_escape"
10259 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10263 &setup.ask_on_game_over, "ask_on_game_over"
10267 &setup.ask_on_quit_game, "ask_on_quit_game"
10271 &setup.ask_on_quit_program, "ask_on_quit_program"
10275 &setup.quick_switch, "quick_player_switch"
10279 &setup.input_on_focus, "input_on_focus"
10283 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10287 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10291 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10295 &setup.game_speed_extended, "game_speed_extended"
10299 &setup.game_frame_delay, "game_frame_delay"
10303 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10307 &setup.bd_skip_hatching, "bd_skip_hatching"
10311 &setup.bd_scroll_delay, "bd_scroll_delay"
10315 &setup.bd_smooth_movements, "bd_smooth_movements"
10319 &setup.sp_show_border_elements, "sp_show_border_elements"
10323 &setup.small_game_graphics, "small_game_graphics"
10327 &setup.show_load_save_buttons, "show_load_save_buttons"
10331 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10335 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10339 &setup.graphics_set, "graphics_set"
10343 &setup.sounds_set, "sounds_set"
10347 &setup.music_set, "music_set"
10351 &setup.override_level_graphics, "override_level_graphics"
10355 &setup.override_level_sounds, "override_level_sounds"
10359 &setup.override_level_music, "override_level_music"
10363 &setup.volume_simple, "volume_simple"
10367 &setup.volume_loops, "volume_loops"
10371 &setup.volume_music, "volume_music"
10375 &setup.network_mode, "network_mode"
10379 &setup.network_player_nr, "network_player"
10383 &setup.network_server_hostname, "network_server_hostname"
10387 &setup.touch.control_type, "touch.control_type"
10391 &setup.touch.move_distance, "touch.move_distance"
10395 &setup.touch.drop_distance, "touch.drop_distance"
10399 &setup.touch.transparency, "touch.transparency"
10403 &setup.touch.draw_outlined, "touch.draw_outlined"
10407 &setup.touch.draw_pressed, "touch.draw_pressed"
10411 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10415 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10419 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10423 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10427 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10431 static struct TokenInfo auto_setup_tokens[] =
10435 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10439 static struct TokenInfo server_setup_tokens[] =
10443 &setup.player_uuid, "player_uuid"
10447 &setup.player_version, "player_version"
10451 &setup.use_api_server, TEST_PREFIX "use_api_server"
10455 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10459 &setup.api_server_password, TEST_PREFIX "api_server_password"
10463 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10467 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10471 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10475 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10479 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10483 static struct TokenInfo editor_setup_tokens[] =
10487 &setup.editor.el_classic, "editor.el_classic"
10491 &setup.editor.el_custom, "editor.el_custom"
10495 &setup.editor.el_user_defined, "editor.el_user_defined"
10499 &setup.editor.el_dynamic, "editor.el_dynamic"
10503 &setup.editor.el_headlines, "editor.el_headlines"
10507 &setup.editor.show_element_token, "editor.show_element_token"
10511 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10515 static struct TokenInfo editor_cascade_setup_tokens[] =
10519 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10523 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10527 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10531 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10535 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10539 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10543 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10547 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10551 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10555 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10559 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10563 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10567 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10571 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10575 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10579 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10583 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10587 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10591 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10595 static struct TokenInfo shortcut_setup_tokens[] =
10599 &setup.shortcut.save_game, "shortcut.save_game"
10603 &setup.shortcut.load_game, "shortcut.load_game"
10607 &setup.shortcut.restart_game, "shortcut.restart_game"
10611 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10615 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10619 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10623 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10627 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10631 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10635 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10639 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10643 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10647 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10651 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10655 &setup.shortcut.tape_record, "shortcut.tape_record"
10659 &setup.shortcut.tape_play, "shortcut.tape_play"
10663 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10667 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10671 &setup.shortcut.sound_music, "shortcut.sound_music"
10675 &setup.shortcut.snap_left, "shortcut.snap_left"
10679 &setup.shortcut.snap_right, "shortcut.snap_right"
10683 &setup.shortcut.snap_up, "shortcut.snap_up"
10687 &setup.shortcut.snap_down, "shortcut.snap_down"
10691 static struct SetupInputInfo setup_input;
10692 static struct TokenInfo player_setup_tokens[] =
10696 &setup_input.use_joystick, ".use_joystick"
10700 &setup_input.joy.device_name, ".joy.device_name"
10704 &setup_input.joy.xleft, ".joy.xleft"
10708 &setup_input.joy.xmiddle, ".joy.xmiddle"
10712 &setup_input.joy.xright, ".joy.xright"
10716 &setup_input.joy.yupper, ".joy.yupper"
10720 &setup_input.joy.ymiddle, ".joy.ymiddle"
10724 &setup_input.joy.ylower, ".joy.ylower"
10728 &setup_input.joy.snap, ".joy.snap_field"
10732 &setup_input.joy.drop, ".joy.place_bomb"
10736 &setup_input.key.left, ".key.move_left"
10740 &setup_input.key.right, ".key.move_right"
10744 &setup_input.key.up, ".key.move_up"
10748 &setup_input.key.down, ".key.move_down"
10752 &setup_input.key.snap, ".key.snap_field"
10756 &setup_input.key.drop, ".key.place_bomb"
10760 static struct TokenInfo system_setup_tokens[] =
10764 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10768 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10772 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10776 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10780 static struct TokenInfo internal_setup_tokens[] =
10784 &setup.internal.program_title, "program_title"
10788 &setup.internal.program_version, "program_version"
10792 &setup.internal.program_author, "program_author"
10796 &setup.internal.program_email, "program_email"
10800 &setup.internal.program_website, "program_website"
10804 &setup.internal.program_copyright, "program_copyright"
10808 &setup.internal.program_company, "program_company"
10812 &setup.internal.program_icon_file, "program_icon_file"
10816 &setup.internal.default_graphics_set, "default_graphics_set"
10820 &setup.internal.default_sounds_set, "default_sounds_set"
10824 &setup.internal.default_music_set, "default_music_set"
10828 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10832 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10836 &setup.internal.fallback_music_file, "fallback_music_file"
10840 &setup.internal.default_level_series, "default_level_series"
10844 &setup.internal.default_window_width, "default_window_width"
10848 &setup.internal.default_window_height, "default_window_height"
10852 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10856 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10860 &setup.internal.create_user_levelset, "create_user_levelset"
10864 &setup.internal.info_screens_from_main, "info_screens_from_main"
10868 &setup.internal.menu_game, "menu_game"
10872 &setup.internal.menu_engines, "menu_engines"
10876 &setup.internal.menu_editor, "menu_editor"
10880 &setup.internal.menu_graphics, "menu_graphics"
10884 &setup.internal.menu_sound, "menu_sound"
10888 &setup.internal.menu_artwork, "menu_artwork"
10892 &setup.internal.menu_input, "menu_input"
10896 &setup.internal.menu_touch, "menu_touch"
10900 &setup.internal.menu_shortcuts, "menu_shortcuts"
10904 &setup.internal.menu_exit, "menu_exit"
10908 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10912 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10916 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10920 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10924 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10928 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10932 &setup.internal.info_title, "info_title"
10936 &setup.internal.info_elements, "info_elements"
10940 &setup.internal.info_music, "info_music"
10944 &setup.internal.info_credits, "info_credits"
10948 &setup.internal.info_program, "info_program"
10952 &setup.internal.info_version, "info_version"
10956 &setup.internal.info_levelset, "info_levelset"
10960 &setup.internal.info_exit, "info_exit"
10964 static struct TokenInfo debug_setup_tokens[] =
10968 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10972 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10976 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10980 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10984 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10988 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10992 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10996 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11000 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11004 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11008 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11012 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11016 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11020 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11024 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11028 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11032 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11036 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11040 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11044 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11048 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11051 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11055 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11059 &setup.debug.xsn_mode, "debug.xsn_mode"
11063 &setup.debug.xsn_percent, "debug.xsn_percent"
11067 static struct TokenInfo options_setup_tokens[] =
11071 &setup.options.verbose, "options.verbose"
11075 &setup.options.debug, "options.debug"
11079 &setup.options.debug_mode, "options.debug_mode"
11083 static void setSetupInfoToDefaults(struct SetupInfo *si)
11087 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11089 si->multiple_users = TRUE;
11092 si->sound_loops = TRUE;
11093 si->sound_music = TRUE;
11094 si->sound_simple = TRUE;
11096 si->global_animations = TRUE;
11097 si->scroll_delay = TRUE;
11098 si->forced_scroll_delay = FALSE;
11099 si->scroll_delay_value = STD_SCROLL_DELAY;
11100 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11101 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11102 si->fade_screens = TRUE;
11103 si->autorecord = TRUE;
11104 si->autorecord_after_replay = TRUE;
11105 si->auto_pause_on_start = FALSE;
11106 si->show_titlescreen = TRUE;
11107 si->quick_doors = FALSE;
11108 si->team_mode = FALSE;
11109 si->handicap = TRUE;
11110 si->skip_levels = TRUE;
11111 si->increment_levels = TRUE;
11112 si->auto_play_next_level = TRUE;
11113 si->count_score_after_game = TRUE;
11114 si->show_scores_after_game = TRUE;
11115 si->time_limit = TRUE;
11116 si->fullscreen = FALSE;
11117 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11118 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11119 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11120 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11121 si->ask_on_escape = TRUE;
11122 si->ask_on_escape_editor = TRUE;
11123 si->ask_on_game_over = TRUE;
11124 si->ask_on_quit_game = TRUE;
11125 si->ask_on_quit_program = TRUE;
11126 si->quick_switch = FALSE;
11127 si->input_on_focus = FALSE;
11128 si->prefer_aga_graphics = TRUE;
11129 si->prefer_lowpass_sounds = FALSE;
11130 si->prefer_extra_panel_items = TRUE;
11131 si->game_speed_extended = FALSE;
11132 si->game_frame_delay = GAME_FRAME_DELAY;
11133 si->bd_skip_uncovering = FALSE;
11134 si->bd_skip_hatching = FALSE;
11135 si->bd_scroll_delay = TRUE;
11136 si->bd_smooth_movements = AUTO;
11137 si->sp_show_border_elements = FALSE;
11138 si->small_game_graphics = FALSE;
11139 si->show_load_save_buttons = FALSE;
11140 si->show_undo_redo_buttons = FALSE;
11141 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11143 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11144 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11145 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11147 si->override_level_graphics = FALSE;
11148 si->override_level_sounds = FALSE;
11149 si->override_level_music = FALSE;
11151 si->volume_simple = 100; // percent
11152 si->volume_loops = 100; // percent
11153 si->volume_music = 100; // percent
11155 si->network_mode = FALSE;
11156 si->network_player_nr = 0; // first player
11157 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11159 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11160 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11161 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11162 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11163 si->touch.draw_outlined = TRUE;
11164 si->touch.draw_pressed = TRUE;
11166 for (i = 0; i < 2; i++)
11168 char *default_grid_button[6][2] =
11174 { "111222", " vv " },
11175 { "111222", " vv " }
11177 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11178 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11179 int min_xsize = MIN(6, grid_xsize);
11180 int min_ysize = MIN(6, grid_ysize);
11181 int startx = grid_xsize - min_xsize;
11182 int starty = grid_ysize - min_ysize;
11185 // virtual buttons grid can only be set to defaults if video is initialized
11186 // (this will be repeated if virtual buttons are not loaded from setup file)
11187 if (video.initialized)
11189 si->touch.grid_xsize[i] = grid_xsize;
11190 si->touch.grid_ysize[i] = grid_ysize;
11194 si->touch.grid_xsize[i] = -1;
11195 si->touch.grid_ysize[i] = -1;
11198 for (x = 0; x < MAX_GRID_XSIZE; x++)
11199 for (y = 0; y < MAX_GRID_YSIZE; y++)
11200 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11202 for (x = 0; x < min_xsize; x++)
11203 for (y = 0; y < min_ysize; y++)
11204 si->touch.grid_button[i][x][starty + y] =
11205 default_grid_button[y][0][x];
11207 for (x = 0; x < min_xsize; x++)
11208 for (y = 0; y < min_ysize; y++)
11209 si->touch.grid_button[i][startx + x][starty + y] =
11210 default_grid_button[y][1][x];
11213 si->touch.grid_initialized = video.initialized;
11215 si->touch.overlay_buttons = FALSE;
11217 si->editor.el_boulderdash = TRUE;
11218 si->editor.el_boulderdash_native = TRUE;
11219 si->editor.el_emerald_mine = TRUE;
11220 si->editor.el_emerald_mine_club = TRUE;
11221 si->editor.el_more = TRUE;
11222 si->editor.el_sokoban = TRUE;
11223 si->editor.el_supaplex = TRUE;
11224 si->editor.el_diamond_caves = TRUE;
11225 si->editor.el_dx_boulderdash = TRUE;
11227 si->editor.el_mirror_magic = TRUE;
11228 si->editor.el_deflektor = TRUE;
11230 si->editor.el_chars = TRUE;
11231 si->editor.el_steel_chars = TRUE;
11233 si->editor.el_classic = TRUE;
11234 si->editor.el_custom = TRUE;
11236 si->editor.el_user_defined = FALSE;
11237 si->editor.el_dynamic = TRUE;
11239 si->editor.el_headlines = TRUE;
11241 si->editor.show_element_token = FALSE;
11243 si->editor.show_read_only_warning = TRUE;
11245 si->editor.use_template_for_new_levels = TRUE;
11247 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11248 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11249 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11250 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11251 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11253 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11254 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11255 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11256 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11257 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11259 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11260 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11261 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11262 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11263 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11264 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11266 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11267 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11268 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11270 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11271 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11272 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11273 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11275 for (i = 0; i < MAX_PLAYERS; i++)
11277 si->input[i].use_joystick = FALSE;
11278 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11279 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11280 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11281 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11282 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11283 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11284 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11285 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11286 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11287 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11288 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11289 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11290 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11291 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11292 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11295 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11296 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11297 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11298 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11300 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11301 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11302 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11303 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11304 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11305 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11306 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11308 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11310 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11311 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11312 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11314 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11315 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11316 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11318 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11319 si->internal.choose_from_top_leveldir = FALSE;
11320 si->internal.show_scaling_in_title = TRUE;
11321 si->internal.create_user_levelset = TRUE;
11322 si->internal.info_screens_from_main = FALSE;
11324 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11325 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11327 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11328 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11329 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11330 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11331 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11332 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11333 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11334 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11335 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11336 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11338 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11339 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11340 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11341 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11342 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11343 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11344 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11345 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11346 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11347 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11349 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11350 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11352 si->debug.show_frames_per_second = FALSE;
11354 si->debug.xsn_mode = AUTO;
11355 si->debug.xsn_percent = 0;
11357 si->options.verbose = FALSE;
11358 si->options.debug = FALSE;
11359 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11361 #if defined(PLATFORM_ANDROID)
11362 si->fullscreen = TRUE;
11363 si->touch.overlay_buttons = TRUE;
11366 setHideSetupEntry(&setup.debug.xsn_mode);
11369 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11371 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11374 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11376 si->player_uuid = NULL; // (will be set later)
11377 si->player_version = 1; // (will be set later)
11379 si->use_api_server = TRUE;
11380 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11381 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11382 si->ask_for_uploading_tapes = TRUE;
11383 si->ask_for_remaining_tapes = FALSE;
11384 si->provide_uploading_tapes = TRUE;
11385 si->ask_for_using_api_server = TRUE;
11386 si->has_remaining_tapes = FALSE;
11389 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11391 si->editor_cascade.el_bd = TRUE;
11392 si->editor_cascade.el_bd_native = TRUE;
11393 si->editor_cascade.el_em = TRUE;
11394 si->editor_cascade.el_emc = TRUE;
11395 si->editor_cascade.el_rnd = TRUE;
11396 si->editor_cascade.el_sb = TRUE;
11397 si->editor_cascade.el_sp = TRUE;
11398 si->editor_cascade.el_dc = TRUE;
11399 si->editor_cascade.el_dx = TRUE;
11401 si->editor_cascade.el_mm = TRUE;
11402 si->editor_cascade.el_df = TRUE;
11404 si->editor_cascade.el_chars = FALSE;
11405 si->editor_cascade.el_steel_chars = FALSE;
11406 si->editor_cascade.el_ce = FALSE;
11407 si->editor_cascade.el_ge = FALSE;
11408 si->editor_cascade.el_es = FALSE;
11409 si->editor_cascade.el_ref = FALSE;
11410 si->editor_cascade.el_user = FALSE;
11411 si->editor_cascade.el_dynamic = FALSE;
11414 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11416 static char *getHideSetupToken(void *setup_value)
11418 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11420 if (setup_value != NULL)
11421 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11423 return hide_setup_token;
11426 void setHideSetupEntry(void *setup_value)
11428 char *hide_setup_token = getHideSetupToken(setup_value);
11430 if (hide_setup_hash == NULL)
11431 hide_setup_hash = newSetupFileHash();
11433 if (setup_value != NULL)
11434 setHashEntry(hide_setup_hash, hide_setup_token, "");
11437 void removeHideSetupEntry(void *setup_value)
11439 char *hide_setup_token = getHideSetupToken(setup_value);
11441 if (setup_value != NULL)
11442 removeHashEntry(hide_setup_hash, hide_setup_token);
11445 boolean hideSetupEntry(void *setup_value)
11447 char *hide_setup_token = getHideSetupToken(setup_value);
11449 return (setup_value != NULL &&
11450 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11453 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11454 struct TokenInfo *token_info,
11455 int token_nr, char *token_text)
11457 char *token_hide_text = getStringCat2(token_text, ".hide");
11458 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11460 // set the value of this setup option in the setup option structure
11461 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11463 // check if this setup option should be hidden in the setup menu
11464 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11465 setHideSetupEntry(token_info[token_nr].value);
11467 free(token_hide_text);
11470 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11471 struct TokenInfo *token_info,
11474 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11475 token_info[token_nr].text);
11478 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11482 if (!setup_file_hash)
11485 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11486 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11488 setup.touch.grid_initialized = TRUE;
11489 for (i = 0; i < 2; i++)
11491 int grid_xsize = setup.touch.grid_xsize[i];
11492 int grid_ysize = setup.touch.grid_ysize[i];
11495 // if virtual buttons are not loaded from setup file, repeat initializing
11496 // virtual buttons grid with default values later when video is initialized
11497 if (grid_xsize == -1 ||
11500 setup.touch.grid_initialized = FALSE;
11505 for (y = 0; y < grid_ysize; y++)
11507 char token_string[MAX_LINE_LEN];
11509 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11511 char *value_string = getHashEntry(setup_file_hash, token_string);
11513 if (value_string == NULL)
11516 for (x = 0; x < grid_xsize; x++)
11518 char c = value_string[x];
11520 setup.touch.grid_button[i][x][y] =
11521 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11526 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11527 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11529 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11530 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11532 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11536 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11538 setup_input = setup.input[pnr];
11539 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11541 char full_token[100];
11543 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11544 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11547 setup.input[pnr] = setup_input;
11550 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11551 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11553 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11554 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11556 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11557 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11559 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11560 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11562 setHideRelatedSetupEntries();
11565 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11569 if (!setup_file_hash)
11572 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11573 setSetupInfo(auto_setup_tokens, i,
11574 getHashEntry(setup_file_hash,
11575 auto_setup_tokens[i].text));
11578 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11582 if (!setup_file_hash)
11585 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11586 setSetupInfo(server_setup_tokens, i,
11587 getHashEntry(setup_file_hash,
11588 server_setup_tokens[i].text));
11591 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11595 if (!setup_file_hash)
11598 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11599 setSetupInfo(editor_cascade_setup_tokens, i,
11600 getHashEntry(setup_file_hash,
11601 editor_cascade_setup_tokens[i].text));
11604 void LoadUserNames(void)
11606 int last_user_nr = user.nr;
11609 if (global.user_names != NULL)
11611 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11612 checked_free(global.user_names[i]);
11614 checked_free(global.user_names);
11617 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11619 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11623 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11625 if (setup_file_hash)
11627 char *player_name = getHashEntry(setup_file_hash, "player_name");
11629 global.user_names[i] = getFixedUserName(player_name);
11631 freeSetupFileHash(setup_file_hash);
11634 if (global.user_names[i] == NULL)
11635 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11638 user.nr = last_user_nr;
11641 void LoadSetupFromFilename(char *filename)
11643 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11645 if (setup_file_hash)
11647 decodeSetupFileHash_Default(setup_file_hash);
11649 freeSetupFileHash(setup_file_hash);
11653 Debug("setup", "using default setup values");
11657 static void LoadSetup_SpecialPostProcessing(void)
11659 char *player_name_new;
11661 // needed to work around problems with fixed length strings
11662 player_name_new = getFixedUserName(setup.player_name);
11663 free(setup.player_name);
11664 setup.player_name = player_name_new;
11666 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11667 if (setup.scroll_delay == FALSE)
11669 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11670 setup.scroll_delay = TRUE; // now always "on"
11673 // make sure that scroll delay value stays inside valid range
11674 setup.scroll_delay_value =
11675 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11678 void LoadSetup_Default(void)
11682 // always start with reliable default values
11683 setSetupInfoToDefaults(&setup);
11685 // try to load setup values from default setup file
11686 filename = getDefaultSetupFilename();
11688 if (fileExists(filename))
11689 LoadSetupFromFilename(filename);
11691 // try to load setup values from platform setup file
11692 filename = getPlatformSetupFilename();
11694 if (fileExists(filename))
11695 LoadSetupFromFilename(filename);
11697 // try to load setup values from user setup file
11698 filename = getSetupFilename();
11700 LoadSetupFromFilename(filename);
11702 LoadSetup_SpecialPostProcessing();
11705 void LoadSetup_AutoSetup(void)
11707 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11708 SetupFileHash *setup_file_hash = NULL;
11710 // always start with reliable default values
11711 setSetupInfoToDefaults_AutoSetup(&setup);
11713 setup_file_hash = loadSetupFileHash(filename);
11715 if (setup_file_hash)
11717 decodeSetupFileHash_AutoSetup(setup_file_hash);
11719 freeSetupFileHash(setup_file_hash);
11725 void LoadSetup_ServerSetup(void)
11727 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11728 SetupFileHash *setup_file_hash = NULL;
11730 // always start with reliable default values
11731 setSetupInfoToDefaults_ServerSetup(&setup);
11733 setup_file_hash = loadSetupFileHash(filename);
11735 if (setup_file_hash)
11737 decodeSetupFileHash_ServerSetup(setup_file_hash);
11739 freeSetupFileHash(setup_file_hash);
11744 if (setup.player_uuid == NULL)
11746 // player UUID does not yet exist in setup file
11747 setup.player_uuid = getStringCopy(getUUID());
11748 setup.player_version = 2;
11750 SaveSetup_ServerSetup();
11754 void LoadSetup_EditorCascade(void)
11756 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11757 SetupFileHash *setup_file_hash = NULL;
11759 // always start with reliable default values
11760 setSetupInfoToDefaults_EditorCascade(&setup);
11762 setup_file_hash = loadSetupFileHash(filename);
11764 if (setup_file_hash)
11766 decodeSetupFileHash_EditorCascade(setup_file_hash);
11768 freeSetupFileHash(setup_file_hash);
11774 void LoadSetup(void)
11776 LoadSetup_Default();
11777 LoadSetup_AutoSetup();
11778 LoadSetup_ServerSetup();
11779 LoadSetup_EditorCascade();
11782 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11783 char *mapping_line)
11785 char mapping_guid[MAX_LINE_LEN];
11786 char *mapping_start, *mapping_end;
11788 // get GUID from game controller mapping line: copy complete line
11789 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11790 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11792 // get GUID from game controller mapping line: cut after GUID part
11793 mapping_start = strchr(mapping_guid, ',');
11794 if (mapping_start != NULL)
11795 *mapping_start = '\0';
11797 // cut newline from game controller mapping line
11798 mapping_end = strchr(mapping_line, '\n');
11799 if (mapping_end != NULL)
11800 *mapping_end = '\0';
11802 // add mapping entry to game controller mappings hash
11803 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11806 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11811 if (!(file = fopen(filename, MODE_READ)))
11813 Warn("cannot read game controller mappings file '%s'", filename);
11818 while (!feof(file))
11820 char line[MAX_LINE_LEN];
11822 if (!fgets(line, MAX_LINE_LEN, file))
11825 addGameControllerMappingToHash(mappings_hash, line);
11831 void SaveSetup_Default(void)
11833 char *filename = getSetupFilename();
11837 InitUserDataDirectory();
11839 if (!(file = fopen(filename, MODE_WRITE)))
11841 Warn("cannot write setup file '%s'", filename);
11846 fprintFileHeader(file, SETUP_FILENAME);
11848 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11850 // just to make things nicer :)
11851 if (global_setup_tokens[i].value == &setup.multiple_users ||
11852 global_setup_tokens[i].value == &setup.sound ||
11853 global_setup_tokens[i].value == &setup.graphics_set ||
11854 global_setup_tokens[i].value == &setup.volume_simple ||
11855 global_setup_tokens[i].value == &setup.network_mode ||
11856 global_setup_tokens[i].value == &setup.touch.control_type ||
11857 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11858 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11859 fprintf(file, "\n");
11861 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11864 for (i = 0; i < 2; i++)
11866 int grid_xsize = setup.touch.grid_xsize[i];
11867 int grid_ysize = setup.touch.grid_ysize[i];
11870 fprintf(file, "\n");
11872 for (y = 0; y < grid_ysize; y++)
11874 char token_string[MAX_LINE_LEN];
11875 char value_string[MAX_LINE_LEN];
11877 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11879 for (x = 0; x < grid_xsize; x++)
11881 char c = setup.touch.grid_button[i][x][y];
11883 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11886 value_string[grid_xsize] = '\0';
11888 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11892 fprintf(file, "\n");
11893 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11894 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11896 fprintf(file, "\n");
11897 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11898 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11900 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11904 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11905 fprintf(file, "\n");
11907 setup_input = setup.input[pnr];
11908 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11909 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11912 fprintf(file, "\n");
11913 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11914 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11916 // (internal setup values not saved to user setup file)
11918 fprintf(file, "\n");
11919 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11920 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11921 setup.debug.xsn_mode != AUTO)
11922 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11924 fprintf(file, "\n");
11925 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11926 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11930 SetFilePermissions(filename, PERMS_PRIVATE);
11933 void SaveSetup_AutoSetup(void)
11935 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11939 InitUserDataDirectory();
11941 if (!(file = fopen(filename, MODE_WRITE)))
11943 Warn("cannot write auto setup file '%s'", filename);
11950 fprintFileHeader(file, AUTOSETUP_FILENAME);
11952 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11953 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11957 SetFilePermissions(filename, PERMS_PRIVATE);
11962 void SaveSetup_ServerSetup(void)
11964 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11968 InitUserDataDirectory();
11970 if (!(file = fopen(filename, MODE_WRITE)))
11972 Warn("cannot write server setup file '%s'", filename);
11979 fprintFileHeader(file, SERVERSETUP_FILENAME);
11981 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11983 // just to make things nicer :)
11984 if (server_setup_tokens[i].value == &setup.use_api_server)
11985 fprintf(file, "\n");
11987 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11992 SetFilePermissions(filename, PERMS_PRIVATE);
11997 void SaveSetup_EditorCascade(void)
11999 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12003 InitUserDataDirectory();
12005 if (!(file = fopen(filename, MODE_WRITE)))
12007 Warn("cannot write editor cascade state file '%s'", filename);
12014 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12016 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12017 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12021 SetFilePermissions(filename, PERMS_PRIVATE);
12026 void SaveSetup(void)
12028 SaveSetup_Default();
12029 SaveSetup_AutoSetup();
12030 SaveSetup_ServerSetup();
12031 SaveSetup_EditorCascade();
12034 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12039 if (!(file = fopen(filename, MODE_WRITE)))
12041 Warn("cannot write game controller mappings file '%s'", filename);
12046 BEGIN_HASH_ITERATION(mappings_hash, itr)
12048 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12050 END_HASH_ITERATION(mappings_hash, itr)
12055 void SaveSetup_AddGameControllerMapping(char *mapping)
12057 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12058 SetupFileHash *mappings_hash = newSetupFileHash();
12060 InitUserDataDirectory();
12062 // load existing personal game controller mappings
12063 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12065 // add new mapping to personal game controller mappings
12066 addGameControllerMappingToHash(mappings_hash, mapping);
12068 // save updated personal game controller mappings
12069 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12071 freeSetupFileHash(mappings_hash);
12075 void LoadCustomElementDescriptions(void)
12077 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12078 SetupFileHash *setup_file_hash;
12081 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12083 if (element_info[i].custom_description != NULL)
12085 free(element_info[i].custom_description);
12086 element_info[i].custom_description = NULL;
12090 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12093 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12095 char *token = getStringCat2(element_info[i].token_name, ".name");
12096 char *value = getHashEntry(setup_file_hash, token);
12099 element_info[i].custom_description = getStringCopy(value);
12104 freeSetupFileHash(setup_file_hash);
12107 static int getElementFromToken(char *token)
12109 char *value = getHashEntry(element_token_hash, token);
12112 return atoi(value);
12114 Warn("unknown element token '%s'", token);
12116 return EL_UNDEFINED;
12119 void FreeGlobalAnimEventInfo(void)
12121 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12123 if (gaei->event_list == NULL)
12128 for (i = 0; i < gaei->num_event_lists; i++)
12130 checked_free(gaei->event_list[i]->event_value);
12131 checked_free(gaei->event_list[i]);
12134 checked_free(gaei->event_list);
12136 gaei->event_list = NULL;
12137 gaei->num_event_lists = 0;
12140 static int AddGlobalAnimEventList(void)
12142 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12143 int list_pos = gaei->num_event_lists++;
12145 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12146 sizeof(struct GlobalAnimEventListInfo *));
12148 gaei->event_list[list_pos] =
12149 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12151 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12153 gaeli->event_value = NULL;
12154 gaeli->num_event_values = 0;
12159 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12161 // do not add empty global animation events
12162 if (event_value == ANIM_EVENT_NONE)
12165 // if list position is undefined, create new list
12166 if (list_pos == ANIM_EVENT_UNDEFINED)
12167 list_pos = AddGlobalAnimEventList();
12169 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12170 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12171 int value_pos = gaeli->num_event_values++;
12173 gaeli->event_value = checked_realloc(gaeli->event_value,
12174 gaeli->num_event_values * sizeof(int *));
12176 gaeli->event_value[value_pos] = event_value;
12181 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12183 if (list_pos == ANIM_EVENT_UNDEFINED)
12184 return ANIM_EVENT_NONE;
12186 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12187 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12189 return gaeli->event_value[value_pos];
12192 int GetGlobalAnimEventValueCount(int list_pos)
12194 if (list_pos == ANIM_EVENT_UNDEFINED)
12197 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12198 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12200 return gaeli->num_event_values;
12203 // This function checks if a string <s> of the format "string1, string2, ..."
12204 // exactly contains a string <s_contained>.
12206 static boolean string_has_parameter(char *s, char *s_contained)
12210 if (s == NULL || s_contained == NULL)
12213 if (strlen(s_contained) > strlen(s))
12216 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12218 char next_char = s[strlen(s_contained)];
12220 // check if next character is delimiter or whitespace
12221 if (next_char == ',' || next_char == '\0' ||
12222 next_char == ' ' || next_char == '\t')
12226 // check if string contains another parameter string after a comma
12227 substring = strchr(s, ',');
12228 if (substring == NULL) // string does not contain a comma
12231 // advance string pointer to next character after the comma
12234 // skip potential whitespaces after the comma
12235 while (*substring == ' ' || *substring == '\t')
12238 return string_has_parameter(substring, s_contained);
12241 static int get_anim_parameter_value_ce(char *s)
12244 char *pattern_1 = "ce_change:custom_";
12245 char *pattern_2 = ".page_";
12246 int pattern_1_len = strlen(pattern_1);
12247 char *matching_char = strstr(s_ptr, pattern_1);
12248 int result = ANIM_EVENT_NONE;
12250 if (matching_char == NULL)
12251 return ANIM_EVENT_NONE;
12253 result = ANIM_EVENT_CE_CHANGE;
12255 s_ptr = matching_char + pattern_1_len;
12257 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12258 if (*s_ptr >= '0' && *s_ptr <= '9')
12260 int gic_ce_nr = (*s_ptr++ - '0');
12262 if (*s_ptr >= '0' && *s_ptr <= '9')
12264 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12266 if (*s_ptr >= '0' && *s_ptr <= '9')
12267 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12270 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12271 return ANIM_EVENT_NONE;
12273 // custom element stored as 0 to 255
12276 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12280 // invalid custom element number specified
12282 return ANIM_EVENT_NONE;
12285 // check for change page number ("page_X" or "page_XX") (optional)
12286 if (strPrefix(s_ptr, pattern_2))
12288 s_ptr += strlen(pattern_2);
12290 if (*s_ptr >= '0' && *s_ptr <= '9')
12292 int gic_page_nr = (*s_ptr++ - '0');
12294 if (*s_ptr >= '0' && *s_ptr <= '9')
12295 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12297 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12298 return ANIM_EVENT_NONE;
12300 // change page stored as 1 to 32 (0 means "all change pages")
12302 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12306 // invalid animation part number specified
12308 return ANIM_EVENT_NONE;
12312 // discard result if next character is neither delimiter nor whitespace
12313 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12314 *s_ptr == ' ' || *s_ptr == '\t'))
12315 return ANIM_EVENT_NONE;
12320 static int get_anim_parameter_value(char *s)
12322 int event_value[] =
12330 char *pattern_1[] =
12338 char *pattern_2 = ".part_";
12339 char *matching_char = NULL;
12341 int pattern_1_len = 0;
12342 int result = ANIM_EVENT_NONE;
12345 result = get_anim_parameter_value_ce(s);
12347 if (result != ANIM_EVENT_NONE)
12350 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12352 matching_char = strstr(s_ptr, pattern_1[i]);
12353 pattern_1_len = strlen(pattern_1[i]);
12354 result = event_value[i];
12356 if (matching_char != NULL)
12360 if (matching_char == NULL)
12361 return ANIM_EVENT_NONE;
12363 s_ptr = matching_char + pattern_1_len;
12365 // check for main animation number ("anim_X" or "anim_XX")
12366 if (*s_ptr >= '0' && *s_ptr <= '9')
12368 int gic_anim_nr = (*s_ptr++ - '0');
12370 if (*s_ptr >= '0' && *s_ptr <= '9')
12371 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12373 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12374 return ANIM_EVENT_NONE;
12376 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12380 // invalid main animation number specified
12382 return ANIM_EVENT_NONE;
12385 // check for animation part number ("part_X" or "part_XX") (optional)
12386 if (strPrefix(s_ptr, pattern_2))
12388 s_ptr += strlen(pattern_2);
12390 if (*s_ptr >= '0' && *s_ptr <= '9')
12392 int gic_part_nr = (*s_ptr++ - '0');
12394 if (*s_ptr >= '0' && *s_ptr <= '9')
12395 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12397 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12398 return ANIM_EVENT_NONE;
12400 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12404 // invalid animation part number specified
12406 return ANIM_EVENT_NONE;
12410 // discard result if next character is neither delimiter nor whitespace
12411 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12412 *s_ptr == ' ' || *s_ptr == '\t'))
12413 return ANIM_EVENT_NONE;
12418 static int get_anim_parameter_values(char *s)
12420 int list_pos = ANIM_EVENT_UNDEFINED;
12421 int event_value = ANIM_EVENT_DEFAULT;
12423 if (string_has_parameter(s, "any"))
12424 event_value |= ANIM_EVENT_ANY;
12426 if (string_has_parameter(s, "click:self") ||
12427 string_has_parameter(s, "click") ||
12428 string_has_parameter(s, "self"))
12429 event_value |= ANIM_EVENT_SELF;
12431 if (string_has_parameter(s, "unclick:any"))
12432 event_value |= ANIM_EVENT_UNCLICK_ANY;
12434 // if animation event found, add it to global animation event list
12435 if (event_value != ANIM_EVENT_NONE)
12436 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12440 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12441 event_value = get_anim_parameter_value(s);
12443 // if animation event found, add it to global animation event list
12444 if (event_value != ANIM_EVENT_NONE)
12445 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12447 // continue with next part of the string, starting with next comma
12448 s = strchr(s + 1, ',');
12454 static int get_anim_action_parameter_value(char *token)
12456 // check most common default case first to massively speed things up
12457 if (strEqual(token, ARG_UNDEFINED))
12458 return ANIM_EVENT_ACTION_NONE;
12460 int result = getImageIDFromToken(token);
12464 char *gfx_token = getStringCat2("gfx.", token);
12466 result = getImageIDFromToken(gfx_token);
12468 checked_free(gfx_token);
12473 Key key = getKeyFromX11KeyName(token);
12475 if (key != KSYM_UNDEFINED)
12476 result = -(int)key;
12483 result = get_hash_from_string(token); // unsigned int => int
12484 result = ABS(result); // may be negative now
12485 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12487 setHashEntry(anim_url_hash, int2str(result, 0), token);
12492 result = ANIM_EVENT_ACTION_NONE;
12497 int get_parameter_value(char *value_raw, char *suffix, int type)
12499 char *value = getStringToLower(value_raw);
12500 int result = 0; // probably a save default value
12502 if (strEqual(suffix, ".direction"))
12504 result = (strEqual(value, "left") ? MV_LEFT :
12505 strEqual(value, "right") ? MV_RIGHT :
12506 strEqual(value, "up") ? MV_UP :
12507 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12509 else if (strEqual(suffix, ".position"))
12511 result = (strEqual(value, "left") ? POS_LEFT :
12512 strEqual(value, "right") ? POS_RIGHT :
12513 strEqual(value, "top") ? POS_TOP :
12514 strEqual(value, "upper") ? POS_UPPER :
12515 strEqual(value, "middle") ? POS_MIDDLE :
12516 strEqual(value, "lower") ? POS_LOWER :
12517 strEqual(value, "bottom") ? POS_BOTTOM :
12518 strEqual(value, "any") ? POS_ANY :
12519 strEqual(value, "ce") ? POS_CE :
12520 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12521 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12523 else if (strEqual(suffix, ".align"))
12525 result = (strEqual(value, "left") ? ALIGN_LEFT :
12526 strEqual(value, "right") ? ALIGN_RIGHT :
12527 strEqual(value, "center") ? ALIGN_CENTER :
12528 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12530 else if (strEqual(suffix, ".valign"))
12532 result = (strEqual(value, "top") ? VALIGN_TOP :
12533 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12534 strEqual(value, "middle") ? VALIGN_MIDDLE :
12535 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12537 else if (strEqual(suffix, ".anim_mode"))
12539 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12540 string_has_parameter(value, "loop") ? ANIM_LOOP :
12541 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12542 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12543 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12544 string_has_parameter(value, "random") ? ANIM_RANDOM :
12545 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12546 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12547 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12548 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12549 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12550 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12551 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12552 string_has_parameter(value, "all") ? ANIM_ALL :
12553 string_has_parameter(value, "tiled") ? ANIM_TILED :
12554 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12557 if (string_has_parameter(value, "once"))
12558 result |= ANIM_ONCE;
12560 if (string_has_parameter(value, "reverse"))
12561 result |= ANIM_REVERSE;
12563 if (string_has_parameter(value, "opaque_player"))
12564 result |= ANIM_OPAQUE_PLAYER;
12566 if (string_has_parameter(value, "static_panel"))
12567 result |= ANIM_STATIC_PANEL;
12569 else if (strEqual(suffix, ".init_event") ||
12570 strEqual(suffix, ".anim_event"))
12572 result = get_anim_parameter_values(value);
12574 else if (strEqual(suffix, ".init_delay_action") ||
12575 strEqual(suffix, ".anim_delay_action") ||
12576 strEqual(suffix, ".post_delay_action") ||
12577 strEqual(suffix, ".init_event_action") ||
12578 strEqual(suffix, ".anim_event_action"))
12580 result = get_anim_action_parameter_value(value_raw);
12582 else if (strEqual(suffix, ".class"))
12584 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12585 get_hash_from_string(value));
12587 else if (strEqual(suffix, ".style"))
12589 result = STYLE_DEFAULT;
12591 if (string_has_parameter(value, "accurate_borders"))
12592 result |= STYLE_ACCURATE_BORDERS;
12594 if (string_has_parameter(value, "inner_corners"))
12595 result |= STYLE_INNER_CORNERS;
12597 if (string_has_parameter(value, "reverse"))
12598 result |= STYLE_REVERSE;
12600 if (string_has_parameter(value, "leftmost_position"))
12601 result |= STYLE_LEFTMOST_POSITION;
12603 if (string_has_parameter(value, "block_clicks"))
12604 result |= STYLE_BLOCK;
12606 if (string_has_parameter(value, "passthrough_clicks"))
12607 result |= STYLE_PASSTHROUGH;
12609 if (string_has_parameter(value, "multiple_actions"))
12610 result |= STYLE_MULTIPLE_ACTIONS;
12612 if (string_has_parameter(value, "consume_ce_event"))
12613 result |= STYLE_CONSUME_CE_EVENT;
12615 else if (strEqual(suffix, ".fade_mode"))
12617 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12618 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12619 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12620 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12621 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12622 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12623 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12624 FADE_MODE_DEFAULT);
12626 else if (strEqual(suffix, ".auto_delay_unit"))
12628 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12629 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12630 AUTO_DELAY_UNIT_DEFAULT);
12632 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12634 result = gfx.get_font_from_token_function(value);
12636 else // generic parameter of type integer or boolean
12638 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12639 type == TYPE_INTEGER ? get_integer_from_string(value) :
12640 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12641 ARG_UNDEFINED_VALUE);
12649 static int get_token_parameter_value(char *token, char *value_raw)
12653 if (token == NULL || value_raw == NULL)
12654 return ARG_UNDEFINED_VALUE;
12656 suffix = strrchr(token, '.');
12657 if (suffix == NULL)
12660 if (strEqual(suffix, ".element"))
12661 return getElementFromToken(value_raw);
12663 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12664 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12667 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12668 boolean ignore_defaults)
12672 for (i = 0; image_config_vars[i].token != NULL; i++)
12674 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12676 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12677 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12681 *image_config_vars[i].value =
12682 get_token_parameter_value(image_config_vars[i].token, value);
12686 void InitMenuDesignSettings_Static(void)
12688 // always start with reliable default values from static default config
12689 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12692 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12696 // the following initializes hierarchical values from static configuration
12698 // special case: initialize "ARG_DEFAULT" values in static default config
12699 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12700 titlescreen_initial_first_default.fade_mode =
12701 title_initial_first_default.fade_mode;
12702 titlescreen_initial_first_default.fade_delay =
12703 title_initial_first_default.fade_delay;
12704 titlescreen_initial_first_default.post_delay =
12705 title_initial_first_default.post_delay;
12706 titlescreen_initial_first_default.auto_delay =
12707 title_initial_first_default.auto_delay;
12708 titlescreen_initial_first_default.auto_delay_unit =
12709 title_initial_first_default.auto_delay_unit;
12710 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12711 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12712 titlescreen_first_default.post_delay = title_first_default.post_delay;
12713 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12714 titlescreen_first_default.auto_delay_unit =
12715 title_first_default.auto_delay_unit;
12716 titlemessage_initial_first_default.fade_mode =
12717 title_initial_first_default.fade_mode;
12718 titlemessage_initial_first_default.fade_delay =
12719 title_initial_first_default.fade_delay;
12720 titlemessage_initial_first_default.post_delay =
12721 title_initial_first_default.post_delay;
12722 titlemessage_initial_first_default.auto_delay =
12723 title_initial_first_default.auto_delay;
12724 titlemessage_initial_first_default.auto_delay_unit =
12725 title_initial_first_default.auto_delay_unit;
12726 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12727 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12728 titlemessage_first_default.post_delay = title_first_default.post_delay;
12729 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12730 titlemessage_first_default.auto_delay_unit =
12731 title_first_default.auto_delay_unit;
12733 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12734 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12735 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12736 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12737 titlescreen_initial_default.auto_delay_unit =
12738 title_initial_default.auto_delay_unit;
12739 titlescreen_default.fade_mode = title_default.fade_mode;
12740 titlescreen_default.fade_delay = title_default.fade_delay;
12741 titlescreen_default.post_delay = title_default.post_delay;
12742 titlescreen_default.auto_delay = title_default.auto_delay;
12743 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12744 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12745 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12746 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12747 titlemessage_initial_default.auto_delay_unit =
12748 title_initial_default.auto_delay_unit;
12749 titlemessage_default.fade_mode = title_default.fade_mode;
12750 titlemessage_default.fade_delay = title_default.fade_delay;
12751 titlemessage_default.post_delay = title_default.post_delay;
12752 titlemessage_default.auto_delay = title_default.auto_delay;
12753 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12755 // special case: initialize "ARG_DEFAULT" values in static default config
12756 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12757 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12759 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12760 titlescreen_first[i] = titlescreen_first_default;
12761 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12762 titlemessage_first[i] = titlemessage_first_default;
12764 titlescreen_initial[i] = titlescreen_initial_default;
12765 titlescreen[i] = titlescreen_default;
12766 titlemessage_initial[i] = titlemessage_initial_default;
12767 titlemessage[i] = titlemessage_default;
12770 // special case: initialize "ARG_DEFAULT" values in static default config
12771 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12772 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12774 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12777 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12778 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12779 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12782 // special case: initialize "ARG_DEFAULT" values in static default config
12783 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12784 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12786 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12787 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12788 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12790 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12793 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12797 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12801 struct XY *dst, *src;
12803 game_buttons_xy[] =
12805 { &game.button.save, &game.button.stop },
12806 { &game.button.pause2, &game.button.pause },
12807 { &game.button.load, &game.button.play },
12808 { &game.button.undo, &game.button.stop },
12809 { &game.button.redo, &game.button.play },
12815 // special case: initialize later added SETUP list size from LEVELS value
12816 if (menu.list_size[GAME_MODE_SETUP] == -1)
12817 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12819 // set default position for snapshot buttons to stop/pause/play buttons
12820 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12821 if ((*game_buttons_xy[i].dst).x == -1 &&
12822 (*game_buttons_xy[i].dst).y == -1)
12823 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12825 // --------------------------------------------------------------------------
12826 // dynamic viewports (including playfield margins, borders and alignments)
12827 // --------------------------------------------------------------------------
12829 // dynamic viewports currently only supported for landscape mode
12830 int display_width = MAX(video.display_width, video.display_height);
12831 int display_height = MIN(video.display_width, video.display_height);
12833 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12835 struct RectWithBorder *vp_window = &viewport.window[i];
12836 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12837 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12838 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12839 boolean dynamic_window_width = (vp_window->min_width != -1);
12840 boolean dynamic_window_height = (vp_window->min_height != -1);
12841 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12842 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12844 // adjust window size if min/max width/height is specified
12846 if (vp_window->min_width != -1)
12848 int window_width = display_width;
12850 // when using static window height, use aspect ratio of display
12851 if (vp_window->min_height == -1)
12852 window_width = vp_window->height * display_width / display_height;
12854 vp_window->width = MAX(vp_window->min_width, window_width);
12857 if (vp_window->min_height != -1)
12859 int window_height = display_height;
12861 // when using static window width, use aspect ratio of display
12862 if (vp_window->min_width == -1)
12863 window_height = vp_window->width * display_height / display_width;
12865 vp_window->height = MAX(vp_window->min_height, window_height);
12868 if (vp_window->max_width != -1)
12869 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12871 if (vp_window->max_height != -1)
12872 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12874 int playfield_width = vp_window->width;
12875 int playfield_height = vp_window->height;
12877 // adjust playfield size and position according to specified margins
12879 playfield_width -= vp_playfield->margin_left;
12880 playfield_width -= vp_playfield->margin_right;
12882 playfield_height -= vp_playfield->margin_top;
12883 playfield_height -= vp_playfield->margin_bottom;
12885 // adjust playfield size if min/max width/height is specified
12887 if (vp_playfield->min_width != -1)
12888 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12890 if (vp_playfield->min_height != -1)
12891 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12893 if (vp_playfield->max_width != -1)
12894 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12896 if (vp_playfield->max_height != -1)
12897 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12899 // adjust playfield position according to specified alignment
12901 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12902 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12903 else if (vp_playfield->align == ALIGN_CENTER)
12904 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12905 else if (vp_playfield->align == ALIGN_RIGHT)
12906 vp_playfield->x += playfield_width - vp_playfield->width;
12908 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12909 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12910 else if (vp_playfield->valign == VALIGN_MIDDLE)
12911 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12912 else if (vp_playfield->valign == VALIGN_BOTTOM)
12913 vp_playfield->y += playfield_height - vp_playfield->height;
12915 vp_playfield->x += vp_playfield->margin_left;
12916 vp_playfield->y += vp_playfield->margin_top;
12918 // adjust individual playfield borders if only default border is specified
12920 if (vp_playfield->border_left == -1)
12921 vp_playfield->border_left = vp_playfield->border_size;
12922 if (vp_playfield->border_right == -1)
12923 vp_playfield->border_right = vp_playfield->border_size;
12924 if (vp_playfield->border_top == -1)
12925 vp_playfield->border_top = vp_playfield->border_size;
12926 if (vp_playfield->border_bottom == -1)
12927 vp_playfield->border_bottom = vp_playfield->border_size;
12929 // set dynamic playfield borders if borders are specified as undefined
12930 // (but only if window size was dynamic and playfield size was static)
12932 if (dynamic_window_width && !dynamic_playfield_width)
12934 if (vp_playfield->border_left == -1)
12936 vp_playfield->border_left = (vp_playfield->x -
12937 vp_playfield->margin_left);
12938 vp_playfield->x -= vp_playfield->border_left;
12939 vp_playfield->width += vp_playfield->border_left;
12942 if (vp_playfield->border_right == -1)
12944 vp_playfield->border_right = (vp_window->width -
12946 vp_playfield->width -
12947 vp_playfield->margin_right);
12948 vp_playfield->width += vp_playfield->border_right;
12952 if (dynamic_window_height && !dynamic_playfield_height)
12954 if (vp_playfield->border_top == -1)
12956 vp_playfield->border_top = (vp_playfield->y -
12957 vp_playfield->margin_top);
12958 vp_playfield->y -= vp_playfield->border_top;
12959 vp_playfield->height += vp_playfield->border_top;
12962 if (vp_playfield->border_bottom == -1)
12964 vp_playfield->border_bottom = (vp_window->height -
12966 vp_playfield->height -
12967 vp_playfield->margin_bottom);
12968 vp_playfield->height += vp_playfield->border_bottom;
12972 // adjust playfield size to be a multiple of a defined alignment tile size
12974 int align_size = vp_playfield->align_size;
12975 int playfield_xtiles = vp_playfield->width / align_size;
12976 int playfield_ytiles = vp_playfield->height / align_size;
12977 int playfield_width_corrected = playfield_xtiles * align_size;
12978 int playfield_height_corrected = playfield_ytiles * align_size;
12979 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12980 i == GFX_SPECIAL_ARG_EDITOR);
12982 if (is_playfield_mode &&
12983 dynamic_playfield_width &&
12984 vp_playfield->width != playfield_width_corrected)
12986 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12988 vp_playfield->width = playfield_width_corrected;
12990 if (vp_playfield->align == ALIGN_LEFT)
12992 vp_playfield->border_left += playfield_xdiff;
12994 else if (vp_playfield->align == ALIGN_RIGHT)
12996 vp_playfield->border_right += playfield_xdiff;
12998 else if (vp_playfield->align == ALIGN_CENTER)
13000 int border_left_diff = playfield_xdiff / 2;
13001 int border_right_diff = playfield_xdiff - border_left_diff;
13003 vp_playfield->border_left += border_left_diff;
13004 vp_playfield->border_right += border_right_diff;
13008 if (is_playfield_mode &&
13009 dynamic_playfield_height &&
13010 vp_playfield->height != playfield_height_corrected)
13012 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13014 vp_playfield->height = playfield_height_corrected;
13016 if (vp_playfield->valign == VALIGN_TOP)
13018 vp_playfield->border_top += playfield_ydiff;
13020 else if (vp_playfield->align == VALIGN_BOTTOM)
13022 vp_playfield->border_right += playfield_ydiff;
13024 else if (vp_playfield->align == VALIGN_MIDDLE)
13026 int border_top_diff = playfield_ydiff / 2;
13027 int border_bottom_diff = playfield_ydiff - border_top_diff;
13029 vp_playfield->border_top += border_top_diff;
13030 vp_playfield->border_bottom += border_bottom_diff;
13034 // adjust door positions according to specified alignment
13036 for (j = 0; j < 2; j++)
13038 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13040 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13041 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13042 else if (vp_door->align == ALIGN_CENTER)
13043 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13044 else if (vp_door->align == ALIGN_RIGHT)
13045 vp_door->x += vp_window->width - vp_door->width;
13047 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13048 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13049 else if (vp_door->valign == VALIGN_MIDDLE)
13050 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13051 else if (vp_door->valign == VALIGN_BOTTOM)
13052 vp_door->y += vp_window->height - vp_door->height;
13057 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13061 struct XYTileSize *dst, *src;
13064 editor_buttons_xy[] =
13067 &editor.button.element_left, &editor.palette.element_left,
13068 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13071 &editor.button.element_middle, &editor.palette.element_middle,
13072 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13075 &editor.button.element_right, &editor.palette.element_right,
13076 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13083 // set default position for element buttons to element graphics
13084 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13086 if ((*editor_buttons_xy[i].dst).x == -1 &&
13087 (*editor_buttons_xy[i].dst).y == -1)
13089 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13091 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13093 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13097 // adjust editor palette rows and columns if specified to be dynamic
13099 if (editor.palette.cols == -1)
13101 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13102 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13103 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13105 editor.palette.cols = (vp_width - sc_width) / bt_width;
13107 if (editor.palette.x == -1)
13109 int palette_width = editor.palette.cols * bt_width + sc_width;
13111 editor.palette.x = (vp_width - palette_width) / 2;
13115 if (editor.palette.rows == -1)
13117 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13118 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13119 int tx_height = getFontHeight(FONT_TEXT_2);
13121 editor.palette.rows = (vp_height - tx_height) / bt_height;
13123 if (editor.palette.y == -1)
13125 int palette_height = editor.palette.rows * bt_height + tx_height;
13127 editor.palette.y = (vp_height - palette_height) / 2;
13132 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13133 boolean initialize)
13135 // special case: check if network and preview player positions are redefined,
13136 // to compare this later against the main menu level preview being redefined
13137 struct TokenIntPtrInfo menu_config_players[] =
13139 { "main.network_players.x", &menu.main.network_players.redefined },
13140 { "main.network_players.y", &menu.main.network_players.redefined },
13141 { "main.preview_players.x", &menu.main.preview_players.redefined },
13142 { "main.preview_players.y", &menu.main.preview_players.redefined },
13143 { "preview.x", &preview.redefined },
13144 { "preview.y", &preview.redefined }
13150 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13151 *menu_config_players[i].value = FALSE;
13155 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13156 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13157 *menu_config_players[i].value = TRUE;
13161 static void InitMenuDesignSettings_PreviewPlayers(void)
13163 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13166 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13168 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13171 static void LoadMenuDesignSettingsFromFilename(char *filename)
13173 static struct TitleFadingInfo tfi;
13174 static struct TitleMessageInfo tmi;
13175 static struct TokenInfo title_tokens[] =
13177 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13178 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13179 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13180 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13181 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13185 static struct TokenInfo titlemessage_tokens[] =
13187 { TYPE_INTEGER, &tmi.x, ".x" },
13188 { TYPE_INTEGER, &tmi.y, ".y" },
13189 { TYPE_INTEGER, &tmi.width, ".width" },
13190 { TYPE_INTEGER, &tmi.height, ".height" },
13191 { TYPE_INTEGER, &tmi.chars, ".chars" },
13192 { TYPE_INTEGER, &tmi.lines, ".lines" },
13193 { TYPE_INTEGER, &tmi.align, ".align" },
13194 { TYPE_INTEGER, &tmi.valign, ".valign" },
13195 { TYPE_INTEGER, &tmi.font, ".font" },
13196 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13197 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13198 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13199 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13200 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13201 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13202 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13203 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13204 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13210 struct TitleFadingInfo *info;
13215 // initialize first titles from "enter screen" definitions, if defined
13216 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13217 { &title_first_default, "menu.enter_screen.TITLE" },
13219 // initialize title screens from "next screen" definitions, if defined
13220 { &title_initial_default, "menu.next_screen.TITLE" },
13221 { &title_default, "menu.next_screen.TITLE" },
13227 struct TitleMessageInfo *array;
13230 titlemessage_arrays[] =
13232 // initialize first titles from "enter screen" definitions, if defined
13233 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13234 { titlescreen_first, "menu.enter_screen.TITLE" },
13235 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13236 { titlemessage_first, "menu.enter_screen.TITLE" },
13238 // initialize titles from "next screen" definitions, if defined
13239 { titlescreen_initial, "menu.next_screen.TITLE" },
13240 { titlescreen, "menu.next_screen.TITLE" },
13241 { titlemessage_initial, "menu.next_screen.TITLE" },
13242 { titlemessage, "menu.next_screen.TITLE" },
13244 // overwrite titles with title definitions, if defined
13245 { titlescreen_initial_first, "[title_initial]" },
13246 { titlescreen_first, "[title]" },
13247 { titlemessage_initial_first, "[title_initial]" },
13248 { titlemessage_first, "[title]" },
13250 { titlescreen_initial, "[title_initial]" },
13251 { titlescreen, "[title]" },
13252 { titlemessage_initial, "[title_initial]" },
13253 { titlemessage, "[title]" },
13255 // overwrite titles with title screen/message definitions, if defined
13256 { titlescreen_initial_first, "[titlescreen_initial]" },
13257 { titlescreen_first, "[titlescreen]" },
13258 { titlemessage_initial_first, "[titlemessage_initial]" },
13259 { titlemessage_first, "[titlemessage]" },
13261 { titlescreen_initial, "[titlescreen_initial]" },
13262 { titlescreen, "[titlescreen]" },
13263 { titlemessage_initial, "[titlemessage_initial]" },
13264 { titlemessage, "[titlemessage]" },
13268 SetupFileHash *setup_file_hash;
13271 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13274 // the following initializes hierarchical values from dynamic configuration
13276 // special case: initialize with default values that may be overwritten
13277 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13278 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13280 struct TokenIntPtrInfo menu_config[] =
13282 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13283 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13284 { "menu.list_size", &menu.list_size[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.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13299 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13301 struct TokenIntPtrInfo menu_config[] =
13303 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13304 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13305 { "menu.list_size.INFO", &menu.list_size_info[i] },
13306 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13307 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13310 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13312 char *token = menu_config[j].token;
13313 char *value = getHashEntry(setup_file_hash, token);
13316 *menu_config[j].value = get_integer_from_string(value);
13320 // special case: initialize with default values that may be overwritten
13321 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13322 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13324 struct TokenIntPtrInfo menu_config[] =
13326 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13327 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13330 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13332 char *token = menu_config[j].token;
13333 char *value = getHashEntry(setup_file_hash, token);
13336 *menu_config[j].value = get_integer_from_string(value);
13340 // special case: initialize with default values that may be overwritten
13341 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13342 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13344 struct TokenIntPtrInfo menu_config[] =
13346 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13347 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13348 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13349 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13350 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13351 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13352 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13353 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13354 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13355 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13358 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13360 char *token = menu_config[j].token;
13361 char *value = getHashEntry(setup_file_hash, token);
13364 *menu_config[j].value = get_integer_from_string(value);
13368 // special case: initialize with default values that may be overwritten
13369 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13370 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13372 struct TokenIntPtrInfo menu_config[] =
13374 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13375 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13376 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13377 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13378 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13379 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13380 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13381 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13382 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13385 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13387 char *token = menu_config[j].token;
13388 char *value = getHashEntry(setup_file_hash, token);
13391 *menu_config[j].value = get_token_parameter_value(token, value);
13395 // special case: initialize with default values that may be overwritten
13396 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13397 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13401 char *token_prefix;
13402 struct RectWithBorder *struct_ptr;
13406 { "viewport.window", &viewport.window[i] },
13407 { "viewport.playfield", &viewport.playfield[i] },
13408 { "viewport.door_1", &viewport.door_1[i] },
13409 { "viewport.door_2", &viewport.door_2[i] }
13412 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13414 struct TokenIntPtrInfo vp_config[] =
13416 { ".x", &vp_struct[j].struct_ptr->x },
13417 { ".y", &vp_struct[j].struct_ptr->y },
13418 { ".width", &vp_struct[j].struct_ptr->width },
13419 { ".height", &vp_struct[j].struct_ptr->height },
13420 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13421 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13422 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13423 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13424 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13425 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13426 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13427 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13428 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13429 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13430 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13431 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13432 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13433 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13434 { ".align", &vp_struct[j].struct_ptr->align },
13435 { ".valign", &vp_struct[j].struct_ptr->valign }
13438 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13440 char *token = getStringCat2(vp_struct[j].token_prefix,
13441 vp_config[k].token);
13442 char *value = getHashEntry(setup_file_hash, token);
13445 *vp_config[k].value = get_token_parameter_value(token, value);
13452 // special case: initialize with default values that may be overwritten
13453 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13454 for (i = 0; title_info[i].info != NULL; i++)
13456 struct TitleFadingInfo *info = title_info[i].info;
13457 char *base_token = title_info[i].text;
13459 for (j = 0; title_tokens[j].type != -1; j++)
13461 char *token = getStringCat2(base_token, title_tokens[j].text);
13462 char *value = getHashEntry(setup_file_hash, token);
13466 int parameter_value = get_token_parameter_value(token, value);
13470 *(int *)title_tokens[j].value = (int)parameter_value;
13479 // special case: initialize with default values that may be overwritten
13480 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13481 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13483 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13484 char *base_token = titlemessage_arrays[i].text;
13486 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13488 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13489 char *value = getHashEntry(setup_file_hash, token);
13493 int parameter_value = get_token_parameter_value(token, value);
13495 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13499 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13500 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13502 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13512 // read (and overwrite with) values that may be specified in config file
13513 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13515 // special case: check if network and preview player positions are redefined
13516 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13518 freeSetupFileHash(setup_file_hash);
13521 void LoadMenuDesignSettings(void)
13523 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13525 InitMenuDesignSettings_Static();
13526 InitMenuDesignSettings_SpecialPreProcessing();
13527 InitMenuDesignSettings_PreviewPlayers();
13529 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13531 // first look for special settings configured in level series config
13532 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13534 if (fileExists(filename_base))
13535 LoadMenuDesignSettingsFromFilename(filename_base);
13538 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13540 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13541 LoadMenuDesignSettingsFromFilename(filename_local);
13543 InitMenuDesignSettings_SpecialPostProcessing();
13546 void LoadMenuDesignSettings_AfterGraphics(void)
13548 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13551 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13552 boolean ignore_defaults)
13556 for (i = 0; sound_config_vars[i].token != NULL; i++)
13558 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13560 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13561 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13565 *sound_config_vars[i].value =
13566 get_token_parameter_value(sound_config_vars[i].token, value);
13570 void InitSoundSettings_Static(void)
13572 // always start with reliable default values from static default config
13573 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13576 static void LoadSoundSettingsFromFilename(char *filename)
13578 SetupFileHash *setup_file_hash;
13580 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13583 // read (and overwrite with) values that may be specified in config file
13584 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13586 freeSetupFileHash(setup_file_hash);
13589 void LoadSoundSettings(void)
13591 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13593 InitSoundSettings_Static();
13595 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13597 // first look for special settings configured in level series config
13598 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13600 if (fileExists(filename_base))
13601 LoadSoundSettingsFromFilename(filename_base);
13604 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13606 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13607 LoadSoundSettingsFromFilename(filename_local);
13610 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13612 char *filename = getEditorSetupFilename();
13613 SetupFileList *setup_file_list, *list;
13614 SetupFileHash *element_hash;
13615 int num_unknown_tokens = 0;
13618 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13621 element_hash = newSetupFileHash();
13623 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13624 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13626 // determined size may be larger than needed (due to unknown elements)
13628 for (list = setup_file_list; list != NULL; list = list->next)
13631 // add space for up to 3 more elements for padding that may be needed
13632 *num_elements += 3;
13634 // free memory for old list of elements, if needed
13635 checked_free(*elements);
13637 // allocate memory for new list of elements
13638 *elements = checked_malloc(*num_elements * sizeof(int));
13641 for (list = setup_file_list; list != NULL; list = list->next)
13643 char *value = getHashEntry(element_hash, list->token);
13645 if (value == NULL) // try to find obsolete token mapping
13647 char *mapped_token = get_mapped_token(list->token);
13649 if (mapped_token != NULL)
13651 value = getHashEntry(element_hash, mapped_token);
13653 free(mapped_token);
13659 (*elements)[(*num_elements)++] = atoi(value);
13663 if (num_unknown_tokens == 0)
13666 Warn("unknown token(s) found in config file:");
13667 Warn("- config file: '%s'", filename);
13669 num_unknown_tokens++;
13672 Warn("- token: '%s'", list->token);
13676 if (num_unknown_tokens > 0)
13679 while (*num_elements % 4) // pad with empty elements, if needed
13680 (*elements)[(*num_elements)++] = EL_EMPTY;
13682 freeSetupFileList(setup_file_list);
13683 freeSetupFileHash(element_hash);
13686 for (i = 0; i < *num_elements; i++)
13687 Debug("editor", "element '%s' [%d]\n",
13688 element_info[(*elements)[i]].token_name, (*elements)[i]);
13692 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13695 SetupFileHash *setup_file_hash = NULL;
13696 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13697 char *filename_music, *filename_prefix, *filename_info;
13703 token_to_value_ptr[] =
13705 { "title_header", &tmp_music_file_info.title_header },
13706 { "artist_header", &tmp_music_file_info.artist_header },
13707 { "album_header", &tmp_music_file_info.album_header },
13708 { "year_header", &tmp_music_file_info.year_header },
13709 { "played_header", &tmp_music_file_info.played_header },
13711 { "title", &tmp_music_file_info.title },
13712 { "artist", &tmp_music_file_info.artist },
13713 { "album", &tmp_music_file_info.album },
13714 { "year", &tmp_music_file_info.year },
13715 { "played", &tmp_music_file_info.played },
13721 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13722 getCustomMusicFilename(basename));
13724 if (filename_music == NULL)
13727 // ---------- try to replace file extension ----------
13729 filename_prefix = getStringCopy(filename_music);
13730 if (strrchr(filename_prefix, '.') != NULL)
13731 *strrchr(filename_prefix, '.') = '\0';
13732 filename_info = getStringCat2(filename_prefix, ".txt");
13734 if (fileExists(filename_info))
13735 setup_file_hash = loadSetupFileHash(filename_info);
13737 free(filename_prefix);
13738 free(filename_info);
13740 if (setup_file_hash == NULL)
13742 // ---------- try to add file extension ----------
13744 filename_prefix = getStringCopy(filename_music);
13745 filename_info = getStringCat2(filename_prefix, ".txt");
13747 if (fileExists(filename_info))
13748 setup_file_hash = loadSetupFileHash(filename_info);
13750 free(filename_prefix);
13751 free(filename_info);
13754 if (setup_file_hash == NULL)
13757 // ---------- music file info found ----------
13759 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13761 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13763 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13765 *token_to_value_ptr[i].value_ptr =
13766 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13769 tmp_music_file_info.basename = getStringCopy(basename);
13770 tmp_music_file_info.music = music;
13771 tmp_music_file_info.is_sound = is_sound;
13773 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13774 *new_music_file_info = tmp_music_file_info;
13776 return new_music_file_info;
13779 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13781 return get_music_file_info_ext(basename, music, FALSE);
13784 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13786 return get_music_file_info_ext(basename, sound, TRUE);
13789 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13790 char *basename, boolean is_sound)
13792 for (; list != NULL; list = list->next)
13793 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13799 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13801 return music_info_listed_ext(list, basename, FALSE);
13804 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13806 return music_info_listed_ext(list, basename, TRUE);
13809 void LoadMusicInfo(void)
13811 int num_music_noconf = getMusicListSize_NoConf();
13812 int num_music = getMusicListSize();
13813 int num_sounds = getSoundListSize();
13814 struct FileInfo *music, *sound;
13815 struct MusicFileInfo *next, **new;
13819 while (music_file_info != NULL)
13821 next = music_file_info->next;
13823 checked_free(music_file_info->basename);
13825 checked_free(music_file_info->title_header);
13826 checked_free(music_file_info->artist_header);
13827 checked_free(music_file_info->album_header);
13828 checked_free(music_file_info->year_header);
13829 checked_free(music_file_info->played_header);
13831 checked_free(music_file_info->title);
13832 checked_free(music_file_info->artist);
13833 checked_free(music_file_info->album);
13834 checked_free(music_file_info->year);
13835 checked_free(music_file_info->played);
13837 free(music_file_info);
13839 music_file_info = next;
13842 new = &music_file_info;
13844 // get (configured or unconfigured) music file info for all levels
13845 for (i = leveldir_current->first_level;
13846 i <= leveldir_current->last_level; i++)
13850 if (levelset.music[i] != MUS_UNDEFINED)
13852 // get music file info for configured level music
13853 music_nr = levelset.music[i];
13855 else if (num_music_noconf > 0)
13857 // get music file info for unconfigured level music
13858 int level_pos = i - leveldir_current->first_level;
13860 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13867 char *basename = getMusicInfoEntryFilename(music_nr);
13869 if (basename == NULL)
13872 if (!music_info_listed(music_file_info, basename))
13874 *new = get_music_file_info(basename, music_nr);
13877 new = &(*new)->next;
13881 // get music file info for all remaining configured music files
13882 for (i = 0; i < num_music; i++)
13884 music = getMusicListEntry(i);
13886 if (music->filename == NULL)
13889 if (strEqual(music->filename, UNDEFINED_FILENAME))
13892 // a configured file may be not recognized as music
13893 if (!FileIsMusic(music->filename))
13896 if (!music_info_listed(music_file_info, music->filename))
13898 *new = get_music_file_info(music->filename, i);
13901 new = &(*new)->next;
13905 // get sound file info for all configured sound files
13906 for (i = 0; i < num_sounds; i++)
13908 sound = getSoundListEntry(i);
13910 if (sound->filename == NULL)
13913 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13916 // a configured file may be not recognized as sound
13917 if (!FileIsSound(sound->filename))
13920 if (!sound_info_listed(music_file_info, sound->filename))
13922 *new = get_sound_file_info(sound->filename, i);
13924 new = &(*new)->next;
13928 // add pointers to previous list nodes
13930 struct MusicFileInfo *node = music_file_info;
13932 while (node != NULL)
13935 node->next->prev = node;
13941 static void add_helpanim_entry(int element, int action, int direction,
13942 int delay, int *num_list_entries)
13944 struct HelpAnimInfo *new_list_entry;
13945 (*num_list_entries)++;
13948 checked_realloc(helpanim_info,
13949 *num_list_entries * sizeof(struct HelpAnimInfo));
13950 new_list_entry = &helpanim_info[*num_list_entries - 1];
13952 new_list_entry->element = element;
13953 new_list_entry->action = action;
13954 new_list_entry->direction = direction;
13955 new_list_entry->delay = delay;
13958 static void print_unknown_token(char *filename, char *token, int token_nr)
13963 Warn("unknown token(s) found in config file:");
13964 Warn("- config file: '%s'", filename);
13967 Warn("- token: '%s'", token);
13970 static void print_unknown_token_end(int token_nr)
13976 void LoadHelpAnimInfo(void)
13978 char *filename = getHelpAnimFilename();
13979 SetupFileList *setup_file_list = NULL, *list;
13980 SetupFileHash *element_hash, *action_hash, *direction_hash;
13981 int num_list_entries = 0;
13982 int num_unknown_tokens = 0;
13985 if (fileExists(filename))
13986 setup_file_list = loadSetupFileList(filename);
13988 if (setup_file_list == NULL)
13990 // use reliable default values from static configuration
13991 SetupFileList *insert_ptr;
13993 insert_ptr = setup_file_list =
13994 newSetupFileList(helpanim_config[0].token,
13995 helpanim_config[0].value);
13997 for (i = 1; helpanim_config[i].token; i++)
13998 insert_ptr = addListEntry(insert_ptr,
13999 helpanim_config[i].token,
14000 helpanim_config[i].value);
14003 element_hash = newSetupFileHash();
14004 action_hash = newSetupFileHash();
14005 direction_hash = newSetupFileHash();
14007 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14008 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14010 for (i = 0; i < NUM_ACTIONS; i++)
14011 setHashEntry(action_hash, element_action_info[i].suffix,
14012 i_to_a(element_action_info[i].value));
14014 // do not store direction index (bit) here, but direction value!
14015 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14016 setHashEntry(direction_hash, element_direction_info[i].suffix,
14017 i_to_a(1 << element_direction_info[i].value));
14019 for (list = setup_file_list; list != NULL; list = list->next)
14021 char *element_token, *action_token, *direction_token;
14022 char *element_value, *action_value, *direction_value;
14023 int delay = atoi(list->value);
14025 if (strEqual(list->token, "end"))
14027 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14032 /* first try to break element into element/action/direction parts;
14033 if this does not work, also accept combined "element[.act][.dir]"
14034 elements (like "dynamite.active"), which are unique elements */
14036 if (strchr(list->token, '.') == NULL) // token contains no '.'
14038 element_value = getHashEntry(element_hash, list->token);
14039 if (element_value != NULL) // element found
14040 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14041 &num_list_entries);
14044 // no further suffixes found -- this is not an element
14045 print_unknown_token(filename, list->token, num_unknown_tokens++);
14051 // token has format "<prefix>.<something>"
14053 action_token = strchr(list->token, '.'); // suffix may be action ...
14054 direction_token = action_token; // ... or direction
14056 element_token = getStringCopy(list->token);
14057 *strchr(element_token, '.') = '\0';
14059 element_value = getHashEntry(element_hash, element_token);
14061 if (element_value == NULL) // this is no element
14063 element_value = getHashEntry(element_hash, list->token);
14064 if (element_value != NULL) // combined element found
14065 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14066 &num_list_entries);
14068 print_unknown_token(filename, list->token, num_unknown_tokens++);
14070 free(element_token);
14075 action_value = getHashEntry(action_hash, action_token);
14077 if (action_value != NULL) // action found
14079 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14080 &num_list_entries);
14082 free(element_token);
14087 direction_value = getHashEntry(direction_hash, direction_token);
14089 if (direction_value != NULL) // direction found
14091 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14092 &num_list_entries);
14094 free(element_token);
14099 if (strchr(action_token + 1, '.') == NULL)
14101 // no further suffixes found -- this is not an action nor direction
14103 element_value = getHashEntry(element_hash, list->token);
14104 if (element_value != NULL) // combined element found
14105 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14106 &num_list_entries);
14108 print_unknown_token(filename, list->token, num_unknown_tokens++);
14110 free(element_token);
14115 // token has format "<prefix>.<suffix>.<something>"
14117 direction_token = strchr(action_token + 1, '.');
14119 action_token = getStringCopy(action_token);
14120 *strchr(action_token + 1, '.') = '\0';
14122 action_value = getHashEntry(action_hash, action_token);
14124 if (action_value == NULL) // this is no action
14126 element_value = getHashEntry(element_hash, list->token);
14127 if (element_value != NULL) // combined element found
14128 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14129 &num_list_entries);
14131 print_unknown_token(filename, list->token, num_unknown_tokens++);
14133 free(element_token);
14134 free(action_token);
14139 direction_value = getHashEntry(direction_hash, direction_token);
14141 if (direction_value != NULL) // direction found
14143 add_helpanim_entry(atoi(element_value), atoi(action_value),
14144 atoi(direction_value), delay, &num_list_entries);
14146 free(element_token);
14147 free(action_token);
14152 // this is no direction
14154 element_value = getHashEntry(element_hash, list->token);
14155 if (element_value != NULL) // combined element found
14156 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14157 &num_list_entries);
14159 print_unknown_token(filename, list->token, num_unknown_tokens++);
14161 free(element_token);
14162 free(action_token);
14165 print_unknown_token_end(num_unknown_tokens);
14167 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14168 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14170 freeSetupFileList(setup_file_list);
14171 freeSetupFileHash(element_hash);
14172 freeSetupFileHash(action_hash);
14173 freeSetupFileHash(direction_hash);
14176 for (i = 0; i < num_list_entries; i++)
14177 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14178 EL_NAME(helpanim_info[i].element),
14179 helpanim_info[i].element,
14180 helpanim_info[i].action,
14181 helpanim_info[i].direction,
14182 helpanim_info[i].delay);
14186 void LoadHelpTextInfo(void)
14188 char *filename = getHelpTextFilename();
14191 if (helptext_info != NULL)
14193 freeSetupFileHash(helptext_info);
14194 helptext_info = NULL;
14197 if (fileExists(filename))
14198 helptext_info = loadSetupFileHash(filename);
14200 if (helptext_info == NULL)
14202 // use reliable default values from static configuration
14203 helptext_info = newSetupFileHash();
14205 for (i = 0; helptext_config[i].token; i++)
14206 setHashEntry(helptext_info,
14207 helptext_config[i].token,
14208 helptext_config[i].value);
14212 BEGIN_HASH_ITERATION(helptext_info, itr)
14214 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14215 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14217 END_HASH_ITERATION(hash, itr)
14222 // ----------------------------------------------------------------------------
14224 // ----------------------------------------------------------------------------
14226 #define MAX_NUM_CONVERT_LEVELS 1000
14228 void ConvertLevels(void)
14230 static LevelDirTree *convert_leveldir = NULL;
14231 static int convert_level_nr = -1;
14232 static int num_levels_handled = 0;
14233 static int num_levels_converted = 0;
14234 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14237 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14238 global.convert_leveldir);
14240 if (convert_leveldir == NULL)
14241 Fail("no such level identifier: '%s'", global.convert_leveldir);
14243 leveldir_current = convert_leveldir;
14245 if (global.convert_level_nr != -1)
14247 convert_leveldir->first_level = global.convert_level_nr;
14248 convert_leveldir->last_level = global.convert_level_nr;
14251 convert_level_nr = convert_leveldir->first_level;
14253 PrintLine("=", 79);
14254 Print("Converting levels\n");
14255 PrintLine("-", 79);
14256 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14257 Print("Level series name: '%s'\n", convert_leveldir->name);
14258 Print("Level series author: '%s'\n", convert_leveldir->author);
14259 Print("Number of levels: %d\n", convert_leveldir->levels);
14260 PrintLine("=", 79);
14263 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14264 levels_failed[i] = FALSE;
14266 while (convert_level_nr <= convert_leveldir->last_level)
14268 char *level_filename;
14271 level_nr = convert_level_nr++;
14273 Print("Level %03d: ", level_nr);
14275 LoadLevel(level_nr);
14276 if (level.no_level_file || level.no_valid_file)
14278 Print("(no level)\n");
14282 Print("converting level ... ");
14285 // special case: conversion of some EMC levels as requested by ACME
14286 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14289 level_filename = getDefaultLevelFilename(level_nr);
14290 new_level = !fileExists(level_filename);
14294 SaveLevel(level_nr);
14296 num_levels_converted++;
14298 Print("converted.\n");
14302 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14303 levels_failed[level_nr] = TRUE;
14305 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14308 num_levels_handled++;
14312 PrintLine("=", 79);
14313 Print("Number of levels handled: %d\n", num_levels_handled);
14314 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14315 (num_levels_handled ?
14316 num_levels_converted * 100 / num_levels_handled : 0));
14317 PrintLine("-", 79);
14318 Print("Summary (for automatic parsing by scripts):\n");
14319 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14320 convert_leveldir->identifier, num_levels_converted,
14321 num_levels_handled,
14322 (num_levels_handled ?
14323 num_levels_converted * 100 / num_levels_handled : 0));
14325 if (num_levels_handled != num_levels_converted)
14327 Print(", FAILED:");
14328 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14329 if (levels_failed[i])
14334 PrintLine("=", 79);
14336 CloseAllAndExit(0);
14340 // ----------------------------------------------------------------------------
14341 // create and save images for use in level sketches (raw BMP format)
14342 // ----------------------------------------------------------------------------
14344 void CreateLevelSketchImages(void)
14350 InitElementPropertiesGfxElement();
14352 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14353 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14355 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14357 int element = getMappedElement(i);
14358 char basename1[16];
14359 char basename2[16];
14363 sprintf(basename1, "%04d.bmp", i);
14364 sprintf(basename2, "%04ds.bmp", i);
14366 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14367 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14369 DrawSizedElement(0, 0, element, TILESIZE);
14370 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14372 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14373 Fail("cannot save level sketch image file '%s'", filename1);
14375 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14376 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14378 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14379 Fail("cannot save level sketch image file '%s'", filename2);
14384 // create corresponding SQL statements (for normal and small images)
14387 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14388 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14391 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14392 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14394 // optional: create content for forum level sketch demonstration post
14396 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14399 FreeBitmap(bitmap1);
14400 FreeBitmap(bitmap2);
14403 fprintf(stderr, "\n");
14405 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14407 CloseAllAndExit(0);
14411 // ----------------------------------------------------------------------------
14412 // create and save images for element collecting animations (raw BMP format)
14413 // ----------------------------------------------------------------------------
14415 static boolean createCollectImage(int element)
14417 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14420 void CreateCollectElementImages(void)
14424 int anim_frames = num_steps - 1;
14425 int tile_size = TILESIZE;
14426 int anim_width = tile_size * anim_frames;
14427 int anim_height = tile_size;
14428 int num_collect_images = 0;
14429 int pos_collect_images = 0;
14431 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14432 if (createCollectImage(i))
14433 num_collect_images++;
14435 Info("Creating %d element collecting animation images ...",
14436 num_collect_images);
14438 int dst_width = anim_width * 2;
14439 int dst_height = anim_height * num_collect_images / 2;
14440 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14441 char *basename_bmp = "RocksCollect.bmp";
14442 char *basename_png = "RocksCollect.png";
14443 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14444 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14445 int len_filename_bmp = strlen(filename_bmp);
14446 int len_filename_png = strlen(filename_png);
14447 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14448 char cmd_convert[max_command_len];
14450 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14454 // force using RGBA surface for destination bitmap
14455 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14456 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14458 dst_bitmap->surface =
14459 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14461 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14463 if (!createCollectImage(i))
14466 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14467 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14468 int graphic = el2img(i);
14469 char *token_name = element_info[i].token_name;
14470 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14471 Bitmap *src_bitmap;
14474 Info("- creating collecting image for '%s' ...", token_name);
14476 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14478 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14479 tile_size, tile_size, 0, 0);
14481 // force using RGBA surface for temporary bitmap (using transparent black)
14482 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14483 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14485 tmp_bitmap->surface =
14486 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14488 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14490 for (j = 0; j < anim_frames; j++)
14492 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14493 int frame_size = frame_size_final * num_steps;
14494 int offset = (tile_size - frame_size_final) / 2;
14495 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14497 while (frame_size > frame_size_final)
14501 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14503 FreeBitmap(frame_bitmap);
14505 frame_bitmap = half_bitmap;
14508 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14509 frame_size_final, frame_size_final,
14510 dst_x + j * tile_size + offset, dst_y + offset);
14512 FreeBitmap(frame_bitmap);
14515 tmp_bitmap->surface_masked = NULL;
14517 FreeBitmap(tmp_bitmap);
14519 pos_collect_images++;
14522 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14523 Fail("cannot save element collecting image file '%s'", filename_bmp);
14525 FreeBitmap(dst_bitmap);
14527 Info("Converting image file from BMP to PNG ...");
14529 if (system(cmd_convert) != 0)
14530 Fail("converting image file failed");
14532 unlink(filename_bmp);
14536 CloseAllAndExit(0);
14540 // ----------------------------------------------------------------------------
14541 // create and save images for custom and group elements (raw BMP format)
14542 // ----------------------------------------------------------------------------
14544 void CreateCustomElementImages(char *directory)
14546 char *src_basename = "RocksCE-template.ilbm";
14547 char *dst_basename = "RocksCE.bmp";
14548 char *src_filename = getPath2(directory, src_basename);
14549 char *dst_filename = getPath2(directory, dst_basename);
14550 Bitmap *src_bitmap;
14552 int yoffset_ce = 0;
14553 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14556 InitVideoDefaults();
14558 ReCreateBitmap(&backbuffer, video.width, video.height);
14560 src_bitmap = LoadImage(src_filename);
14562 bitmap = CreateBitmap(TILEX * 16 * 2,
14563 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14566 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14573 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14574 TILEX * x, TILEY * y + yoffset_ce);
14576 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14578 TILEX * x + TILEX * 16,
14579 TILEY * y + yoffset_ce);
14581 for (j = 2; j >= 0; j--)
14585 BlitBitmap(src_bitmap, bitmap,
14586 TILEX + c * 7, 0, 6, 10,
14587 TILEX * x + 6 + j * 7,
14588 TILEY * y + 11 + yoffset_ce);
14590 BlitBitmap(src_bitmap, bitmap,
14591 TILEX + c * 8, TILEY, 6, 10,
14592 TILEX * 16 + TILEX * x + 6 + j * 8,
14593 TILEY * y + 10 + yoffset_ce);
14599 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14606 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14607 TILEX * x, TILEY * y + yoffset_ge);
14609 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14611 TILEX * x + TILEX * 16,
14612 TILEY * y + yoffset_ge);
14614 for (j = 1; j >= 0; j--)
14618 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14619 TILEX * x + 6 + j * 10,
14620 TILEY * y + 11 + yoffset_ge);
14622 BlitBitmap(src_bitmap, bitmap,
14623 TILEX + c * 8, TILEY + 12, 6, 10,
14624 TILEX * 16 + TILEX * x + 10 + j * 8,
14625 TILEY * y + 10 + yoffset_ge);
14631 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14632 Fail("cannot save CE graphics file '%s'", dst_filename);
14634 FreeBitmap(bitmap);
14636 CloseAllAndExit(0);