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
775 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
776 &li.bd_water_cannot_flow_down, FALSE
779 // (the following values are related to various game elements)
783 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
784 &li.score[SC_EMERALD], 10
789 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
790 &li.score[SC_DIAMOND], 10
795 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
796 &li.score[SC_BUG], 10
801 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
802 &li.score[SC_SPACESHIP], 10
807 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
808 &li.score[SC_PACMAN], 10
813 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
814 &li.score[SC_NUT], 10
819 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
820 &li.score[SC_DYNAMITE], 10
825 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
826 &li.score[SC_KEY], 10
831 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
832 &li.score[SC_PEARL], 10
837 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
838 &li.score[SC_CRYSTAL], 10
841 // (amoeba values used by R'n'D game engine only)
844 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
845 &li.amoeba_content, EL_DIAMOND
849 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
854 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
855 &li.grow_into_diggable, TRUE
857 // (amoeba values used by BD game engine only)
860 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
861 &li.bd_amoeba_wait_for_hatching, FALSE
865 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
866 &li.bd_amoeba_start_immediately, TRUE
870 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
871 &li.bd_amoeba_2_explode_by_amoeba, TRUE
875 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
876 &li.bd_amoeba_threshold_too_big, 200
880 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
881 &li.bd_amoeba_slow_growth_time, 200
885 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
886 &li.bd_amoeba_slow_growth_rate, 3
890 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
891 &li.bd_amoeba_fast_growth_rate, 25
895 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
896 &li.bd_amoeba_content_too_big, EL_BD_ROCK
900 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
901 &li.bd_amoeba_content_enclosed, EL_BD_DIAMOND
906 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
907 &li.bd_amoeba_2_threshold_too_big, 200
911 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
912 &li.bd_amoeba_2_slow_growth_time, 200
916 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
917 &li.bd_amoeba_2_slow_growth_rate, 3
921 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
922 &li.bd_amoeba_2_fast_growth_rate, 25
926 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
927 &li.bd_amoeba_2_content_too_big, EL_BD_ROCK
931 TYPE_ELEMENT, CONF_VALUE_16_BIT(6),
932 &li.bd_amoeba_2_content_enclosed, EL_BD_DIAMOND
936 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
937 &li.bd_amoeba_2_content_exploding, EL_EMPTY
941 TYPE_ELEMENT, CONF_VALUE_16_BIT(8),
942 &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
947 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
948 &li.yamyam_content, EL_ROCK, NULL,
949 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
953 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
954 &li.score[SC_YAMYAM], 10
959 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
960 &li.score[SC_ROBOT], 10
964 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
970 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
976 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
977 &li.time_magic_wall, 10
982 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
983 &li.game_of_life[0], 2
987 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
988 &li.game_of_life[1], 3
992 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
993 &li.game_of_life[2], 3
997 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
998 &li.game_of_life[3], 3
1001 EL_GAME_OF_LIFE, -1,
1002 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
1003 &li.use_life_bugs, FALSE
1008 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1013 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1018 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
1023 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
1028 EL_TIMEGATE_SWITCH, -1,
1029 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1030 &li.time_timegate, 10
1034 EL_LIGHT_SWITCH_ACTIVE, -1,
1035 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1040 EL_SHIELD_NORMAL, -1,
1041 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1042 &li.shield_normal_time, 10
1045 EL_SHIELD_NORMAL, -1,
1046 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1047 &li.score[SC_SHIELD], 10
1051 EL_SHIELD_DEADLY, -1,
1052 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1053 &li.shield_deadly_time, 10
1056 EL_SHIELD_DEADLY, -1,
1057 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1058 &li.score[SC_SHIELD], 10
1063 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1068 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1069 &li.extra_time_score, 10
1073 EL_TIME_ORB_FULL, -1,
1074 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1075 &li.time_orb_time, 10
1078 EL_TIME_ORB_FULL, -1,
1079 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1080 &li.use_time_orb_bug, FALSE
1085 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1086 &li.use_spring_bug, FALSE
1091 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1092 &li.android_move_time, 10
1096 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1097 &li.android_clone_time, 10
1100 EL_EMC_ANDROID, SAVE_CONF_NEVER,
1101 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1102 &li.android_clone_element[0], EL_EMPTY, NULL,
1103 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
1107 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1108 &li.android_clone_element[0], EL_EMPTY, NULL,
1109 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
1114 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1115 &li.lenses_score, 10
1119 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1124 EL_EMC_MAGNIFIER, -1,
1125 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1126 &li.magnify_score, 10
1129 EL_EMC_MAGNIFIER, -1,
1130 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1131 &li.magnify_time, 10
1135 EL_EMC_MAGIC_BALL, -1,
1136 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1140 EL_EMC_MAGIC_BALL, -1,
1141 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1142 &li.ball_random, FALSE
1145 EL_EMC_MAGIC_BALL, -1,
1146 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1147 &li.ball_active_initial, FALSE
1150 EL_EMC_MAGIC_BALL, -1,
1151 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1152 &li.ball_content, EL_EMPTY, NULL,
1153 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
1157 EL_SOKOBAN_FIELD_EMPTY, -1,
1158 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1159 &li.sb_fields_needed, TRUE
1163 EL_SOKOBAN_OBJECT, -1,
1164 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1165 &li.sb_objects_needed, TRUE
1170 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1171 &li.mm_laser_red, FALSE
1175 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1176 &li.mm_laser_green, FALSE
1180 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1181 &li.mm_laser_blue, TRUE
1186 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1187 &li.df_laser_red, TRUE
1191 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1192 &li.df_laser_green, TRUE
1196 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1197 &li.df_laser_blue, FALSE
1201 EL_MM_FUSE_ACTIVE, -1,
1202 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1203 &li.mm_time_fuse, 25
1207 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1208 &li.mm_time_bomb, 75
1212 EL_MM_GRAY_BALL, -1,
1213 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1214 &li.mm_time_ball, 75
1217 EL_MM_GRAY_BALL, -1,
1218 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1219 &li.mm_ball_choice_mode, ANIM_RANDOM
1222 EL_MM_GRAY_BALL, -1,
1223 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1224 &li.mm_ball_content, EL_EMPTY, NULL,
1225 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1228 EL_MM_GRAY_BALL, -1,
1229 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1230 &li.rotate_mm_ball_content, TRUE
1233 EL_MM_GRAY_BALL, -1,
1234 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1235 &li.explode_mm_ball, FALSE
1239 EL_MM_STEEL_BLOCK, -1,
1240 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1241 &li.mm_time_block, 75
1244 EL_MM_LIGHTBALL, -1,
1245 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1246 &li.score[SC_ELEM_BONUS], 10
1256 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1260 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1261 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1265 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1266 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1271 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1272 &xx_envelope.autowrap, FALSE
1276 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1277 &xx_envelope.centered, FALSE
1282 TYPE_STRING, CONF_VALUE_BYTES(1),
1283 &xx_envelope.text, -1, NULL,
1284 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1285 &xx_default_string_empty[0]
1295 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1299 TYPE_STRING, CONF_VALUE_BYTES(1),
1300 &xx_ei.description[0], -1,
1301 &yy_ei.description[0],
1302 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1303 &xx_default_description[0]
1308 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1309 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1310 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1312 #if ENABLE_RESERVED_CODE
1313 // (reserved for later use)
1316 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1317 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1318 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1324 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1325 &xx_ei.use_gfx_element, FALSE,
1326 &yy_ei.use_gfx_element
1330 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1331 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1332 &yy_ei.gfx_element_initial
1337 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1338 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1339 &yy_ei.access_direction
1344 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1345 &xx_ei.collect_score_initial, 10,
1346 &yy_ei.collect_score_initial
1350 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1351 &xx_ei.collect_count_initial, 1,
1352 &yy_ei.collect_count_initial
1357 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1358 &xx_ei.ce_value_fixed_initial, 0,
1359 &yy_ei.ce_value_fixed_initial
1363 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1364 &xx_ei.ce_value_random_initial, 0,
1365 &yy_ei.ce_value_random_initial
1369 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1370 &xx_ei.use_last_ce_value, FALSE,
1371 &yy_ei.use_last_ce_value
1376 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1377 &xx_ei.push_delay_fixed, 8,
1378 &yy_ei.push_delay_fixed
1382 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1383 &xx_ei.push_delay_random, 8,
1384 &yy_ei.push_delay_random
1388 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1389 &xx_ei.drop_delay_fixed, 0,
1390 &yy_ei.drop_delay_fixed
1394 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1395 &xx_ei.drop_delay_random, 0,
1396 &yy_ei.drop_delay_random
1400 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1401 &xx_ei.move_delay_fixed, 0,
1402 &yy_ei.move_delay_fixed
1406 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1407 &xx_ei.move_delay_random, 0,
1408 &yy_ei.move_delay_random
1412 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1413 &xx_ei.step_delay_fixed, 0,
1414 &yy_ei.step_delay_fixed
1418 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1419 &xx_ei.step_delay_random, 0,
1420 &yy_ei.step_delay_random
1425 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1426 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1431 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1432 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1433 &yy_ei.move_direction_initial
1437 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1438 &xx_ei.move_stepsize, TILEX / 8,
1439 &yy_ei.move_stepsize
1444 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1445 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1446 &yy_ei.move_enter_element
1450 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1451 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1452 &yy_ei.move_leave_element
1456 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1457 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1458 &yy_ei.move_leave_type
1463 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1464 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1465 &yy_ei.slippery_type
1470 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1471 &xx_ei.explosion_type, EXPLODES_3X3,
1472 &yy_ei.explosion_type
1476 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1477 &xx_ei.explosion_delay, 16,
1478 &yy_ei.explosion_delay
1482 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1483 &xx_ei.ignition_delay, 8,
1484 &yy_ei.ignition_delay
1489 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1490 &xx_ei.content, EL_EMPTY_SPACE,
1492 &xx_num_contents, 1, 1
1495 // ---------- "num_change_pages" must be the last entry ---------------------
1498 -1, SAVE_CONF_ALWAYS,
1499 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1500 &xx_ei.num_change_pages, 1,
1501 &yy_ei.num_change_pages
1512 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1514 // ---------- "current_change_page" must be the first entry -----------------
1517 -1, SAVE_CONF_ALWAYS,
1518 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1519 &xx_current_change_page, -1
1522 // ---------- (the remaining entries can be in any order) -------------------
1526 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1527 &xx_change.can_change, FALSE
1532 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1533 &xx_event_bits[0], 0
1537 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1538 &xx_event_bits[1], 0
1543 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1544 &xx_change.trigger_player, CH_PLAYER_ANY
1548 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1549 &xx_change.trigger_side, CH_SIDE_ANY
1553 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1554 &xx_change.trigger_page, CH_PAGE_ANY
1559 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1560 &xx_change.target_element, EL_EMPTY_SPACE
1565 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1566 &xx_change.delay_fixed, 0
1570 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1571 &xx_change.delay_random, 0
1575 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1576 &xx_change.delay_frames, FRAMES_PER_SECOND
1581 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1582 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1587 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1588 &xx_change.explode, FALSE
1592 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1593 &xx_change.use_target_content, FALSE
1597 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1598 &xx_change.only_if_complete, FALSE
1602 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1603 &xx_change.use_random_replace, FALSE
1607 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1608 &xx_change.random_percentage, 100
1612 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1613 &xx_change.replace_when, CP_WHEN_EMPTY
1618 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1619 &xx_change.has_action, FALSE
1623 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1624 &xx_change.action_type, CA_NO_ACTION
1628 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1629 &xx_change.action_mode, CA_MODE_UNDEFINED
1633 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1634 &xx_change.action_arg, CA_ARG_UNDEFINED
1639 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1640 &xx_change.action_element, EL_EMPTY_SPACE
1645 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1646 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1647 &xx_num_contents, 1, 1
1657 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1661 TYPE_STRING, CONF_VALUE_BYTES(1),
1662 &xx_ei.description[0], -1, NULL,
1663 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1664 &xx_default_description[0]
1669 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1670 &xx_ei.use_gfx_element, FALSE
1674 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1675 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1680 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1681 &xx_group.choice_mode, ANIM_RANDOM
1686 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1687 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1688 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1698 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1702 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1703 &xx_ei.use_gfx_element, FALSE
1707 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1708 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1718 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1722 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1723 &li.block_snap_field, TRUE
1727 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1728 &li.continuous_snapping, TRUE
1732 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1733 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1737 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1738 &li.use_start_element[0], FALSE
1742 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1743 &li.start_element[0], EL_PLAYER_1
1747 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1748 &li.use_artwork_element[0], FALSE
1752 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1753 &li.artwork_element[0], EL_PLAYER_1
1757 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1758 &li.use_explosion_element[0], FALSE
1762 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1763 &li.explosion_element[0], EL_PLAYER_1
1778 filetype_id_list[] =
1780 { LEVEL_FILE_TYPE_RND, "RND" },
1781 { LEVEL_FILE_TYPE_BD, "BD" },
1782 { LEVEL_FILE_TYPE_EM, "EM" },
1783 { LEVEL_FILE_TYPE_SP, "SP" },
1784 { LEVEL_FILE_TYPE_DX, "DX" },
1785 { LEVEL_FILE_TYPE_SB, "SB" },
1786 { LEVEL_FILE_TYPE_DC, "DC" },
1787 { LEVEL_FILE_TYPE_MM, "MM" },
1788 { LEVEL_FILE_TYPE_MM, "DF" },
1793 // ============================================================================
1794 // level file functions
1795 // ============================================================================
1797 static boolean check_special_flags(char *flag)
1799 if (strEqual(options.special_flags, flag) ||
1800 strEqual(leveldir_current->special_flags, flag))
1806 static struct DateInfo getCurrentDate(void)
1808 time_t epoch_seconds = time(NULL);
1809 struct tm *now = localtime(&epoch_seconds);
1810 struct DateInfo date;
1812 date.year = now->tm_year + 1900;
1813 date.month = now->tm_mon + 1;
1814 date.day = now->tm_mday;
1816 date.src = DATE_SRC_CLOCK;
1821 static void resetEventFlags(struct ElementChangeInfo *change)
1825 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1826 change->has_event[i] = FALSE;
1829 static void resetEventBits(void)
1833 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1834 xx_event_bits[i] = 0;
1837 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1841 /* important: only change event flag if corresponding event bit is set
1842 (this is because all xx_event_bits[] values are loaded separately,
1843 and all xx_event_bits[] values are set back to zero before loading
1844 another value xx_event_bits[x] (each value representing 32 flags)) */
1846 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1847 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1848 change->has_event[i] = TRUE;
1851 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1855 /* in contrast to the above function setEventFlagsFromEventBits(), it
1856 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1857 depending on the corresponding change->has_event[i] values here, as
1858 all xx_event_bits[] values are reset in resetEventBits() before */
1860 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1861 if (change->has_event[i])
1862 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1865 static char *getDefaultElementDescription(struct ElementInfo *ei)
1867 static char description[MAX_ELEMENT_NAME_LEN + 1];
1868 char *default_description = (ei->custom_description != NULL ?
1869 ei->custom_description :
1870 ei->editor_description);
1873 // always start with reliable default values
1874 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1875 description[i] = '\0';
1877 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1878 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1880 return &description[0];
1883 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1885 char *default_description = getDefaultElementDescription(ei);
1888 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1889 ei->description[i] = default_description[i];
1892 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1896 for (i = 0; conf[i].data_type != -1; i++)
1898 int default_value = conf[i].default_value;
1899 int data_type = conf[i].data_type;
1900 int conf_type = conf[i].conf_type;
1901 int byte_mask = conf_type & CONF_MASK_BYTES;
1903 if (byte_mask == CONF_MASK_MULTI_BYTES)
1905 int default_num_entities = conf[i].default_num_entities;
1906 int max_num_entities = conf[i].max_num_entities;
1908 *(int *)(conf[i].num_entities) = default_num_entities;
1910 if (data_type == TYPE_STRING)
1912 char *default_string = conf[i].default_string;
1913 char *string = (char *)(conf[i].value);
1915 strncpy(string, default_string, max_num_entities);
1917 else if (data_type == TYPE_ELEMENT_LIST)
1919 int *element_array = (int *)(conf[i].value);
1922 for (j = 0; j < max_num_entities; j++)
1923 element_array[j] = default_value;
1925 else if (data_type == TYPE_CONTENT_LIST)
1927 struct Content *content = (struct Content *)(conf[i].value);
1930 for (c = 0; c < max_num_entities; c++)
1931 for (y = 0; y < 3; y++)
1932 for (x = 0; x < 3; x++)
1933 content[c].e[x][y] = default_value;
1936 else // constant size configuration data (1, 2 or 4 bytes)
1938 if (data_type == TYPE_BOOLEAN)
1939 *(boolean *)(conf[i].value) = default_value;
1941 *(int *) (conf[i].value) = default_value;
1946 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1950 for (i = 0; conf[i].data_type != -1; i++)
1952 int data_type = conf[i].data_type;
1953 int conf_type = conf[i].conf_type;
1954 int byte_mask = conf_type & CONF_MASK_BYTES;
1956 if (byte_mask == CONF_MASK_MULTI_BYTES)
1958 int max_num_entities = conf[i].max_num_entities;
1960 if (data_type == TYPE_STRING)
1962 char *string = (char *)(conf[i].value);
1963 char *string_copy = (char *)(conf[i].value_copy);
1965 strncpy(string_copy, string, max_num_entities);
1967 else if (data_type == TYPE_ELEMENT_LIST)
1969 int *element_array = (int *)(conf[i].value);
1970 int *element_array_copy = (int *)(conf[i].value_copy);
1973 for (j = 0; j < max_num_entities; j++)
1974 element_array_copy[j] = element_array[j];
1976 else if (data_type == TYPE_CONTENT_LIST)
1978 struct Content *content = (struct Content *)(conf[i].value);
1979 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1982 for (c = 0; c < max_num_entities; c++)
1983 for (y = 0; y < 3; y++)
1984 for (x = 0; x < 3; x++)
1985 content_copy[c].e[x][y] = content[c].e[x][y];
1988 else // constant size configuration data (1, 2 or 4 bytes)
1990 if (data_type == TYPE_BOOLEAN)
1991 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1993 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1998 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2002 xx_ei = *ei_from; // copy element data into temporary buffer
2003 yy_ei = *ei_to; // copy element data into temporary buffer
2005 copyConfigFromConfigList(chunk_config_CUSX_base);
2010 // ---------- reinitialize and copy change pages ----------
2012 ei_to->num_change_pages = ei_from->num_change_pages;
2013 ei_to->current_change_page = ei_from->current_change_page;
2015 setElementChangePages(ei_to, ei_to->num_change_pages);
2017 for (i = 0; i < ei_to->num_change_pages; i++)
2018 ei_to->change_page[i] = ei_from->change_page[i];
2020 // ---------- copy group element info ----------
2021 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
2022 *ei_to->group = *ei_from->group;
2024 // mark this custom element as modified
2025 ei_to->modified_settings = TRUE;
2028 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2030 int change_page_size = sizeof(struct ElementChangeInfo);
2032 ei->num_change_pages = MAX(1, change_pages);
2035 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2037 if (ei->current_change_page >= ei->num_change_pages)
2038 ei->current_change_page = ei->num_change_pages - 1;
2040 ei->change = &ei->change_page[ei->current_change_page];
2043 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2045 xx_change = *change; // copy change data into temporary buffer
2047 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2049 *change = xx_change;
2051 resetEventFlags(change);
2053 change->direct_action = 0;
2054 change->other_action = 0;
2056 change->pre_change_function = NULL;
2057 change->change_function = NULL;
2058 change->post_change_function = NULL;
2061 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2065 li = *level; // copy level data into temporary buffer
2066 setConfigToDefaultsFromConfigList(chunk_config_INFO);
2067 *level = li; // copy temporary buffer back to level data
2069 setLevelInfoToDefaults_BD();
2070 setLevelInfoToDefaults_EM();
2071 setLevelInfoToDefaults_SP();
2072 setLevelInfoToDefaults_MM();
2074 level->native_bd_level = &native_bd_level;
2075 level->native_em_level = &native_em_level;
2076 level->native_sp_level = &native_sp_level;
2077 level->native_mm_level = &native_mm_level;
2079 level->file_version = FILE_VERSION_ACTUAL;
2080 level->game_version = GAME_VERSION_ACTUAL;
2082 level->creation_date = getCurrentDate();
2084 level->encoding_16bit_field = TRUE;
2085 level->encoding_16bit_yamyam = TRUE;
2086 level->encoding_16bit_amoeba = TRUE;
2088 // clear level name and level author string buffers
2089 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2090 level->name[i] = '\0';
2091 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2092 level->author[i] = '\0';
2094 // set level name and level author to default values
2095 strcpy(level->name, NAMELESS_LEVEL_NAME);
2096 strcpy(level->author, ANONYMOUS_NAME);
2098 // set level playfield to playable default level with player and exit
2099 for (x = 0; x < MAX_LEV_FIELDX; x++)
2100 for (y = 0; y < MAX_LEV_FIELDY; y++)
2101 level->field[x][y] = EL_SAND;
2103 level->field[0][0] = EL_PLAYER_1;
2104 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2106 BorderElement = EL_STEELWALL;
2108 // detect custom elements when loading them
2109 level->file_has_custom_elements = FALSE;
2111 // set all bug compatibility flags to "false" => do not emulate this bug
2112 level->use_action_after_change_bug = FALSE;
2114 if (leveldir_current)
2116 // try to determine better author name than 'anonymous'
2117 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2119 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2120 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2124 switch (LEVELCLASS(leveldir_current))
2126 case LEVELCLASS_TUTORIAL:
2127 strcpy(level->author, PROGRAM_AUTHOR_STRING);
2130 case LEVELCLASS_CONTRIB:
2131 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2132 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2135 case LEVELCLASS_PRIVATE:
2136 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2137 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2141 // keep default value
2148 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2150 static boolean clipboard_elements_initialized = FALSE;
2153 InitElementPropertiesStatic();
2155 li = *level; // copy level data into temporary buffer
2156 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2157 *level = li; // copy temporary buffer back to level data
2159 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2162 struct ElementInfo *ei = &element_info[element];
2164 if (element == EL_MM_GRAY_BALL)
2166 struct LevelInfo_MM *level_mm = level->native_mm_level;
2169 for (j = 0; j < level->num_mm_ball_contents; j++)
2170 level->mm_ball_content[j] =
2171 map_element_MM_to_RND(level_mm->ball_content[j]);
2174 // never initialize clipboard elements after the very first time
2175 // (to be able to use clipboard elements between several levels)
2176 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2179 if (IS_ENVELOPE(element))
2181 int envelope_nr = element - EL_ENVELOPE_1;
2183 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2185 level->envelope[envelope_nr] = xx_envelope;
2188 if (IS_CUSTOM_ELEMENT(element) ||
2189 IS_GROUP_ELEMENT(element) ||
2190 IS_INTERNAL_ELEMENT(element))
2192 xx_ei = *ei; // copy element data into temporary buffer
2194 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2199 setElementChangePages(ei, 1);
2200 setElementChangeInfoToDefaults(ei->change);
2202 if (IS_CUSTOM_ELEMENT(element) ||
2203 IS_GROUP_ELEMENT(element))
2205 setElementDescriptionToDefault(ei);
2207 ei->modified_settings = FALSE;
2210 if (IS_CUSTOM_ELEMENT(element) ||
2211 IS_INTERNAL_ELEMENT(element))
2213 // internal values used in level editor
2215 ei->access_type = 0;
2216 ei->access_layer = 0;
2217 ei->access_protected = 0;
2218 ei->walk_to_action = 0;
2219 ei->smash_targets = 0;
2222 ei->can_explode_by_fire = FALSE;
2223 ei->can_explode_smashed = FALSE;
2224 ei->can_explode_impact = FALSE;
2226 ei->current_change_page = 0;
2229 if (IS_GROUP_ELEMENT(element) ||
2230 IS_INTERNAL_ELEMENT(element))
2232 struct ElementGroupInfo *group;
2234 // initialize memory for list of elements in group
2235 if (ei->group == NULL)
2236 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2240 xx_group = *group; // copy group data into temporary buffer
2242 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2247 if (IS_EMPTY_ELEMENT(element) ||
2248 IS_INTERNAL_ELEMENT(element))
2250 xx_ei = *ei; // copy element data into temporary buffer
2252 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2258 clipboard_elements_initialized = TRUE;
2261 static void setLevelInfoToDefaults(struct LevelInfo *level,
2262 boolean level_info_only,
2263 boolean reset_file_status)
2265 setLevelInfoToDefaults_Level(level);
2267 if (!level_info_only)
2268 setLevelInfoToDefaults_Elements(level);
2270 if (reset_file_status)
2272 level->no_valid_file = FALSE;
2273 level->no_level_file = FALSE;
2276 level->changed = FALSE;
2279 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2281 level_file_info->nr = 0;
2282 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2283 level_file_info->packed = FALSE;
2285 setString(&level_file_info->basename, NULL);
2286 setString(&level_file_info->filename, NULL);
2289 int getMappedElement_SB(int, boolean);
2291 static void ActivateLevelTemplate(void)
2295 if (check_special_flags("load_xsb_to_ces"))
2297 // fill smaller playfields with padding "beyond border wall" elements
2298 if (level.fieldx < level_template.fieldx ||
2299 level.fieldy < level_template.fieldy)
2301 short field[level.fieldx][level.fieldy];
2302 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2303 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2304 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2305 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2307 // copy old playfield (which is smaller than the visible area)
2308 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2309 field[x][y] = level.field[x][y];
2311 // fill new, larger playfield with "beyond border wall" elements
2312 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2313 level.field[x][y] = getMappedElement_SB('_', TRUE);
2315 // copy the old playfield to the middle of the new playfield
2316 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2317 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2319 level.fieldx = new_fieldx;
2320 level.fieldy = new_fieldy;
2324 // Currently there is no special action needed to activate the template
2325 // data, because 'element_info' property settings overwrite the original
2326 // level data, while all other variables do not change.
2328 // Exception: 'from_level_template' elements in the original level playfield
2329 // are overwritten with the corresponding elements at the same position in
2330 // playfield from the level template.
2332 for (x = 0; x < level.fieldx; x++)
2333 for (y = 0; y < level.fieldy; y++)
2334 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2335 level.field[x][y] = level_template.field[x][y];
2337 if (check_special_flags("load_xsb_to_ces"))
2339 struct LevelInfo level_backup = level;
2341 // overwrite all individual level settings from template level settings
2342 level = level_template;
2344 // restore level file info
2345 level.file_info = level_backup.file_info;
2347 // restore playfield size
2348 level.fieldx = level_backup.fieldx;
2349 level.fieldy = level_backup.fieldy;
2351 // restore playfield content
2352 for (x = 0; x < level.fieldx; x++)
2353 for (y = 0; y < level.fieldy; y++)
2354 level.field[x][y] = level_backup.field[x][y];
2356 // restore name and author from individual level
2357 strcpy(level.name, level_backup.name);
2358 strcpy(level.author, level_backup.author);
2360 // restore flag "use_custom_template"
2361 level.use_custom_template = level_backup.use_custom_template;
2365 static boolean checkForPackageFromBasename_BD(char *basename)
2367 // check for native BD level file extensions
2368 if (!strSuffixLower(basename, ".bd") &&
2369 !strSuffixLower(basename, ".bdr") &&
2370 !strSuffixLower(basename, ".brc") &&
2371 !strSuffixLower(basename, ".gds"))
2374 // check for standard single-level BD files (like "001.bd")
2375 if (strSuffixLower(basename, ".bd") &&
2376 strlen(basename) == 6 &&
2377 basename[0] >= '0' && basename[0] <= '9' &&
2378 basename[1] >= '0' && basename[1] <= '9' &&
2379 basename[2] >= '0' && basename[2] <= '9')
2382 // this is a level package in native BD file format
2386 static char *getLevelFilenameFromBasename(char *basename)
2388 static char *filename = NULL;
2390 checked_free(filename);
2392 filename = getPath2(getCurrentLevelDir(), basename);
2397 static int getFileTypeFromBasename(char *basename)
2399 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2401 static char *filename = NULL;
2402 struct stat file_status;
2404 // ---------- try to determine file type from filename ----------
2406 // check for typical filename of a Supaplex level package file
2407 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2408 return LEVEL_FILE_TYPE_SP;
2410 // check for typical filename of a Diamond Caves II level package file
2411 if (strSuffixLower(basename, ".dc") ||
2412 strSuffixLower(basename, ".dc2"))
2413 return LEVEL_FILE_TYPE_DC;
2415 // check for typical filename of a Sokoban level package file
2416 if (strSuffixLower(basename, ".xsb") &&
2417 strchr(basename, '%') == NULL)
2418 return LEVEL_FILE_TYPE_SB;
2420 // check for typical filename of a Boulder Dash (GDash) level package file
2421 if (checkForPackageFromBasename_BD(basename))
2422 return LEVEL_FILE_TYPE_BD;
2424 // ---------- try to determine file type from filesize ----------
2426 checked_free(filename);
2427 filename = getPath2(getCurrentLevelDir(), basename);
2429 if (stat(filename, &file_status) == 0)
2431 // check for typical filesize of a Supaplex level package file
2432 if (file_status.st_size == 170496)
2433 return LEVEL_FILE_TYPE_SP;
2436 return LEVEL_FILE_TYPE_UNKNOWN;
2439 static int getFileTypeFromMagicBytes(char *filename, int type)
2443 if ((file = openFile(filename, MODE_READ)))
2445 char chunk_name[CHUNK_ID_LEN + 1];
2447 getFileChunkBE(file, chunk_name, NULL);
2449 if (strEqual(chunk_name, "MMII") ||
2450 strEqual(chunk_name, "MIRR"))
2451 type = LEVEL_FILE_TYPE_MM;
2459 static boolean checkForPackageFromBasename(char *basename)
2461 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2462 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2464 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2467 static char *getSingleLevelBasenameExt(int nr, char *extension)
2469 static char basename[MAX_FILENAME_LEN];
2472 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2474 sprintf(basename, "%03d.%s", nr, extension);
2479 static char *getSingleLevelBasename(int nr)
2481 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2484 static char *getPackedLevelBasename(int type)
2486 static char basename[MAX_FILENAME_LEN];
2487 char *directory = getCurrentLevelDir();
2489 DirectoryEntry *dir_entry;
2491 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2493 if ((dir = openDirectory(directory)) == NULL)
2495 Warn("cannot read current level directory '%s'", directory);
2500 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2502 char *entry_basename = dir_entry->basename;
2503 int entry_type = getFileTypeFromBasename(entry_basename);
2505 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2507 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2510 strcpy(basename, entry_basename);
2517 closeDirectory(dir);
2522 static char *getSingleLevelFilename(int nr)
2524 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2527 #if ENABLE_UNUSED_CODE
2528 static char *getPackedLevelFilename(int type)
2530 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2534 char *getDefaultLevelFilename(int nr)
2536 return getSingleLevelFilename(nr);
2539 #if ENABLE_UNUSED_CODE
2540 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2544 lfi->packed = FALSE;
2546 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2547 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2551 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2552 int type, char *format, ...)
2554 static char basename[MAX_FILENAME_LEN];
2557 va_start(ap, format);
2558 vsprintf(basename, format, ap);
2562 lfi->packed = FALSE;
2564 setString(&lfi->basename, basename);
2565 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2568 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2574 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2575 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2578 static int getFiletypeFromID(char *filetype_id)
2580 char *filetype_id_lower;
2581 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2584 if (filetype_id == NULL)
2585 return LEVEL_FILE_TYPE_UNKNOWN;
2587 filetype_id_lower = getStringToLower(filetype_id);
2589 for (i = 0; filetype_id_list[i].id != NULL; i++)
2591 char *id_lower = getStringToLower(filetype_id_list[i].id);
2593 if (strEqual(filetype_id_lower, id_lower))
2594 filetype = filetype_id_list[i].filetype;
2598 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2602 free(filetype_id_lower);
2607 char *getLocalLevelTemplateFilename(void)
2609 return getDefaultLevelFilename(-1);
2612 char *getGlobalLevelTemplateFilename(void)
2614 // global variable "leveldir_current" must be modified in the loop below
2615 LevelDirTree *leveldir_current_last = leveldir_current;
2616 char *filename = NULL;
2618 // check for template level in path from current to topmost tree node
2620 while (leveldir_current != NULL)
2622 filename = getDefaultLevelFilename(-1);
2624 if (fileExists(filename))
2627 leveldir_current = leveldir_current->node_parent;
2630 // restore global variable "leveldir_current" modified in above loop
2631 leveldir_current = leveldir_current_last;
2636 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2640 // special case: level number is negative => check for level template file
2643 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2644 getSingleLevelBasename(-1));
2646 // replace local level template filename with global template filename
2647 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2649 // no fallback if template file not existing
2653 // special case: check for file name/pattern specified in "levelinfo.conf"
2654 if (leveldir_current->level_filename != NULL)
2656 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2658 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2659 leveldir_current->level_filename, nr);
2661 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2663 if (fileExists(lfi->filename))
2666 else if (leveldir_current->level_filetype != NULL)
2668 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2670 // check for specified native level file with standard file name
2671 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2672 "%03d.%s", nr, LEVELFILE_EXTENSION);
2673 if (fileExists(lfi->filename))
2677 // check for native Rocks'n'Diamonds level file
2678 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2679 "%03d.%s", nr, LEVELFILE_EXTENSION);
2680 if (fileExists(lfi->filename))
2683 // check for native Boulder Dash level file
2684 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2685 if (fileExists(lfi->filename))
2688 // check for Emerald Mine level file (V1)
2689 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2690 'a' + (nr / 10) % 26, '0' + nr % 10);
2691 if (fileExists(lfi->filename))
2693 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2694 'A' + (nr / 10) % 26, '0' + nr % 10);
2695 if (fileExists(lfi->filename))
2698 // check for Emerald Mine level file (V2 to V5)
2699 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2700 if (fileExists(lfi->filename))
2703 // check for Emerald Mine level file (V6 / single mode)
2704 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2705 if (fileExists(lfi->filename))
2707 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2708 if (fileExists(lfi->filename))
2711 // check for Emerald Mine level file (V6 / teamwork mode)
2712 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2713 if (fileExists(lfi->filename))
2715 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2716 if (fileExists(lfi->filename))
2719 // check for various packed level file formats
2720 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2721 if (fileExists(lfi->filename))
2724 // no known level file found -- use default values (and fail later)
2725 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2726 "%03d.%s", nr, LEVELFILE_EXTENSION);
2729 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2731 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2732 lfi->type = getFileTypeFromBasename(lfi->basename);
2734 if (lfi->type == LEVEL_FILE_TYPE_RND)
2735 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2738 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2740 // always start with reliable default values
2741 setFileInfoToDefaults(level_file_info);
2743 level_file_info->nr = nr; // set requested level number
2745 determineLevelFileInfo_Filename(level_file_info);
2746 determineLevelFileInfo_Filetype(level_file_info);
2749 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2750 struct LevelFileInfo *lfi_to)
2752 lfi_to->nr = lfi_from->nr;
2753 lfi_to->type = lfi_from->type;
2754 lfi_to->packed = lfi_from->packed;
2756 setString(&lfi_to->basename, lfi_from->basename);
2757 setString(&lfi_to->filename, lfi_from->filename);
2760 // ----------------------------------------------------------------------------
2761 // functions for loading R'n'D level
2762 // ----------------------------------------------------------------------------
2764 int getMappedElement(int element)
2766 // remap some (historic, now obsolete) elements
2770 case EL_PLAYER_OBSOLETE:
2771 element = EL_PLAYER_1;
2774 case EL_KEY_OBSOLETE:
2778 case EL_EM_KEY_1_FILE_OBSOLETE:
2779 element = EL_EM_KEY_1;
2782 case EL_EM_KEY_2_FILE_OBSOLETE:
2783 element = EL_EM_KEY_2;
2786 case EL_EM_KEY_3_FILE_OBSOLETE:
2787 element = EL_EM_KEY_3;
2790 case EL_EM_KEY_4_FILE_OBSOLETE:
2791 element = EL_EM_KEY_4;
2794 case EL_ENVELOPE_OBSOLETE:
2795 element = EL_ENVELOPE_1;
2803 if (element >= NUM_FILE_ELEMENTS)
2805 Warn("invalid level element %d", element);
2807 element = EL_UNKNOWN;
2815 static int getMappedElementByVersion(int element, int game_version)
2817 // remap some elements due to certain game version
2819 if (game_version <= VERSION_IDENT(2,2,0,0))
2821 // map game font elements
2822 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2823 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2824 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2825 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2828 if (game_version < VERSION_IDENT(3,0,0,0))
2830 // map Supaplex gravity tube elements
2831 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2832 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2833 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2834 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2841 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2843 level->file_version = getFileVersion(file);
2844 level->game_version = getFileVersion(file);
2849 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2851 level->creation_date.year = getFile16BitBE(file);
2852 level->creation_date.month = getFile8Bit(file);
2853 level->creation_date.day = getFile8Bit(file);
2855 level->creation_date.src = DATE_SRC_LEVELFILE;
2860 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2862 int initial_player_stepsize;
2863 int initial_player_gravity;
2866 level->fieldx = getFile8Bit(file);
2867 level->fieldy = getFile8Bit(file);
2869 level->time = getFile16BitBE(file);
2870 level->gems_needed = getFile16BitBE(file);
2872 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2873 level->name[i] = getFile8Bit(file);
2874 level->name[MAX_LEVEL_NAME_LEN] = 0;
2876 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2877 level->score[i] = getFile8Bit(file);
2879 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2880 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2881 for (y = 0; y < 3; y++)
2882 for (x = 0; x < 3; x++)
2883 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2885 level->amoeba_speed = getFile8Bit(file);
2886 level->time_magic_wall = getFile8Bit(file);
2887 level->time_wheel = getFile8Bit(file);
2888 level->amoeba_content = getMappedElement(getFile8Bit(file));
2890 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2893 for (i = 0; i < MAX_PLAYERS; i++)
2894 level->initial_player_stepsize[i] = initial_player_stepsize;
2896 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2898 for (i = 0; i < MAX_PLAYERS; i++)
2899 level->initial_player_gravity[i] = initial_player_gravity;
2901 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2902 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2904 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2906 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2907 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2908 level->can_move_into_acid_bits = getFile32BitBE(file);
2909 level->dont_collide_with_bits = getFile8Bit(file);
2911 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2912 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2914 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2915 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2916 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2918 level->game_engine_type = getFile8Bit(file);
2920 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2925 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2929 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2930 level->name[i] = getFile8Bit(file);
2931 level->name[MAX_LEVEL_NAME_LEN] = 0;
2936 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2940 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2941 level->author[i] = getFile8Bit(file);
2942 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2947 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2950 int chunk_size_expected = level->fieldx * level->fieldy;
2952 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2953 stored with 16-bit encoding (and should be twice as big then).
2954 Even worse, playfield data was stored 16-bit when only yamyam content
2955 contained 16-bit elements and vice versa. */
2957 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2958 chunk_size_expected *= 2;
2960 if (chunk_size_expected != chunk_size)
2962 ReadUnusedBytesFromFile(file, chunk_size);
2963 return chunk_size_expected;
2966 for (y = 0; y < level->fieldy; y++)
2967 for (x = 0; x < level->fieldx; x++)
2968 level->field[x][y] =
2969 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2974 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2977 int header_size = 4;
2978 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2979 int chunk_size_expected = header_size + content_size;
2981 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2982 stored with 16-bit encoding (and should be twice as big then).
2983 Even worse, playfield data was stored 16-bit when only yamyam content
2984 contained 16-bit elements and vice versa. */
2986 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2987 chunk_size_expected += content_size;
2989 if (chunk_size_expected != chunk_size)
2991 ReadUnusedBytesFromFile(file, chunk_size);
2992 return chunk_size_expected;
2996 level->num_yamyam_contents = getFile8Bit(file);
3000 // correct invalid number of content fields -- should never happen
3001 if (level->num_yamyam_contents < 1 ||
3002 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3003 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3005 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3006 for (y = 0; y < 3; y++)
3007 for (x = 0; x < 3; x++)
3008 level->yamyam_content[i].e[x][y] =
3009 getMappedElement(level->encoding_16bit_field ?
3010 getFile16BitBE(file) : getFile8Bit(file));
3014 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3019 int content_array[MAX_ELEMENT_CONTENTS][3][3];
3021 element = getMappedElement(getFile16BitBE(file));
3022 num_contents = getFile8Bit(file);
3024 getFile8Bit(file); // content x size (unused)
3025 getFile8Bit(file); // content y size (unused)
3027 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3029 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3030 for (y = 0; y < 3; y++)
3031 for (x = 0; x < 3; x++)
3032 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3034 // correct invalid number of content fields -- should never happen
3035 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3036 num_contents = STD_ELEMENT_CONTENTS;
3038 if (element == EL_YAMYAM)
3040 level->num_yamyam_contents = num_contents;
3042 for (i = 0; i < num_contents; i++)
3043 for (y = 0; y < 3; y++)
3044 for (x = 0; x < 3; x++)
3045 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3047 else if (element == EL_BD_AMOEBA)
3049 level->amoeba_content = content_array[0][0][0];
3053 Warn("cannot load content for element '%d'", element);
3059 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3065 int chunk_size_expected;
3067 element = getMappedElement(getFile16BitBE(file));
3068 if (!IS_ENVELOPE(element))
3069 element = EL_ENVELOPE_1;
3071 envelope_nr = element - EL_ENVELOPE_1;
3073 envelope_len = getFile16BitBE(file);
3075 level->envelope[envelope_nr].xsize = getFile8Bit(file);
3076 level->envelope[envelope_nr].ysize = getFile8Bit(file);
3078 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3080 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3081 if (chunk_size_expected != chunk_size)
3083 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3084 return chunk_size_expected;
3087 for (i = 0; i < envelope_len; i++)
3088 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3093 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3095 int num_changed_custom_elements = getFile16BitBE(file);
3096 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3099 if (chunk_size_expected != chunk_size)
3101 ReadUnusedBytesFromFile(file, chunk_size - 2);
3102 return chunk_size_expected;
3105 for (i = 0; i < num_changed_custom_elements; i++)
3107 int element = getMappedElement(getFile16BitBE(file));
3108 int properties = getFile32BitBE(file);
3110 if (IS_CUSTOM_ELEMENT(element))
3111 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3113 Warn("invalid custom element number %d", element);
3115 // older game versions that wrote level files with CUS1 chunks used
3116 // different default push delay values (not yet stored in level file)
3117 element_info[element].push_delay_fixed = 2;
3118 element_info[element].push_delay_random = 8;
3121 level->file_has_custom_elements = TRUE;
3126 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3128 int num_changed_custom_elements = getFile16BitBE(file);
3129 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3132 if (chunk_size_expected != chunk_size)
3134 ReadUnusedBytesFromFile(file, chunk_size - 2);
3135 return chunk_size_expected;
3138 for (i = 0; i < num_changed_custom_elements; i++)
3140 int element = getMappedElement(getFile16BitBE(file));
3141 int custom_target_element = getMappedElement(getFile16BitBE(file));
3143 if (IS_CUSTOM_ELEMENT(element))
3144 element_info[element].change->target_element = custom_target_element;
3146 Warn("invalid custom element number %d", element);
3149 level->file_has_custom_elements = TRUE;
3154 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3156 int num_changed_custom_elements = getFile16BitBE(file);
3157 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3160 if (chunk_size_expected != chunk_size)
3162 ReadUnusedBytesFromFile(file, chunk_size - 2);
3163 return chunk_size_expected;
3166 for (i = 0; i < num_changed_custom_elements; i++)
3168 int element = getMappedElement(getFile16BitBE(file));
3169 struct ElementInfo *ei = &element_info[element];
3170 unsigned int event_bits;
3172 if (!IS_CUSTOM_ELEMENT(element))
3174 Warn("invalid custom element number %d", element);
3176 element = EL_INTERNAL_DUMMY;
3179 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3180 ei->description[j] = getFile8Bit(file);
3181 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3183 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3185 // some free bytes for future properties and padding
3186 ReadUnusedBytesFromFile(file, 7);
3188 ei->use_gfx_element = getFile8Bit(file);
3189 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3191 ei->collect_score_initial = getFile8Bit(file);
3192 ei->collect_count_initial = getFile8Bit(file);
3194 ei->push_delay_fixed = getFile16BitBE(file);
3195 ei->push_delay_random = getFile16BitBE(file);
3196 ei->move_delay_fixed = getFile16BitBE(file);
3197 ei->move_delay_random = getFile16BitBE(file);
3199 ei->move_pattern = getFile16BitBE(file);
3200 ei->move_direction_initial = getFile8Bit(file);
3201 ei->move_stepsize = getFile8Bit(file);
3203 for (y = 0; y < 3; y++)
3204 for (x = 0; x < 3; x++)
3205 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3207 // bits 0 - 31 of "has_event[]"
3208 event_bits = getFile32BitBE(file);
3209 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3210 if (event_bits & (1u << j))
3211 ei->change->has_event[j] = TRUE;
3213 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3215 ei->change->delay_fixed = getFile16BitBE(file);
3216 ei->change->delay_random = getFile16BitBE(file);
3217 ei->change->delay_frames = getFile16BitBE(file);
3219 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3221 ei->change->explode = getFile8Bit(file);
3222 ei->change->use_target_content = getFile8Bit(file);
3223 ei->change->only_if_complete = getFile8Bit(file);
3224 ei->change->use_random_replace = getFile8Bit(file);
3226 ei->change->random_percentage = getFile8Bit(file);
3227 ei->change->replace_when = getFile8Bit(file);
3229 for (y = 0; y < 3; y++)
3230 for (x = 0; x < 3; x++)
3231 ei->change->target_content.e[x][y] =
3232 getMappedElement(getFile16BitBE(file));
3234 ei->slippery_type = getFile8Bit(file);
3236 // some free bytes for future properties and padding
3237 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3239 // mark that this custom element has been modified
3240 ei->modified_settings = TRUE;
3243 level->file_has_custom_elements = TRUE;
3248 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3250 struct ElementInfo *ei;
3251 int chunk_size_expected;
3255 // ---------- custom element base property values (96 bytes) ----------------
3257 element = getMappedElement(getFile16BitBE(file));
3259 if (!IS_CUSTOM_ELEMENT(element))
3261 Warn("invalid custom element number %d", element);
3263 ReadUnusedBytesFromFile(file, chunk_size - 2);
3268 ei = &element_info[element];
3270 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3271 ei->description[i] = getFile8Bit(file);
3272 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3274 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3276 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3278 ei->num_change_pages = getFile8Bit(file);
3280 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3281 if (chunk_size_expected != chunk_size)
3283 ReadUnusedBytesFromFile(file, chunk_size - 43);
3284 return chunk_size_expected;
3287 ei->ce_value_fixed_initial = getFile16BitBE(file);
3288 ei->ce_value_random_initial = getFile16BitBE(file);
3289 ei->use_last_ce_value = getFile8Bit(file);
3291 ei->use_gfx_element = getFile8Bit(file);
3292 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3294 ei->collect_score_initial = getFile8Bit(file);
3295 ei->collect_count_initial = getFile8Bit(file);
3297 ei->drop_delay_fixed = getFile8Bit(file);
3298 ei->push_delay_fixed = getFile8Bit(file);
3299 ei->drop_delay_random = getFile8Bit(file);
3300 ei->push_delay_random = getFile8Bit(file);
3301 ei->move_delay_fixed = getFile16BitBE(file);
3302 ei->move_delay_random = getFile16BitBE(file);
3304 // bits 0 - 15 of "move_pattern" ...
3305 ei->move_pattern = getFile16BitBE(file);
3306 ei->move_direction_initial = getFile8Bit(file);
3307 ei->move_stepsize = getFile8Bit(file);
3309 ei->slippery_type = getFile8Bit(file);
3311 for (y = 0; y < 3; y++)
3312 for (x = 0; x < 3; x++)
3313 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3315 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3316 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3317 ei->move_leave_type = getFile8Bit(file);
3319 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3320 ei->move_pattern |= (getFile16BitBE(file) << 16);
3322 ei->access_direction = getFile8Bit(file);
3324 ei->explosion_delay = getFile8Bit(file);
3325 ei->ignition_delay = getFile8Bit(file);
3326 ei->explosion_type = getFile8Bit(file);
3328 // some free bytes for future custom property values and padding
3329 ReadUnusedBytesFromFile(file, 1);
3331 // ---------- change page property values (48 bytes) ------------------------
3333 setElementChangePages(ei, ei->num_change_pages);
3335 for (i = 0; i < ei->num_change_pages; i++)
3337 struct ElementChangeInfo *change = &ei->change_page[i];
3338 unsigned int event_bits;
3340 // always start with reliable default values
3341 setElementChangeInfoToDefaults(change);
3343 // bits 0 - 31 of "has_event[]" ...
3344 event_bits = getFile32BitBE(file);
3345 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3346 if (event_bits & (1u << j))
3347 change->has_event[j] = TRUE;
3349 change->target_element = getMappedElement(getFile16BitBE(file));
3351 change->delay_fixed = getFile16BitBE(file);
3352 change->delay_random = getFile16BitBE(file);
3353 change->delay_frames = getFile16BitBE(file);
3355 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3357 change->explode = getFile8Bit(file);
3358 change->use_target_content = getFile8Bit(file);
3359 change->only_if_complete = getFile8Bit(file);
3360 change->use_random_replace = getFile8Bit(file);
3362 change->random_percentage = getFile8Bit(file);
3363 change->replace_when = getFile8Bit(file);
3365 for (y = 0; y < 3; y++)
3366 for (x = 0; x < 3; x++)
3367 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3369 change->can_change = getFile8Bit(file);
3371 change->trigger_side = getFile8Bit(file);
3373 change->trigger_player = getFile8Bit(file);
3374 change->trigger_page = getFile8Bit(file);
3376 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3377 CH_PAGE_ANY : (1 << change->trigger_page));
3379 change->has_action = getFile8Bit(file);
3380 change->action_type = getFile8Bit(file);
3381 change->action_mode = getFile8Bit(file);
3382 change->action_arg = getFile16BitBE(file);
3384 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3385 event_bits = getFile8Bit(file);
3386 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3387 if (event_bits & (1u << (j - 32)))
3388 change->has_event[j] = TRUE;
3391 // mark this custom element as modified
3392 ei->modified_settings = TRUE;
3394 level->file_has_custom_elements = TRUE;
3399 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3401 struct ElementInfo *ei;
3402 struct ElementGroupInfo *group;
3406 element = getMappedElement(getFile16BitBE(file));
3408 if (!IS_GROUP_ELEMENT(element))
3410 Warn("invalid group element number %d", element);
3412 ReadUnusedBytesFromFile(file, chunk_size - 2);
3417 ei = &element_info[element];
3419 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3420 ei->description[i] = getFile8Bit(file);
3421 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3423 group = element_info[element].group;
3425 group->num_elements = getFile8Bit(file);
3427 ei->use_gfx_element = getFile8Bit(file);
3428 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3430 group->choice_mode = getFile8Bit(file);
3432 // some free bytes for future values and padding
3433 ReadUnusedBytesFromFile(file, 3);
3435 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3436 group->element[i] = getMappedElement(getFile16BitBE(file));
3438 // mark this group element as modified
3439 element_info[element].modified_settings = TRUE;
3441 level->file_has_custom_elements = TRUE;
3446 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3447 int element, int real_element)
3449 int micro_chunk_size = 0;
3450 int conf_type = getFile8Bit(file);
3451 int byte_mask = conf_type & CONF_MASK_BYTES;
3452 boolean element_found = FALSE;
3455 micro_chunk_size += 1;
3457 if (byte_mask == CONF_MASK_MULTI_BYTES)
3459 int num_bytes = getFile16BitBE(file);
3460 byte *buffer = checked_malloc(num_bytes);
3462 ReadBytesFromFile(file, buffer, num_bytes);
3464 for (i = 0; conf[i].data_type != -1; i++)
3466 if (conf[i].element == element &&
3467 conf[i].conf_type == conf_type)
3469 int data_type = conf[i].data_type;
3470 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3471 int max_num_entities = conf[i].max_num_entities;
3473 if (num_entities > max_num_entities)
3475 Warn("truncating number of entities for element %d from %d to %d",
3476 element, num_entities, max_num_entities);
3478 num_entities = max_num_entities;
3481 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3482 data_type == TYPE_CONTENT_LIST))
3484 // for element and content lists, zero entities are not allowed
3485 Warn("found empty list of entities for element %d", element);
3487 // do not set "num_entities" here to prevent reading behind buffer
3489 *(int *)(conf[i].num_entities) = 1; // at least one is required
3493 *(int *)(conf[i].num_entities) = num_entities;
3496 element_found = TRUE;
3498 if (data_type == TYPE_STRING)
3500 char *string = (char *)(conf[i].value);
3503 for (j = 0; j < max_num_entities; j++)
3504 string[j] = (j < num_entities ? buffer[j] : '\0');
3506 else if (data_type == TYPE_ELEMENT_LIST)
3508 int *element_array = (int *)(conf[i].value);
3511 for (j = 0; j < num_entities; j++)
3513 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3515 else if (data_type == TYPE_CONTENT_LIST)
3517 struct Content *content= (struct Content *)(conf[i].value);
3520 for (c = 0; c < num_entities; c++)
3521 for (y = 0; y < 3; y++)
3522 for (x = 0; x < 3; x++)
3523 content[c].e[x][y] =
3524 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3527 element_found = FALSE;
3533 checked_free(buffer);
3535 micro_chunk_size += 2 + num_bytes;
3537 else // constant size configuration data (1, 2 or 4 bytes)
3539 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3540 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3541 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3543 for (i = 0; conf[i].data_type != -1; i++)
3545 if (conf[i].element == element &&
3546 conf[i].conf_type == conf_type)
3548 int data_type = conf[i].data_type;
3550 if (data_type == TYPE_ELEMENT)
3551 value = getMappedElement(value);
3553 if (data_type == TYPE_BOOLEAN)
3554 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3556 *(int *) (conf[i].value) = value;
3558 element_found = TRUE;
3564 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3569 char *error_conf_chunk_bytes =
3570 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3571 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3572 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3573 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3574 int error_element = real_element;
3576 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3577 error_conf_chunk_bytes, error_conf_chunk_token,
3578 error_element, EL_NAME(error_element));
3581 return micro_chunk_size;
3584 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3586 int real_chunk_size = 0;
3588 li = *level; // copy level data into temporary buffer
3590 while (!checkEndOfFile(file))
3592 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3594 if (real_chunk_size >= chunk_size)
3598 *level = li; // copy temporary buffer back to level data
3600 return real_chunk_size;
3603 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3605 int real_chunk_size = 0;
3607 li = *level; // copy level data into temporary buffer
3609 while (!checkEndOfFile(file))
3611 int element = getMappedElement(getFile16BitBE(file));
3613 real_chunk_size += 2;
3614 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3616 if (real_chunk_size >= chunk_size)
3620 *level = li; // copy temporary buffer back to level data
3622 return real_chunk_size;
3625 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3627 int real_chunk_size = 0;
3629 li = *level; // copy level data into temporary buffer
3631 while (!checkEndOfFile(file))
3633 int element = getMappedElement(getFile16BitBE(file));
3635 real_chunk_size += 2;
3636 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3638 if (real_chunk_size >= chunk_size)
3642 *level = li; // copy temporary buffer back to level data
3644 return real_chunk_size;
3647 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3649 int element = getMappedElement(getFile16BitBE(file));
3650 int envelope_nr = element - EL_ENVELOPE_1;
3651 int real_chunk_size = 2;
3653 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3655 while (!checkEndOfFile(file))
3657 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3660 if (real_chunk_size >= chunk_size)
3664 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3666 return real_chunk_size;
3669 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3671 int element = getMappedElement(getFile16BitBE(file));
3672 int real_chunk_size = 2;
3673 struct ElementInfo *ei = &element_info[element];
3676 xx_ei = *ei; // copy element data into temporary buffer
3678 xx_ei.num_change_pages = -1;
3680 while (!checkEndOfFile(file))
3682 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3684 if (xx_ei.num_change_pages != -1)
3687 if (real_chunk_size >= chunk_size)
3693 if (ei->num_change_pages == -1)
3695 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3698 ei->num_change_pages = 1;
3700 setElementChangePages(ei, 1);
3701 setElementChangeInfoToDefaults(ei->change);
3703 return real_chunk_size;
3706 // initialize number of change pages stored for this custom element
3707 setElementChangePages(ei, ei->num_change_pages);
3708 for (i = 0; i < ei->num_change_pages; i++)
3709 setElementChangeInfoToDefaults(&ei->change_page[i]);
3711 // start with reading properties for the first change page
3712 xx_current_change_page = 0;
3714 while (!checkEndOfFile(file))
3716 // level file might contain invalid change page number
3717 if (xx_current_change_page >= ei->num_change_pages)
3720 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3722 xx_change = *change; // copy change data into temporary buffer
3724 resetEventBits(); // reset bits; change page might have changed
3726 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3729 *change = xx_change;
3731 setEventFlagsFromEventBits(change);
3733 if (real_chunk_size >= chunk_size)
3737 level->file_has_custom_elements = TRUE;
3739 return real_chunk_size;
3742 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3744 int element = getMappedElement(getFile16BitBE(file));
3745 int real_chunk_size = 2;
3746 struct ElementInfo *ei = &element_info[element];
3747 struct ElementGroupInfo *group = ei->group;
3752 xx_ei = *ei; // copy element data into temporary buffer
3753 xx_group = *group; // copy group data into temporary buffer
3755 while (!checkEndOfFile(file))
3757 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3760 if (real_chunk_size >= chunk_size)
3767 level->file_has_custom_elements = TRUE;
3769 return real_chunk_size;
3772 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3774 int element = getMappedElement(getFile16BitBE(file));
3775 int real_chunk_size = 2;
3776 struct ElementInfo *ei = &element_info[element];
3778 xx_ei = *ei; // copy element data into temporary buffer
3780 while (!checkEndOfFile(file))
3782 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3785 if (real_chunk_size >= chunk_size)
3791 level->file_has_custom_elements = TRUE;
3793 return real_chunk_size;
3796 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3797 struct LevelFileInfo *level_file_info,
3798 boolean level_info_only)
3800 char *filename = level_file_info->filename;
3801 char cookie[MAX_LINE_LEN];
3802 char chunk_name[CHUNK_ID_LEN + 1];
3806 if (!(file = openFile(filename, MODE_READ)))
3808 level->no_valid_file = TRUE;
3809 level->no_level_file = TRUE;
3811 if (level_info_only)
3814 Warn("cannot read level '%s' -- using empty level", filename);
3816 if (!setup.editor.use_template_for_new_levels)
3819 // if level file not found, try to initialize level data from template
3820 filename = getGlobalLevelTemplateFilename();
3822 if (!(file = openFile(filename, MODE_READ)))
3825 // default: for empty levels, use level template for custom elements
3826 level->use_custom_template = TRUE;
3828 level->no_valid_file = FALSE;
3831 getFileChunkBE(file, chunk_name, NULL);
3832 if (strEqual(chunk_name, "RND1"))
3834 getFile32BitBE(file); // not used
3836 getFileChunkBE(file, chunk_name, NULL);
3837 if (!strEqual(chunk_name, "CAVE"))
3839 level->no_valid_file = TRUE;
3841 Warn("unknown format of level file '%s'", filename);
3848 else // check for pre-2.0 file format with cookie string
3850 strcpy(cookie, chunk_name);
3851 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3853 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3854 cookie[strlen(cookie) - 1] = '\0';
3856 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3858 level->no_valid_file = TRUE;
3860 Warn("unknown format of level file '%s'", filename);
3867 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3869 level->no_valid_file = TRUE;
3871 Warn("unsupported version of level file '%s'", filename);
3878 // pre-2.0 level files have no game version, so use file version here
3879 level->game_version = level->file_version;
3882 if (level->file_version < FILE_VERSION_1_2)
3884 // level files from versions before 1.2.0 without chunk structure
3885 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3886 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3894 int (*loader)(File *, int, struct LevelInfo *);
3898 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3899 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3900 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3901 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3902 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3903 { "INFO", -1, LoadLevel_INFO },
3904 { "BODY", -1, LoadLevel_BODY },
3905 { "CONT", -1, LoadLevel_CONT },
3906 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3907 { "CNT3", -1, LoadLevel_CNT3 },
3908 { "CUS1", -1, LoadLevel_CUS1 },
3909 { "CUS2", -1, LoadLevel_CUS2 },
3910 { "CUS3", -1, LoadLevel_CUS3 },
3911 { "CUS4", -1, LoadLevel_CUS4 },
3912 { "GRP1", -1, LoadLevel_GRP1 },
3913 { "CONF", -1, LoadLevel_CONF },
3914 { "ELEM", -1, LoadLevel_ELEM },
3915 { "NOTE", -1, LoadLevel_NOTE },
3916 { "CUSX", -1, LoadLevel_CUSX },
3917 { "GRPX", -1, LoadLevel_GRPX },
3918 { "EMPX", -1, LoadLevel_EMPX },
3923 while (getFileChunkBE(file, chunk_name, &chunk_size))
3927 while (chunk_info[i].name != NULL &&
3928 !strEqual(chunk_name, chunk_info[i].name))
3931 if (chunk_info[i].name == NULL)
3933 Warn("unknown chunk '%s' in level file '%s'",
3934 chunk_name, filename);
3936 ReadUnusedBytesFromFile(file, chunk_size);
3938 else if (chunk_info[i].size != -1 &&
3939 chunk_info[i].size != chunk_size)
3941 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3942 chunk_size, chunk_name, filename);
3944 ReadUnusedBytesFromFile(file, chunk_size);
3948 // call function to load this level chunk
3949 int chunk_size_expected =
3950 (chunk_info[i].loader)(file, chunk_size, level);
3952 if (chunk_size_expected < 0)
3954 Warn("error reading chunk '%s' in level file '%s'",
3955 chunk_name, filename);
3960 // the size of some chunks cannot be checked before reading other
3961 // chunks first (like "HEAD" and "BODY") that contain some header
3962 // information, so check them here
3963 if (chunk_size_expected != chunk_size)
3965 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3966 chunk_size, chunk_name, filename);
3978 // ----------------------------------------------------------------------------
3979 // functions for loading BD level
3980 // ----------------------------------------------------------------------------
3982 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3984 struct LevelInfo_BD *level_bd = level->native_bd_level;
3985 GdCave *cave = NULL; // will be changed below
3986 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3987 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3990 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3992 // cave and map newly allocated when set to defaults above
3993 cave = level_bd->cave;
3996 cave->intermission = level->bd_intermission;
3999 cave->level_time[0] = level->time;
4000 cave->level_diamonds[0] = level->gems_needed;
4003 cave->scheduling = level->bd_scheduling_type;
4004 cave->pal_timing = level->bd_pal_timing;
4005 cave->level_speed[0] = level->bd_cycle_delay_ms;
4006 cave->level_ckdelay[0] = level->bd_cycle_delay_c64;
4007 cave->level_hatching_delay_frame[0] = level->bd_hatching_delay_cycles;
4008 cave->level_hatching_delay_time[0] = level->bd_hatching_delay_seconds;
4011 cave->level_timevalue[0] = level->score[SC_TIME_BONUS];
4012 cave->diamond_value = level->score[SC_EMERALD];
4013 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
4015 // compatibility settings
4016 cave->lineshift = level->bd_line_shifting_borders;
4017 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
4018 cave->short_explosions = level->bd_short_explosions;
4019 cave->gravity_affects_all = level->bd_gravity_affects_all;
4021 // player properties
4022 cave->diagonal_movements = level->bd_diagonal_movements;
4023 cave->active_is_first_found = level->bd_topmost_player_active;
4024 cave->pushing_stone_prob = level->bd_pushing_prob * 10000;
4025 cave->pushing_stone_prob_sweet = level->bd_pushing_prob_with_sweet * 10000;
4026 cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4027 cave->snap_element = map_element_RND_to_BD_cave(level->bd_snap_element);
4029 // element properties
4030 cave->level_bonus_time[0] = level->bd_clock_extra_time;
4031 cave->voodoo_collects_diamonds = level->bd_voodoo_collects_diamonds;
4032 cave->voodoo_any_hurt_kills_player = level->bd_voodoo_hurt_kills_player;
4033 cave->voodoo_dies_by_stone = level->bd_voodoo_dies_by_rock;
4034 cave->voodoo_disappear_in_explosion = level->bd_voodoo_vanish_by_explosion;
4035 cave->level_penalty_time[0] = level->bd_voodoo_penalty_time;
4036 cave->level_magic_wall_time[0] = level->time_magic_wall;
4037 cave->magic_timer_wait_for_hatching = level->bd_magic_wall_wait_hatching;
4038 cave->magic_wall_stops_amoeba = level->bd_magic_wall_stops_amoeba;
4039 cave->amoeba_timer_wait_for_hatching = level->bd_amoeba_wait_for_hatching;
4040 cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4041 cave->amoeba_2_explodes_by_amoeba = level->bd_amoeba_2_explode_by_amoeba;
4042 cave->level_amoeba_threshold[0] = level->bd_amoeba_threshold_too_big;
4043 cave->level_amoeba_time[0] = level->bd_amoeba_slow_growth_time;
4044 cave->amoeba_growth_prob = level->bd_amoeba_slow_growth_rate * 10000;
4045 cave->amoeba_fast_growth_prob = level->bd_amoeba_fast_growth_rate * 10000;
4046 cave->level_amoeba_2_threshold[0] = level->bd_amoeba_2_threshold_too_big;
4047 cave->level_amoeba_2_time[0] = level->bd_amoeba_2_slow_growth_time;
4048 cave->amoeba_2_growth_prob = level->bd_amoeba_2_slow_growth_rate * 10000;
4049 cave->amoeba_2_fast_growth_prob = level->bd_amoeba_2_fast_growth_rate * 10000;
4051 cave->amoeba_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4052 cave->amoeba_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4053 cave->amoeba_2_too_big_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4054 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4055 cave->amoeba_2_explosion_effect = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4056 cave->amoeba_2_looks_like = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4058 cave->slime_predictable = level->bd_slime_is_predictable;
4059 cave->slime_correct_random = level->bd_slime_correct_random;
4060 cave->level_slime_permeability[0] = level->bd_slime_permeability_rate * 10000;
4061 cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4062 cave->level_slime_seed_c64[0] = level->bd_slime_random_seed_c64;
4063 cave->level_rand[0] = level->bd_cave_random_seed_c64;
4065 cave->acid_eats_this = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4066 cave->acid_spread_ratio = level->bd_acid_spread_rate * 10000;
4067 cave->acid_turns_to = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4069 cave->biter_delay_frame = level->bd_biter_move_delay;
4070 cave->biter_eat = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4072 cave->bladder_converts_by = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4074 cave->expanding_wall_changed = level->bd_change_expanding_wall;
4076 cave->replicators_active = level->bd_replicators_active;
4077 cave->replicator_delay_frame = level->bd_replicator_create_delay;
4079 cave->conveyor_belts_active = level->bd_conveyor_belts_active;
4080 cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4082 cave->water_does_not_flow_down = level->bd_water_cannot_flow_down;
4085 strncpy(cave->name, level->name, sizeof(GdString));
4086 cave->name[sizeof(GdString) - 1] = '\0';
4088 // playfield elements
4089 for (x = 0; x < cave->w; x++)
4090 for (y = 0; y < cave->h; y++)
4091 cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4094 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4096 struct LevelInfo_BD *level_bd = level->native_bd_level;
4097 GdCave *cave = level_bd->cave;
4098 int bd_level_nr = level_bd->level_nr;
4101 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4102 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4105 level->bd_intermission = cave->intermission;
4108 level->time = cave->level_time[bd_level_nr];
4109 level->gems_needed = cave->level_diamonds[bd_level_nr];
4112 level->bd_scheduling_type = cave->scheduling;
4113 level->bd_pal_timing = cave->pal_timing;
4114 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
4115 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
4116 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
4117 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
4120 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
4121 level->score[SC_EMERALD] = cave->diamond_value;
4122 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
4124 // compatibility settings
4125 level->bd_line_shifting_borders = cave->lineshift;
4126 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
4127 level->bd_short_explosions = cave->short_explosions;
4128 level->bd_gravity_affects_all = cave->gravity_affects_all;
4130 // player properties
4131 level->bd_diagonal_movements = cave->diagonal_movements;
4132 level->bd_topmost_player_active = cave->active_is_first_found;
4133 level->bd_pushing_prob = cave->pushing_stone_prob / 10000;
4134 level->bd_pushing_prob_with_sweet = cave->pushing_stone_prob_sweet / 10000;
4135 level->bd_push_mega_rock_with_sweet = cave->mega_stones_pushable_with_sweet;
4136 level->bd_snap_element = map_element_BD_to_RND_cave(cave->snap_element);
4138 // element properties
4139 level->bd_clock_extra_time = cave->level_bonus_time[bd_level_nr];
4140 level->bd_voodoo_collects_diamonds = cave->voodoo_collects_diamonds;
4141 level->bd_voodoo_hurt_kills_player = cave->voodoo_any_hurt_kills_player;
4142 level->bd_voodoo_dies_by_rock = cave->voodoo_dies_by_stone;
4143 level->bd_voodoo_vanish_by_explosion = cave->voodoo_disappear_in_explosion;
4144 level->bd_voodoo_penalty_time = cave->level_penalty_time[bd_level_nr];
4145 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
4146 level->bd_magic_wall_wait_hatching = cave->magic_timer_wait_for_hatching;
4147 level->bd_magic_wall_stops_amoeba = cave->magic_wall_stops_amoeba;
4148 level->bd_amoeba_wait_for_hatching = cave->amoeba_timer_wait_for_hatching;
4149 level->bd_amoeba_start_immediately = cave->amoeba_timer_started_immediately;
4150 level->bd_amoeba_2_explode_by_amoeba = cave->amoeba_2_explodes_by_amoeba;
4151 level->bd_amoeba_threshold_too_big = cave->level_amoeba_threshold[bd_level_nr];
4152 level->bd_amoeba_slow_growth_time = cave->level_amoeba_time[bd_level_nr];
4153 level->bd_amoeba_slow_growth_rate = cave->amoeba_growth_prob / 10000;
4154 level->bd_amoeba_fast_growth_rate = cave->amoeba_fast_growth_prob / 10000;
4155 level->bd_amoeba_2_threshold_too_big = cave->level_amoeba_2_threshold[bd_level_nr];
4156 level->bd_amoeba_2_slow_growth_time = cave->level_amoeba_2_time[bd_level_nr];
4157 level->bd_amoeba_2_slow_growth_rate = cave->amoeba_2_growth_prob / 10000;
4158 level->bd_amoeba_2_fast_growth_rate = cave->amoeba_2_fast_growth_prob / 10000;
4160 level->bd_amoeba_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4161 level->bd_amoeba_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4162 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4163 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4164 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4165 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4167 level->bd_slime_is_predictable = cave->slime_predictable;
4168 level->bd_slime_correct_random = cave->slime_correct_random;
4169 level->bd_slime_permeability_rate = cave->level_slime_permeability[bd_level_nr] / 10000;
4170 level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4171 level->bd_slime_random_seed_c64 = cave->level_slime_seed_c64[bd_level_nr];
4172 level->bd_cave_random_seed_c64 = cave->level_rand[bd_level_nr];
4174 level->bd_acid_eats_element = map_element_BD_to_RND_cave(cave->acid_eats_this);
4175 level->bd_acid_spread_rate = cave->acid_spread_ratio / 10000;
4176 level->bd_acid_turns_to_element = map_element_BD_to_RND_cave(cave->acid_turns_to);
4178 level->bd_biter_move_delay = cave->biter_delay_frame;
4179 level->bd_biter_eats_element = map_element_BD_to_RND_cave(cave->biter_eat);
4181 level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4183 level->bd_change_expanding_wall = cave->expanding_wall_changed;
4185 level->bd_replicators_active = cave->replicators_active;
4186 level->bd_replicator_create_delay = cave->replicator_delay_frame;
4188 level->bd_conveyor_belts_active = cave->conveyor_belts_active;
4189 level->bd_conveyor_belts_changed = cave->conveyor_belts_direction_changed;
4191 level->bd_water_cannot_flow_down = cave->water_does_not_flow_down;
4194 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4196 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4197 level->name[MAX_LEVEL_NAME_LEN] = '\0';
4199 // playfield elements
4200 for (x = 0; x < level->fieldx; x++)
4201 for (y = 0; y < level->fieldy; y++)
4202 level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4204 checked_free(cave_name);
4207 static void setTapeInfoToDefaults(void);
4209 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4211 struct LevelInfo_BD *level_bd = level->native_bd_level;
4212 GdCave *cave = level_bd->cave;
4213 GdReplay *replay = level_bd->replay;
4219 // always start with reliable default values
4220 setTapeInfoToDefaults();
4222 tape.level_nr = level_nr; // (currently not used)
4223 tape.random_seed = replay->seed;
4225 TapeSetDateFromIsoDateString(replay->date);
4228 tape.pos[tape.counter].delay = 0;
4230 tape.bd_replay = TRUE;
4232 // all time calculations only used to display approximate tape time
4233 int cave_speed = cave->speed;
4234 int milliseconds_game = 0;
4235 int milliseconds_elapsed = 20;
4237 for (i = 0; i < replay->movements->len; i++)
4239 int replay_action = replay->movements->data[i];
4240 int tape_action = map_action_BD_to_RND(replay_action);
4241 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4242 boolean success = 0;
4246 success = TapeAddAction(action);
4248 milliseconds_game += milliseconds_elapsed;
4250 if (milliseconds_game >= cave_speed)
4252 milliseconds_game -= cave_speed;
4259 tape.pos[tape.counter].delay = 0;
4260 tape.pos[tape.counter].action[0] = 0;
4264 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4270 TapeHaltRecording();
4274 // ----------------------------------------------------------------------------
4275 // functions for loading EM level
4276 // ----------------------------------------------------------------------------
4278 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4280 static int ball_xy[8][2] =
4291 struct LevelInfo_EM *level_em = level->native_em_level;
4292 struct CAVE *cav = level_em->cav;
4295 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4296 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4298 cav->time_seconds = level->time;
4299 cav->gems_needed = level->gems_needed;
4301 cav->emerald_score = level->score[SC_EMERALD];
4302 cav->diamond_score = level->score[SC_DIAMOND];
4303 cav->alien_score = level->score[SC_ROBOT];
4304 cav->tank_score = level->score[SC_SPACESHIP];
4305 cav->bug_score = level->score[SC_BUG];
4306 cav->eater_score = level->score[SC_YAMYAM];
4307 cav->nut_score = level->score[SC_NUT];
4308 cav->dynamite_score = level->score[SC_DYNAMITE];
4309 cav->key_score = level->score[SC_KEY];
4310 cav->exit_score = level->score[SC_TIME_BONUS];
4312 cav->num_eater_arrays = level->num_yamyam_contents;
4314 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4315 for (y = 0; y < 3; y++)
4316 for (x = 0; x < 3; x++)
4317 cav->eater_array[i][y * 3 + x] =
4318 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4320 cav->amoeba_time = level->amoeba_speed;
4321 cav->wonderwall_time = level->time_magic_wall;
4322 cav->wheel_time = level->time_wheel;
4324 cav->android_move_time = level->android_move_time;
4325 cav->android_clone_time = level->android_clone_time;
4326 cav->ball_random = level->ball_random;
4327 cav->ball_active = level->ball_active_initial;
4328 cav->ball_time = level->ball_time;
4329 cav->num_ball_arrays = level->num_ball_contents;
4331 cav->lenses_score = level->lenses_score;
4332 cav->magnify_score = level->magnify_score;
4333 cav->slurp_score = level->slurp_score;
4335 cav->lenses_time = level->lenses_time;
4336 cav->magnify_time = level->magnify_time;
4338 cav->wind_time = 9999;
4339 cav->wind_direction =
4340 map_direction_RND_to_EM(level->wind_direction_initial);
4342 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4343 for (j = 0; j < 8; j++)
4344 cav->ball_array[i][j] =
4345 map_element_RND_to_EM_cave(level->ball_content[i].
4346 e[ball_xy[j][0]][ball_xy[j][1]]);
4348 map_android_clone_elements_RND_to_EM(level);
4350 // first fill the complete playfield with the empty space element
4351 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4352 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4353 cav->cave[x][y] = Cblank;
4355 // then copy the real level contents from level file into the playfield
4356 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4358 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4360 if (level->field[x][y] == EL_AMOEBA_DEAD)
4361 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4363 cav->cave[x][y] = new_element;
4366 for (i = 0; i < MAX_PLAYERS; i++)
4368 cav->player_x[i] = -1;
4369 cav->player_y[i] = -1;
4372 // initialize player positions and delete players from the playfield
4373 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4375 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4377 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4379 cav->player_x[player_nr] = x;
4380 cav->player_y[player_nr] = y;
4382 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4387 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4389 static int ball_xy[8][2] =
4400 struct LevelInfo_EM *level_em = level->native_em_level;
4401 struct CAVE *cav = level_em->cav;
4404 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4405 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4407 level->time = cav->time_seconds;
4408 level->gems_needed = cav->gems_needed;
4410 sprintf(level->name, "Level %d", level->file_info.nr);
4412 level->score[SC_EMERALD] = cav->emerald_score;
4413 level->score[SC_DIAMOND] = cav->diamond_score;
4414 level->score[SC_ROBOT] = cav->alien_score;
4415 level->score[SC_SPACESHIP] = cav->tank_score;
4416 level->score[SC_BUG] = cav->bug_score;
4417 level->score[SC_YAMYAM] = cav->eater_score;
4418 level->score[SC_NUT] = cav->nut_score;
4419 level->score[SC_DYNAMITE] = cav->dynamite_score;
4420 level->score[SC_KEY] = cav->key_score;
4421 level->score[SC_TIME_BONUS] = cav->exit_score;
4423 level->num_yamyam_contents = cav->num_eater_arrays;
4425 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4426 for (y = 0; y < 3; y++)
4427 for (x = 0; x < 3; x++)
4428 level->yamyam_content[i].e[x][y] =
4429 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4431 level->amoeba_speed = cav->amoeba_time;
4432 level->time_magic_wall = cav->wonderwall_time;
4433 level->time_wheel = cav->wheel_time;
4435 level->android_move_time = cav->android_move_time;
4436 level->android_clone_time = cav->android_clone_time;
4437 level->ball_random = cav->ball_random;
4438 level->ball_active_initial = cav->ball_active;
4439 level->ball_time = cav->ball_time;
4440 level->num_ball_contents = cav->num_ball_arrays;
4442 level->lenses_score = cav->lenses_score;
4443 level->magnify_score = cav->magnify_score;
4444 level->slurp_score = cav->slurp_score;
4446 level->lenses_time = cav->lenses_time;
4447 level->magnify_time = cav->magnify_time;
4449 level->wind_direction_initial =
4450 map_direction_EM_to_RND(cav->wind_direction);
4452 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4453 for (j = 0; j < 8; j++)
4454 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4455 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4457 map_android_clone_elements_EM_to_RND(level);
4459 // convert the playfield (some elements need special treatment)
4460 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4462 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4464 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4465 new_element = EL_AMOEBA_DEAD;
4467 level->field[x][y] = new_element;
4470 for (i = 0; i < MAX_PLAYERS; i++)
4472 // in case of all players set to the same field, use the first player
4473 int nr = MAX_PLAYERS - i - 1;
4474 int jx = cav->player_x[nr];
4475 int jy = cav->player_y[nr];
4477 if (jx != -1 && jy != -1)
4478 level->field[jx][jy] = EL_PLAYER_1 + nr;
4481 // time score is counted for each 10 seconds left in Emerald Mine levels
4482 level->time_score_base = 10;
4486 // ----------------------------------------------------------------------------
4487 // functions for loading SP level
4488 // ----------------------------------------------------------------------------
4490 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4492 struct LevelInfo_SP *level_sp = level->native_sp_level;
4493 LevelInfoType *header = &level_sp->header;
4496 level_sp->width = level->fieldx;
4497 level_sp->height = level->fieldy;
4499 for (x = 0; x < level->fieldx; x++)
4500 for (y = 0; y < level->fieldy; y++)
4501 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4503 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4505 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4506 header->LevelTitle[i] = level->name[i];
4507 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4509 header->InfotronsNeeded = level->gems_needed;
4511 header->SpecialPortCount = 0;
4513 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4515 boolean gravity_port_found = FALSE;
4516 boolean gravity_port_valid = FALSE;
4517 int gravity_port_flag;
4518 int gravity_port_base_element;
4519 int element = level->field[x][y];
4521 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4522 element <= EL_SP_GRAVITY_ON_PORT_UP)
4524 gravity_port_found = TRUE;
4525 gravity_port_valid = TRUE;
4526 gravity_port_flag = 1;
4527 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4529 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4530 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4532 gravity_port_found = TRUE;
4533 gravity_port_valid = TRUE;
4534 gravity_port_flag = 0;
4535 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4537 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4538 element <= EL_SP_GRAVITY_PORT_UP)
4540 // change R'n'D style gravity inverting special port to normal port
4541 // (there are no gravity inverting ports in native Supaplex engine)
4543 gravity_port_found = TRUE;
4544 gravity_port_valid = FALSE;
4545 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4548 if (gravity_port_found)
4550 if (gravity_port_valid &&
4551 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4553 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4555 port->PortLocation = (y * level->fieldx + x) * 2;
4556 port->Gravity = gravity_port_flag;
4558 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4560 header->SpecialPortCount++;
4564 // change special gravity port to normal port
4566 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4569 level_sp->playfield[x][y] = element - EL_SP_START;
4574 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4576 struct LevelInfo_SP *level_sp = level->native_sp_level;
4577 LevelInfoType *header = &level_sp->header;
4578 boolean num_invalid_elements = 0;
4581 level->fieldx = level_sp->width;
4582 level->fieldy = level_sp->height;
4584 for (x = 0; x < level->fieldx; x++)
4586 for (y = 0; y < level->fieldy; y++)
4588 int element_old = level_sp->playfield[x][y];
4589 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4591 if (element_new == EL_UNKNOWN)
4593 num_invalid_elements++;
4595 Debug("level:native:SP", "invalid element %d at position %d, %d",
4599 level->field[x][y] = element_new;
4603 if (num_invalid_elements > 0)
4604 Warn("found %d invalid elements%s", num_invalid_elements,
4605 (!options.debug ? " (use '--debug' for more details)" : ""));
4607 for (i = 0; i < MAX_PLAYERS; i++)
4608 level->initial_player_gravity[i] =
4609 (header->InitialGravity == 1 ? TRUE : FALSE);
4611 // skip leading spaces
4612 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4613 if (header->LevelTitle[i] != ' ')
4617 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4618 level->name[j] = header->LevelTitle[i];
4619 level->name[j] = '\0';
4621 // cut trailing spaces
4623 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4624 level->name[j - 1] = '\0';
4626 level->gems_needed = header->InfotronsNeeded;
4628 for (i = 0; i < header->SpecialPortCount; i++)
4630 SpecialPortType *port = &header->SpecialPort[i];
4631 int port_location = port->PortLocation;
4632 int gravity = port->Gravity;
4633 int port_x, port_y, port_element;
4635 port_x = (port_location / 2) % level->fieldx;
4636 port_y = (port_location / 2) / level->fieldx;
4638 if (port_x < 0 || port_x >= level->fieldx ||
4639 port_y < 0 || port_y >= level->fieldy)
4641 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4646 port_element = level->field[port_x][port_y];
4648 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4649 port_element > EL_SP_GRAVITY_PORT_UP)
4651 Warn("no special port at position (%d, %d)", port_x, port_y);
4656 // change previous (wrong) gravity inverting special port to either
4657 // gravity enabling special port or gravity disabling special port
4658 level->field[port_x][port_y] +=
4659 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4660 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4663 // change special gravity ports without database entries to normal ports
4664 for (x = 0; x < level->fieldx; x++)
4665 for (y = 0; y < level->fieldy; y++)
4666 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4667 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4668 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4670 level->time = 0; // no time limit
4671 level->amoeba_speed = 0;
4672 level->time_magic_wall = 0;
4673 level->time_wheel = 0;
4674 level->amoeba_content = EL_EMPTY;
4676 // original Supaplex does not use score values -- rate by playing time
4677 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4678 level->score[i] = 0;
4680 level->rate_time_over_score = TRUE;
4682 // there are no yamyams in supaplex levels
4683 for (i = 0; i < level->num_yamyam_contents; i++)
4684 for (x = 0; x < 3; x++)
4685 for (y = 0; y < 3; y++)
4686 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4689 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4691 struct LevelInfo_SP *level_sp = level->native_sp_level;
4692 struct DemoInfo_SP *demo = &level_sp->demo;
4695 // always start with reliable default values
4696 demo->is_available = FALSE;
4699 if (TAPE_IS_EMPTY(tape))
4702 demo->level_nr = tape.level_nr; // (currently not used)
4704 level_sp->header.DemoRandomSeed = tape.random_seed;
4708 for (i = 0; i < tape.length; i++)
4710 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4711 int demo_repeat = tape.pos[i].delay;
4712 int demo_entries = (demo_repeat + 15) / 16;
4714 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4716 Warn("tape truncated: size exceeds maximum SP demo size %d",
4722 for (j = 0; j < demo_repeat / 16; j++)
4723 demo->data[demo->length++] = 0xf0 | demo_action;
4725 if (demo_repeat % 16)
4726 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4729 demo->is_available = TRUE;
4732 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4734 struct LevelInfo_SP *level_sp = level->native_sp_level;
4735 struct DemoInfo_SP *demo = &level_sp->demo;
4736 char *filename = level->file_info.filename;
4739 // always start with reliable default values
4740 setTapeInfoToDefaults();
4742 if (!demo->is_available)
4745 tape.level_nr = demo->level_nr; // (currently not used)
4746 tape.random_seed = level_sp->header.DemoRandomSeed;
4748 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4751 tape.pos[tape.counter].delay = 0;
4753 for (i = 0; i < demo->length; i++)
4755 int demo_action = demo->data[i] & 0x0f;
4756 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4757 int tape_action = map_key_SP_to_RND(demo_action);
4758 int tape_repeat = demo_repeat + 1;
4759 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4760 boolean success = 0;
4763 for (j = 0; j < tape_repeat; j++)
4764 success = TapeAddAction(action);
4768 Warn("SP demo truncated: size exceeds maximum tape size %d",
4775 TapeHaltRecording();
4779 // ----------------------------------------------------------------------------
4780 // functions for loading MM level
4781 // ----------------------------------------------------------------------------
4783 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4785 struct LevelInfo_MM *level_mm = level->native_mm_level;
4788 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4789 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4791 level_mm->time = level->time;
4792 level_mm->kettles_needed = level->gems_needed;
4793 level_mm->auto_count_kettles = level->auto_count_gems;
4795 level_mm->mm_laser_red = level->mm_laser_red;
4796 level_mm->mm_laser_green = level->mm_laser_green;
4797 level_mm->mm_laser_blue = level->mm_laser_blue;
4799 level_mm->df_laser_red = level->df_laser_red;
4800 level_mm->df_laser_green = level->df_laser_green;
4801 level_mm->df_laser_blue = level->df_laser_blue;
4803 strcpy(level_mm->name, level->name);
4804 strcpy(level_mm->author, level->author);
4806 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4807 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4808 level_mm->score[SC_KEY] = level->score[SC_KEY];
4809 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4810 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4812 level_mm->amoeba_speed = level->amoeba_speed;
4813 level_mm->time_fuse = level->mm_time_fuse;
4814 level_mm->time_bomb = level->mm_time_bomb;
4815 level_mm->time_ball = level->mm_time_ball;
4816 level_mm->time_block = level->mm_time_block;
4818 level_mm->num_ball_contents = level->num_mm_ball_contents;
4819 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4820 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4821 level_mm->explode_ball = level->explode_mm_ball;
4823 for (i = 0; i < level->num_mm_ball_contents; i++)
4824 level_mm->ball_content[i] =
4825 map_element_RND_to_MM(level->mm_ball_content[i]);
4827 for (x = 0; x < level->fieldx; x++)
4828 for (y = 0; y < level->fieldy; y++)
4830 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4833 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4835 struct LevelInfo_MM *level_mm = level->native_mm_level;
4838 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4839 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4841 level->time = level_mm->time;
4842 level->gems_needed = level_mm->kettles_needed;
4843 level->auto_count_gems = level_mm->auto_count_kettles;
4845 level->mm_laser_red = level_mm->mm_laser_red;
4846 level->mm_laser_green = level_mm->mm_laser_green;
4847 level->mm_laser_blue = level_mm->mm_laser_blue;
4849 level->df_laser_red = level_mm->df_laser_red;
4850 level->df_laser_green = level_mm->df_laser_green;
4851 level->df_laser_blue = level_mm->df_laser_blue;
4853 strcpy(level->name, level_mm->name);
4855 // only overwrite author from 'levelinfo.conf' if author defined in level
4856 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4857 strcpy(level->author, level_mm->author);
4859 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4860 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4861 level->score[SC_KEY] = level_mm->score[SC_KEY];
4862 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4863 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4865 level->amoeba_speed = level_mm->amoeba_speed;
4866 level->mm_time_fuse = level_mm->time_fuse;
4867 level->mm_time_bomb = level_mm->time_bomb;
4868 level->mm_time_ball = level_mm->time_ball;
4869 level->mm_time_block = level_mm->time_block;
4871 level->num_mm_ball_contents = level_mm->num_ball_contents;
4872 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4873 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4874 level->explode_mm_ball = level_mm->explode_ball;
4876 for (i = 0; i < level->num_mm_ball_contents; i++)
4877 level->mm_ball_content[i] =
4878 map_element_MM_to_RND(level_mm->ball_content[i]);
4880 for (x = 0; x < level->fieldx; x++)
4881 for (y = 0; y < level->fieldy; y++)
4882 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4886 // ----------------------------------------------------------------------------
4887 // functions for loading DC level
4888 // ----------------------------------------------------------------------------
4890 #define DC_LEVEL_HEADER_SIZE 344
4892 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4895 static int last_data_encoded;
4899 int diff_hi, diff_lo;
4900 int data_hi, data_lo;
4901 unsigned short data_decoded;
4905 last_data_encoded = 0;
4912 diff = data_encoded - last_data_encoded;
4913 diff_hi = diff & ~0xff;
4914 diff_lo = diff & 0xff;
4918 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4919 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4920 data_hi = data_hi & 0xff00;
4922 data_decoded = data_hi | data_lo;
4924 last_data_encoded = data_encoded;
4926 offset1 = (offset1 + 1) % 31;
4927 offset2 = offset2 & 0xff;
4929 return data_decoded;
4932 static int getMappedElement_DC(int element)
4940 // 0x0117 - 0x036e: (?)
4943 // 0x042d - 0x0684: (?)
4959 element = EL_CRYSTAL;
4962 case 0x0e77: // quicksand (boulder)
4963 element = EL_QUICKSAND_FAST_FULL;
4966 case 0x0e99: // slow quicksand (boulder)
4967 element = EL_QUICKSAND_FULL;
4971 element = EL_EM_EXIT_OPEN;
4975 element = EL_EM_EXIT_CLOSED;
4979 element = EL_EM_STEEL_EXIT_OPEN;
4983 element = EL_EM_STEEL_EXIT_CLOSED;
4986 case 0x0f4f: // dynamite (lit 1)
4987 element = EL_EM_DYNAMITE_ACTIVE;
4990 case 0x0f57: // dynamite (lit 2)
4991 element = EL_EM_DYNAMITE_ACTIVE;
4994 case 0x0f5f: // dynamite (lit 3)
4995 element = EL_EM_DYNAMITE_ACTIVE;
4998 case 0x0f67: // dynamite (lit 4)
4999 element = EL_EM_DYNAMITE_ACTIVE;
5006 element = EL_AMOEBA_WET;
5010 element = EL_AMOEBA_DROP;
5014 element = EL_DC_MAGIC_WALL;
5018 element = EL_SPACESHIP_UP;
5022 element = EL_SPACESHIP_DOWN;
5026 element = EL_SPACESHIP_LEFT;
5030 element = EL_SPACESHIP_RIGHT;
5034 element = EL_BUG_UP;
5038 element = EL_BUG_DOWN;
5042 element = EL_BUG_LEFT;
5046 element = EL_BUG_RIGHT;
5050 element = EL_MOLE_UP;
5054 element = EL_MOLE_DOWN;
5058 element = EL_MOLE_LEFT;
5062 element = EL_MOLE_RIGHT;
5070 element = EL_YAMYAM_UP;
5074 element = EL_SWITCHGATE_OPEN;
5078 element = EL_SWITCHGATE_CLOSED;
5082 element = EL_DC_SWITCHGATE_SWITCH_UP;
5086 element = EL_TIMEGATE_CLOSED;
5089 case 0x144c: // conveyor belt switch (green)
5090 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5093 case 0x144f: // conveyor belt switch (red)
5094 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5097 case 0x1452: // conveyor belt switch (blue)
5098 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5102 element = EL_CONVEYOR_BELT_3_MIDDLE;
5106 element = EL_CONVEYOR_BELT_3_LEFT;
5110 element = EL_CONVEYOR_BELT_3_RIGHT;
5114 element = EL_CONVEYOR_BELT_1_MIDDLE;
5118 element = EL_CONVEYOR_BELT_1_LEFT;
5122 element = EL_CONVEYOR_BELT_1_RIGHT;
5126 element = EL_CONVEYOR_BELT_4_MIDDLE;
5130 element = EL_CONVEYOR_BELT_4_LEFT;
5134 element = EL_CONVEYOR_BELT_4_RIGHT;
5138 element = EL_EXPANDABLE_WALL_HORIZONTAL;
5142 element = EL_EXPANDABLE_WALL_VERTICAL;
5146 element = EL_EXPANDABLE_WALL_ANY;
5149 case 0x14ce: // growing steel wall (left/right)
5150 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5153 case 0x14df: // growing steel wall (up/down)
5154 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5157 case 0x14e8: // growing steel wall (up/down/left/right)
5158 element = EL_EXPANDABLE_STEELWALL_ANY;
5162 element = EL_SHIELD_DEADLY;
5166 element = EL_EXTRA_TIME;
5174 element = EL_EMPTY_SPACE;
5177 case 0x1578: // quicksand (empty)
5178 element = EL_QUICKSAND_FAST_EMPTY;
5181 case 0x1579: // slow quicksand (empty)
5182 element = EL_QUICKSAND_EMPTY;
5192 element = EL_EM_DYNAMITE;
5195 case 0x15a1: // key (red)
5196 element = EL_EM_KEY_1;
5199 case 0x15a2: // key (yellow)
5200 element = EL_EM_KEY_2;
5203 case 0x15a3: // key (blue)
5204 element = EL_EM_KEY_4;
5207 case 0x15a4: // key (green)
5208 element = EL_EM_KEY_3;
5211 case 0x15a5: // key (white)
5212 element = EL_DC_KEY_WHITE;
5216 element = EL_WALL_SLIPPERY;
5223 case 0x15a8: // wall (not round)
5227 case 0x15a9: // (blue)
5228 element = EL_CHAR_A;
5231 case 0x15aa: // (blue)
5232 element = EL_CHAR_B;
5235 case 0x15ab: // (blue)
5236 element = EL_CHAR_C;
5239 case 0x15ac: // (blue)
5240 element = EL_CHAR_D;
5243 case 0x15ad: // (blue)
5244 element = EL_CHAR_E;
5247 case 0x15ae: // (blue)
5248 element = EL_CHAR_F;
5251 case 0x15af: // (blue)
5252 element = EL_CHAR_G;
5255 case 0x15b0: // (blue)
5256 element = EL_CHAR_H;
5259 case 0x15b1: // (blue)
5260 element = EL_CHAR_I;
5263 case 0x15b2: // (blue)
5264 element = EL_CHAR_J;
5267 case 0x15b3: // (blue)
5268 element = EL_CHAR_K;
5271 case 0x15b4: // (blue)
5272 element = EL_CHAR_L;
5275 case 0x15b5: // (blue)
5276 element = EL_CHAR_M;
5279 case 0x15b6: // (blue)
5280 element = EL_CHAR_N;
5283 case 0x15b7: // (blue)
5284 element = EL_CHAR_O;
5287 case 0x15b8: // (blue)
5288 element = EL_CHAR_P;
5291 case 0x15b9: // (blue)
5292 element = EL_CHAR_Q;
5295 case 0x15ba: // (blue)
5296 element = EL_CHAR_R;
5299 case 0x15bb: // (blue)
5300 element = EL_CHAR_S;
5303 case 0x15bc: // (blue)
5304 element = EL_CHAR_T;
5307 case 0x15bd: // (blue)
5308 element = EL_CHAR_U;
5311 case 0x15be: // (blue)
5312 element = EL_CHAR_V;
5315 case 0x15bf: // (blue)
5316 element = EL_CHAR_W;
5319 case 0x15c0: // (blue)
5320 element = EL_CHAR_X;
5323 case 0x15c1: // (blue)
5324 element = EL_CHAR_Y;
5327 case 0x15c2: // (blue)
5328 element = EL_CHAR_Z;
5331 case 0x15c3: // (blue)
5332 element = EL_CHAR_AUMLAUT;
5335 case 0x15c4: // (blue)
5336 element = EL_CHAR_OUMLAUT;
5339 case 0x15c5: // (blue)
5340 element = EL_CHAR_UUMLAUT;
5343 case 0x15c6: // (blue)
5344 element = EL_CHAR_0;
5347 case 0x15c7: // (blue)
5348 element = EL_CHAR_1;
5351 case 0x15c8: // (blue)
5352 element = EL_CHAR_2;
5355 case 0x15c9: // (blue)
5356 element = EL_CHAR_3;
5359 case 0x15ca: // (blue)
5360 element = EL_CHAR_4;
5363 case 0x15cb: // (blue)
5364 element = EL_CHAR_5;
5367 case 0x15cc: // (blue)
5368 element = EL_CHAR_6;
5371 case 0x15cd: // (blue)
5372 element = EL_CHAR_7;
5375 case 0x15ce: // (blue)
5376 element = EL_CHAR_8;
5379 case 0x15cf: // (blue)
5380 element = EL_CHAR_9;
5383 case 0x15d0: // (blue)
5384 element = EL_CHAR_PERIOD;
5387 case 0x15d1: // (blue)
5388 element = EL_CHAR_EXCLAM;
5391 case 0x15d2: // (blue)
5392 element = EL_CHAR_COLON;
5395 case 0x15d3: // (blue)
5396 element = EL_CHAR_LESS;
5399 case 0x15d4: // (blue)
5400 element = EL_CHAR_GREATER;
5403 case 0x15d5: // (blue)
5404 element = EL_CHAR_QUESTION;
5407 case 0x15d6: // (blue)
5408 element = EL_CHAR_COPYRIGHT;
5411 case 0x15d7: // (blue)
5412 element = EL_CHAR_UP;
5415 case 0x15d8: // (blue)
5416 element = EL_CHAR_DOWN;
5419 case 0x15d9: // (blue)
5420 element = EL_CHAR_BUTTON;
5423 case 0x15da: // (blue)
5424 element = EL_CHAR_PLUS;
5427 case 0x15db: // (blue)
5428 element = EL_CHAR_MINUS;
5431 case 0x15dc: // (blue)
5432 element = EL_CHAR_APOSTROPHE;
5435 case 0x15dd: // (blue)
5436 element = EL_CHAR_PARENLEFT;
5439 case 0x15de: // (blue)
5440 element = EL_CHAR_PARENRIGHT;
5443 case 0x15df: // (green)
5444 element = EL_CHAR_A;
5447 case 0x15e0: // (green)
5448 element = EL_CHAR_B;
5451 case 0x15e1: // (green)
5452 element = EL_CHAR_C;
5455 case 0x15e2: // (green)
5456 element = EL_CHAR_D;
5459 case 0x15e3: // (green)
5460 element = EL_CHAR_E;
5463 case 0x15e4: // (green)
5464 element = EL_CHAR_F;
5467 case 0x15e5: // (green)
5468 element = EL_CHAR_G;
5471 case 0x15e6: // (green)
5472 element = EL_CHAR_H;
5475 case 0x15e7: // (green)
5476 element = EL_CHAR_I;
5479 case 0x15e8: // (green)
5480 element = EL_CHAR_J;
5483 case 0x15e9: // (green)
5484 element = EL_CHAR_K;
5487 case 0x15ea: // (green)
5488 element = EL_CHAR_L;
5491 case 0x15eb: // (green)
5492 element = EL_CHAR_M;
5495 case 0x15ec: // (green)
5496 element = EL_CHAR_N;
5499 case 0x15ed: // (green)
5500 element = EL_CHAR_O;
5503 case 0x15ee: // (green)
5504 element = EL_CHAR_P;
5507 case 0x15ef: // (green)
5508 element = EL_CHAR_Q;
5511 case 0x15f0: // (green)
5512 element = EL_CHAR_R;
5515 case 0x15f1: // (green)
5516 element = EL_CHAR_S;
5519 case 0x15f2: // (green)
5520 element = EL_CHAR_T;
5523 case 0x15f3: // (green)
5524 element = EL_CHAR_U;
5527 case 0x15f4: // (green)
5528 element = EL_CHAR_V;
5531 case 0x15f5: // (green)
5532 element = EL_CHAR_W;
5535 case 0x15f6: // (green)
5536 element = EL_CHAR_X;
5539 case 0x15f7: // (green)
5540 element = EL_CHAR_Y;
5543 case 0x15f8: // (green)
5544 element = EL_CHAR_Z;
5547 case 0x15f9: // (green)
5548 element = EL_CHAR_AUMLAUT;
5551 case 0x15fa: // (green)
5552 element = EL_CHAR_OUMLAUT;
5555 case 0x15fb: // (green)
5556 element = EL_CHAR_UUMLAUT;
5559 case 0x15fc: // (green)
5560 element = EL_CHAR_0;
5563 case 0x15fd: // (green)
5564 element = EL_CHAR_1;
5567 case 0x15fe: // (green)
5568 element = EL_CHAR_2;
5571 case 0x15ff: // (green)
5572 element = EL_CHAR_3;
5575 case 0x1600: // (green)
5576 element = EL_CHAR_4;
5579 case 0x1601: // (green)
5580 element = EL_CHAR_5;
5583 case 0x1602: // (green)
5584 element = EL_CHAR_6;
5587 case 0x1603: // (green)
5588 element = EL_CHAR_7;
5591 case 0x1604: // (green)
5592 element = EL_CHAR_8;
5595 case 0x1605: // (green)
5596 element = EL_CHAR_9;
5599 case 0x1606: // (green)
5600 element = EL_CHAR_PERIOD;
5603 case 0x1607: // (green)
5604 element = EL_CHAR_EXCLAM;
5607 case 0x1608: // (green)
5608 element = EL_CHAR_COLON;
5611 case 0x1609: // (green)
5612 element = EL_CHAR_LESS;
5615 case 0x160a: // (green)
5616 element = EL_CHAR_GREATER;
5619 case 0x160b: // (green)
5620 element = EL_CHAR_QUESTION;
5623 case 0x160c: // (green)
5624 element = EL_CHAR_COPYRIGHT;
5627 case 0x160d: // (green)
5628 element = EL_CHAR_UP;
5631 case 0x160e: // (green)
5632 element = EL_CHAR_DOWN;
5635 case 0x160f: // (green)
5636 element = EL_CHAR_BUTTON;
5639 case 0x1610: // (green)
5640 element = EL_CHAR_PLUS;
5643 case 0x1611: // (green)
5644 element = EL_CHAR_MINUS;
5647 case 0x1612: // (green)
5648 element = EL_CHAR_APOSTROPHE;
5651 case 0x1613: // (green)
5652 element = EL_CHAR_PARENLEFT;
5655 case 0x1614: // (green)
5656 element = EL_CHAR_PARENRIGHT;
5659 case 0x1615: // (blue steel)
5660 element = EL_STEEL_CHAR_A;
5663 case 0x1616: // (blue steel)
5664 element = EL_STEEL_CHAR_B;
5667 case 0x1617: // (blue steel)
5668 element = EL_STEEL_CHAR_C;
5671 case 0x1618: // (blue steel)
5672 element = EL_STEEL_CHAR_D;
5675 case 0x1619: // (blue steel)
5676 element = EL_STEEL_CHAR_E;
5679 case 0x161a: // (blue steel)
5680 element = EL_STEEL_CHAR_F;
5683 case 0x161b: // (blue steel)
5684 element = EL_STEEL_CHAR_G;
5687 case 0x161c: // (blue steel)
5688 element = EL_STEEL_CHAR_H;
5691 case 0x161d: // (blue steel)
5692 element = EL_STEEL_CHAR_I;
5695 case 0x161e: // (blue steel)
5696 element = EL_STEEL_CHAR_J;
5699 case 0x161f: // (blue steel)
5700 element = EL_STEEL_CHAR_K;
5703 case 0x1620: // (blue steel)
5704 element = EL_STEEL_CHAR_L;
5707 case 0x1621: // (blue steel)
5708 element = EL_STEEL_CHAR_M;
5711 case 0x1622: // (blue steel)
5712 element = EL_STEEL_CHAR_N;
5715 case 0x1623: // (blue steel)
5716 element = EL_STEEL_CHAR_O;
5719 case 0x1624: // (blue steel)
5720 element = EL_STEEL_CHAR_P;
5723 case 0x1625: // (blue steel)
5724 element = EL_STEEL_CHAR_Q;
5727 case 0x1626: // (blue steel)
5728 element = EL_STEEL_CHAR_R;
5731 case 0x1627: // (blue steel)
5732 element = EL_STEEL_CHAR_S;
5735 case 0x1628: // (blue steel)
5736 element = EL_STEEL_CHAR_T;
5739 case 0x1629: // (blue steel)
5740 element = EL_STEEL_CHAR_U;
5743 case 0x162a: // (blue steel)
5744 element = EL_STEEL_CHAR_V;
5747 case 0x162b: // (blue steel)
5748 element = EL_STEEL_CHAR_W;
5751 case 0x162c: // (blue steel)
5752 element = EL_STEEL_CHAR_X;
5755 case 0x162d: // (blue steel)
5756 element = EL_STEEL_CHAR_Y;
5759 case 0x162e: // (blue steel)
5760 element = EL_STEEL_CHAR_Z;
5763 case 0x162f: // (blue steel)
5764 element = EL_STEEL_CHAR_AUMLAUT;
5767 case 0x1630: // (blue steel)
5768 element = EL_STEEL_CHAR_OUMLAUT;
5771 case 0x1631: // (blue steel)
5772 element = EL_STEEL_CHAR_UUMLAUT;
5775 case 0x1632: // (blue steel)
5776 element = EL_STEEL_CHAR_0;
5779 case 0x1633: // (blue steel)
5780 element = EL_STEEL_CHAR_1;
5783 case 0x1634: // (blue steel)
5784 element = EL_STEEL_CHAR_2;
5787 case 0x1635: // (blue steel)
5788 element = EL_STEEL_CHAR_3;
5791 case 0x1636: // (blue steel)
5792 element = EL_STEEL_CHAR_4;
5795 case 0x1637: // (blue steel)
5796 element = EL_STEEL_CHAR_5;
5799 case 0x1638: // (blue steel)
5800 element = EL_STEEL_CHAR_6;
5803 case 0x1639: // (blue steel)
5804 element = EL_STEEL_CHAR_7;
5807 case 0x163a: // (blue steel)
5808 element = EL_STEEL_CHAR_8;
5811 case 0x163b: // (blue steel)
5812 element = EL_STEEL_CHAR_9;
5815 case 0x163c: // (blue steel)
5816 element = EL_STEEL_CHAR_PERIOD;
5819 case 0x163d: // (blue steel)
5820 element = EL_STEEL_CHAR_EXCLAM;
5823 case 0x163e: // (blue steel)
5824 element = EL_STEEL_CHAR_COLON;
5827 case 0x163f: // (blue steel)
5828 element = EL_STEEL_CHAR_LESS;
5831 case 0x1640: // (blue steel)
5832 element = EL_STEEL_CHAR_GREATER;
5835 case 0x1641: // (blue steel)
5836 element = EL_STEEL_CHAR_QUESTION;
5839 case 0x1642: // (blue steel)
5840 element = EL_STEEL_CHAR_COPYRIGHT;
5843 case 0x1643: // (blue steel)
5844 element = EL_STEEL_CHAR_UP;
5847 case 0x1644: // (blue steel)
5848 element = EL_STEEL_CHAR_DOWN;
5851 case 0x1645: // (blue steel)
5852 element = EL_STEEL_CHAR_BUTTON;
5855 case 0x1646: // (blue steel)
5856 element = EL_STEEL_CHAR_PLUS;
5859 case 0x1647: // (blue steel)
5860 element = EL_STEEL_CHAR_MINUS;
5863 case 0x1648: // (blue steel)
5864 element = EL_STEEL_CHAR_APOSTROPHE;
5867 case 0x1649: // (blue steel)
5868 element = EL_STEEL_CHAR_PARENLEFT;
5871 case 0x164a: // (blue steel)
5872 element = EL_STEEL_CHAR_PARENRIGHT;
5875 case 0x164b: // (green steel)
5876 element = EL_STEEL_CHAR_A;
5879 case 0x164c: // (green steel)
5880 element = EL_STEEL_CHAR_B;
5883 case 0x164d: // (green steel)
5884 element = EL_STEEL_CHAR_C;
5887 case 0x164e: // (green steel)
5888 element = EL_STEEL_CHAR_D;
5891 case 0x164f: // (green steel)
5892 element = EL_STEEL_CHAR_E;
5895 case 0x1650: // (green steel)
5896 element = EL_STEEL_CHAR_F;
5899 case 0x1651: // (green steel)
5900 element = EL_STEEL_CHAR_G;
5903 case 0x1652: // (green steel)
5904 element = EL_STEEL_CHAR_H;
5907 case 0x1653: // (green steel)
5908 element = EL_STEEL_CHAR_I;
5911 case 0x1654: // (green steel)
5912 element = EL_STEEL_CHAR_J;
5915 case 0x1655: // (green steel)
5916 element = EL_STEEL_CHAR_K;
5919 case 0x1656: // (green steel)
5920 element = EL_STEEL_CHAR_L;
5923 case 0x1657: // (green steel)
5924 element = EL_STEEL_CHAR_M;
5927 case 0x1658: // (green steel)
5928 element = EL_STEEL_CHAR_N;
5931 case 0x1659: // (green steel)
5932 element = EL_STEEL_CHAR_O;
5935 case 0x165a: // (green steel)
5936 element = EL_STEEL_CHAR_P;
5939 case 0x165b: // (green steel)
5940 element = EL_STEEL_CHAR_Q;
5943 case 0x165c: // (green steel)
5944 element = EL_STEEL_CHAR_R;
5947 case 0x165d: // (green steel)
5948 element = EL_STEEL_CHAR_S;
5951 case 0x165e: // (green steel)
5952 element = EL_STEEL_CHAR_T;
5955 case 0x165f: // (green steel)
5956 element = EL_STEEL_CHAR_U;
5959 case 0x1660: // (green steel)
5960 element = EL_STEEL_CHAR_V;
5963 case 0x1661: // (green steel)
5964 element = EL_STEEL_CHAR_W;
5967 case 0x1662: // (green steel)
5968 element = EL_STEEL_CHAR_X;
5971 case 0x1663: // (green steel)
5972 element = EL_STEEL_CHAR_Y;
5975 case 0x1664: // (green steel)
5976 element = EL_STEEL_CHAR_Z;
5979 case 0x1665: // (green steel)
5980 element = EL_STEEL_CHAR_AUMLAUT;
5983 case 0x1666: // (green steel)
5984 element = EL_STEEL_CHAR_OUMLAUT;
5987 case 0x1667: // (green steel)
5988 element = EL_STEEL_CHAR_UUMLAUT;
5991 case 0x1668: // (green steel)
5992 element = EL_STEEL_CHAR_0;
5995 case 0x1669: // (green steel)
5996 element = EL_STEEL_CHAR_1;
5999 case 0x166a: // (green steel)
6000 element = EL_STEEL_CHAR_2;
6003 case 0x166b: // (green steel)
6004 element = EL_STEEL_CHAR_3;
6007 case 0x166c: // (green steel)
6008 element = EL_STEEL_CHAR_4;
6011 case 0x166d: // (green steel)
6012 element = EL_STEEL_CHAR_5;
6015 case 0x166e: // (green steel)
6016 element = EL_STEEL_CHAR_6;
6019 case 0x166f: // (green steel)
6020 element = EL_STEEL_CHAR_7;
6023 case 0x1670: // (green steel)
6024 element = EL_STEEL_CHAR_8;
6027 case 0x1671: // (green steel)
6028 element = EL_STEEL_CHAR_9;
6031 case 0x1672: // (green steel)
6032 element = EL_STEEL_CHAR_PERIOD;
6035 case 0x1673: // (green steel)
6036 element = EL_STEEL_CHAR_EXCLAM;
6039 case 0x1674: // (green steel)
6040 element = EL_STEEL_CHAR_COLON;
6043 case 0x1675: // (green steel)
6044 element = EL_STEEL_CHAR_LESS;
6047 case 0x1676: // (green steel)
6048 element = EL_STEEL_CHAR_GREATER;
6051 case 0x1677: // (green steel)
6052 element = EL_STEEL_CHAR_QUESTION;
6055 case 0x1678: // (green steel)
6056 element = EL_STEEL_CHAR_COPYRIGHT;
6059 case 0x1679: // (green steel)
6060 element = EL_STEEL_CHAR_UP;
6063 case 0x167a: // (green steel)
6064 element = EL_STEEL_CHAR_DOWN;
6067 case 0x167b: // (green steel)
6068 element = EL_STEEL_CHAR_BUTTON;
6071 case 0x167c: // (green steel)
6072 element = EL_STEEL_CHAR_PLUS;
6075 case 0x167d: // (green steel)
6076 element = EL_STEEL_CHAR_MINUS;
6079 case 0x167e: // (green steel)
6080 element = EL_STEEL_CHAR_APOSTROPHE;
6083 case 0x167f: // (green steel)
6084 element = EL_STEEL_CHAR_PARENLEFT;
6087 case 0x1680: // (green steel)
6088 element = EL_STEEL_CHAR_PARENRIGHT;
6091 case 0x1681: // gate (red)
6092 element = EL_EM_GATE_1;
6095 case 0x1682: // secret gate (red)
6096 element = EL_EM_GATE_1_GRAY;
6099 case 0x1683: // gate (yellow)
6100 element = EL_EM_GATE_2;
6103 case 0x1684: // secret gate (yellow)
6104 element = EL_EM_GATE_2_GRAY;
6107 case 0x1685: // gate (blue)
6108 element = EL_EM_GATE_4;
6111 case 0x1686: // secret gate (blue)
6112 element = EL_EM_GATE_4_GRAY;
6115 case 0x1687: // gate (green)
6116 element = EL_EM_GATE_3;
6119 case 0x1688: // secret gate (green)
6120 element = EL_EM_GATE_3_GRAY;
6123 case 0x1689: // gate (white)
6124 element = EL_DC_GATE_WHITE;
6127 case 0x168a: // secret gate (white)
6128 element = EL_DC_GATE_WHITE_GRAY;
6131 case 0x168b: // secret gate (no key)
6132 element = EL_DC_GATE_FAKE_GRAY;
6136 element = EL_ROBOT_WHEEL;
6140 element = EL_DC_TIMEGATE_SWITCH;
6144 element = EL_ACID_POOL_BOTTOM;
6148 element = EL_ACID_POOL_TOPLEFT;
6152 element = EL_ACID_POOL_TOPRIGHT;
6156 element = EL_ACID_POOL_BOTTOMLEFT;
6160 element = EL_ACID_POOL_BOTTOMRIGHT;
6164 element = EL_STEELWALL;
6168 element = EL_STEELWALL_SLIPPERY;
6171 case 0x1695: // steel wall (not round)
6172 element = EL_STEELWALL;
6175 case 0x1696: // steel wall (left)
6176 element = EL_DC_STEELWALL_1_LEFT;
6179 case 0x1697: // steel wall (bottom)
6180 element = EL_DC_STEELWALL_1_BOTTOM;
6183 case 0x1698: // steel wall (right)
6184 element = EL_DC_STEELWALL_1_RIGHT;
6187 case 0x1699: // steel wall (top)
6188 element = EL_DC_STEELWALL_1_TOP;
6191 case 0x169a: // steel wall (left/bottom)
6192 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6195 case 0x169b: // steel wall (right/bottom)
6196 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6199 case 0x169c: // steel wall (right/top)
6200 element = EL_DC_STEELWALL_1_TOPRIGHT;
6203 case 0x169d: // steel wall (left/top)
6204 element = EL_DC_STEELWALL_1_TOPLEFT;
6207 case 0x169e: // steel wall (right/bottom small)
6208 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6211 case 0x169f: // steel wall (left/bottom small)
6212 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6215 case 0x16a0: // steel wall (right/top small)
6216 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6219 case 0x16a1: // steel wall (left/top small)
6220 element = EL_DC_STEELWALL_1_TOPLEFT_2;
6223 case 0x16a2: // steel wall (left/right)
6224 element = EL_DC_STEELWALL_1_VERTICAL;
6227 case 0x16a3: // steel wall (top/bottom)
6228 element = EL_DC_STEELWALL_1_HORIZONTAL;
6231 case 0x16a4: // steel wall 2 (left end)
6232 element = EL_DC_STEELWALL_2_LEFT;
6235 case 0x16a5: // steel wall 2 (right end)
6236 element = EL_DC_STEELWALL_2_RIGHT;
6239 case 0x16a6: // steel wall 2 (top end)
6240 element = EL_DC_STEELWALL_2_TOP;
6243 case 0x16a7: // steel wall 2 (bottom end)
6244 element = EL_DC_STEELWALL_2_BOTTOM;
6247 case 0x16a8: // steel wall 2 (left/right)
6248 element = EL_DC_STEELWALL_2_HORIZONTAL;
6251 case 0x16a9: // steel wall 2 (up/down)
6252 element = EL_DC_STEELWALL_2_VERTICAL;
6255 case 0x16aa: // steel wall 2 (mid)
6256 element = EL_DC_STEELWALL_2_MIDDLE;
6260 element = EL_SIGN_EXCLAMATION;
6264 element = EL_SIGN_RADIOACTIVITY;
6268 element = EL_SIGN_STOP;
6272 element = EL_SIGN_WHEELCHAIR;
6276 element = EL_SIGN_PARKING;
6280 element = EL_SIGN_NO_ENTRY;
6284 element = EL_SIGN_HEART;
6288 element = EL_SIGN_GIVE_WAY;
6292 element = EL_SIGN_ENTRY_FORBIDDEN;
6296 element = EL_SIGN_EMERGENCY_EXIT;
6300 element = EL_SIGN_YIN_YANG;
6304 element = EL_WALL_EMERALD;
6308 element = EL_WALL_DIAMOND;
6312 element = EL_WALL_PEARL;
6316 element = EL_WALL_CRYSTAL;
6320 element = EL_INVISIBLE_WALL;
6324 element = EL_INVISIBLE_STEELWALL;
6328 // EL_INVISIBLE_SAND
6331 element = EL_LIGHT_SWITCH;
6335 element = EL_ENVELOPE_1;
6339 if (element >= 0x0117 && element <= 0x036e) // (?)
6340 element = EL_DIAMOND;
6341 else if (element >= 0x042d && element <= 0x0684) // (?)
6342 element = EL_EMERALD;
6343 else if (element >= 0x157c && element <= 0x158b)
6345 else if (element >= 0x1590 && element <= 0x159f)
6346 element = EL_DC_LANDMINE;
6347 else if (element >= 0x16bc && element <= 0x16cb)
6348 element = EL_INVISIBLE_SAND;
6351 Warn("unknown Diamond Caves element 0x%04x", element);
6353 element = EL_UNKNOWN;
6358 return getMappedElement(element);
6361 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6363 byte header[DC_LEVEL_HEADER_SIZE];
6365 int envelope_header_pos = 62;
6366 int envelope_content_pos = 94;
6367 int level_name_pos = 251;
6368 int level_author_pos = 292;
6369 int envelope_header_len;
6370 int envelope_content_len;
6372 int level_author_len;
6374 int num_yamyam_contents;
6377 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6379 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6381 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6383 header[i * 2 + 0] = header_word >> 8;
6384 header[i * 2 + 1] = header_word & 0xff;
6387 // read some values from level header to check level decoding integrity
6388 fieldx = header[6] | (header[7] << 8);
6389 fieldy = header[8] | (header[9] << 8);
6390 num_yamyam_contents = header[60] | (header[61] << 8);
6392 // do some simple sanity checks to ensure that level was correctly decoded
6393 if (fieldx < 1 || fieldx > 256 ||
6394 fieldy < 1 || fieldy > 256 ||
6395 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6397 level->no_valid_file = TRUE;
6399 Warn("cannot decode level from stream -- using empty level");
6404 // maximum envelope header size is 31 bytes
6405 envelope_header_len = header[envelope_header_pos];
6406 // maximum envelope content size is 110 (156?) bytes
6407 envelope_content_len = header[envelope_content_pos];
6409 // maximum level title size is 40 bytes
6410 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6411 // maximum level author size is 30 (51?) bytes
6412 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6416 for (i = 0; i < envelope_header_len; i++)
6417 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6418 level->envelope[0].text[envelope_size++] =
6419 header[envelope_header_pos + 1 + i];
6421 if (envelope_header_len > 0 && envelope_content_len > 0)
6423 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6424 level->envelope[0].text[envelope_size++] = '\n';
6425 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6426 level->envelope[0].text[envelope_size++] = '\n';
6429 for (i = 0; i < envelope_content_len; i++)
6430 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6431 level->envelope[0].text[envelope_size++] =
6432 header[envelope_content_pos + 1 + i];
6434 level->envelope[0].text[envelope_size] = '\0';
6436 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6437 level->envelope[0].ysize = 10;
6438 level->envelope[0].autowrap = TRUE;
6439 level->envelope[0].centered = TRUE;
6441 for (i = 0; i < level_name_len; i++)
6442 level->name[i] = header[level_name_pos + 1 + i];
6443 level->name[level_name_len] = '\0';
6445 for (i = 0; i < level_author_len; i++)
6446 level->author[i] = header[level_author_pos + 1 + i];
6447 level->author[level_author_len] = '\0';
6449 num_yamyam_contents = header[60] | (header[61] << 8);
6450 level->num_yamyam_contents =
6451 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6453 for (i = 0; i < num_yamyam_contents; i++)
6455 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6457 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6458 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6460 if (i < MAX_ELEMENT_CONTENTS)
6461 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6465 fieldx = header[6] | (header[7] << 8);
6466 fieldy = header[8] | (header[9] << 8);
6467 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6468 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6470 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6472 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6473 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6475 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6476 level->field[x][y] = getMappedElement_DC(element_dc);
6479 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6480 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6481 level->field[x][y] = EL_PLAYER_1;
6483 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6484 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6485 level->field[x][y] = EL_PLAYER_2;
6487 level->gems_needed = header[18] | (header[19] << 8);
6489 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6490 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6491 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6492 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6493 level->score[SC_NUT] = header[28] | (header[29] << 8);
6494 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6495 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6496 level->score[SC_BUG] = header[34] | (header[35] << 8);
6497 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6498 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6499 level->score[SC_KEY] = header[40] | (header[41] << 8);
6500 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6502 level->time = header[44] | (header[45] << 8);
6504 level->amoeba_speed = header[46] | (header[47] << 8);
6505 level->time_light = header[48] | (header[49] << 8);
6506 level->time_timegate = header[50] | (header[51] << 8);
6507 level->time_wheel = header[52] | (header[53] << 8);
6508 level->time_magic_wall = header[54] | (header[55] << 8);
6509 level->extra_time = header[56] | (header[57] << 8);
6510 level->shield_normal_time = header[58] | (header[59] << 8);
6512 // shield and extra time elements do not have a score
6513 level->score[SC_SHIELD] = 0;
6514 level->extra_time_score = 0;
6516 // set time for normal and deadly shields to the same value
6517 level->shield_deadly_time = level->shield_normal_time;
6519 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6520 // can slip down from flat walls, like normal walls and steel walls
6521 level->em_slippery_gems = TRUE;
6523 // time score is counted for each 10 seconds left in Diamond Caves levels
6524 level->time_score_base = 10;
6527 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6528 struct LevelFileInfo *level_file_info,
6529 boolean level_info_only)
6531 char *filename = level_file_info->filename;
6533 int num_magic_bytes = 8;
6534 char magic_bytes[num_magic_bytes + 1];
6535 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6537 if (!(file = openFile(filename, MODE_READ)))
6539 level->no_valid_file = TRUE;
6541 if (!level_info_only)
6542 Warn("cannot read level '%s' -- using empty level", filename);
6547 // fseek(file, 0x0000, SEEK_SET);
6549 if (level_file_info->packed)
6551 // read "magic bytes" from start of file
6552 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6553 magic_bytes[0] = '\0';
6555 // check "magic bytes" for correct file format
6556 if (!strPrefix(magic_bytes, "DC2"))
6558 level->no_valid_file = TRUE;
6560 Warn("unknown DC level file '%s' -- using empty level", filename);
6565 if (strPrefix(magic_bytes, "DC2Win95") ||
6566 strPrefix(magic_bytes, "DC2Win98"))
6568 int position_first_level = 0x00fa;
6569 int extra_bytes = 4;
6572 // advance file stream to first level inside the level package
6573 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6575 // each block of level data is followed by block of non-level data
6576 num_levels_to_skip *= 2;
6578 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6579 while (num_levels_to_skip >= 0)
6581 // advance file stream to next level inside the level package
6582 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6584 level->no_valid_file = TRUE;
6586 Warn("cannot fseek in file '%s' -- using empty level", filename);
6591 // skip apparently unused extra bytes following each level
6592 ReadUnusedBytesFromFile(file, extra_bytes);
6594 // read size of next level in level package
6595 skip_bytes = getFile32BitLE(file);
6597 num_levels_to_skip--;
6602 level->no_valid_file = TRUE;
6604 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6610 LoadLevelFromFileStream_DC(file, level);
6616 // ----------------------------------------------------------------------------
6617 // functions for loading SB level
6618 // ----------------------------------------------------------------------------
6620 int getMappedElement_SB(int element_ascii, boolean use_ces)
6628 sb_element_mapping[] =
6630 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6631 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6632 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6633 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6634 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6635 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6636 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6637 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6644 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6645 if (element_ascii == sb_element_mapping[i].ascii)
6646 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6648 return EL_UNDEFINED;
6651 static void SetLevelSettings_SB(struct LevelInfo *level)
6655 level->use_step_counter = TRUE;
6658 level->score[SC_TIME_BONUS] = 0;
6659 level->time_score_base = 1;
6660 level->rate_time_over_score = TRUE;
6663 level->auto_exit_sokoban = TRUE;
6666 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6667 struct LevelFileInfo *level_file_info,
6668 boolean level_info_only)
6670 char *filename = level_file_info->filename;
6671 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6672 char last_comment[MAX_LINE_LEN];
6673 char level_name[MAX_LINE_LEN];
6676 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6677 boolean read_continued_line = FALSE;
6678 boolean reading_playfield = FALSE;
6679 boolean got_valid_playfield_line = FALSE;
6680 boolean invalid_playfield_char = FALSE;
6681 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6682 int file_level_nr = 0;
6683 int x = 0, y = 0; // initialized to make compilers happy
6685 last_comment[0] = '\0';
6686 level_name[0] = '\0';
6688 if (!(file = openFile(filename, MODE_READ)))
6690 level->no_valid_file = TRUE;
6692 if (!level_info_only)
6693 Warn("cannot read level '%s' -- using empty level", filename);
6698 while (!checkEndOfFile(file))
6700 // level successfully read, but next level may follow here
6701 if (!got_valid_playfield_line && reading_playfield)
6703 // read playfield from single level file -- skip remaining file
6704 if (!level_file_info->packed)
6707 if (file_level_nr >= num_levels_to_skip)
6712 last_comment[0] = '\0';
6713 level_name[0] = '\0';
6715 reading_playfield = FALSE;
6718 got_valid_playfield_line = FALSE;
6720 // read next line of input file
6721 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6724 // cut trailing line break (this can be newline and/or carriage return)
6725 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6726 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6729 // copy raw input line for later use (mainly debugging output)
6730 strcpy(line_raw, line);
6732 if (read_continued_line)
6734 // append new line to existing line, if there is enough space
6735 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6736 strcat(previous_line, line_ptr);
6738 strcpy(line, previous_line); // copy storage buffer to line
6740 read_continued_line = FALSE;
6743 // if the last character is '\', continue at next line
6744 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6746 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6747 strcpy(previous_line, line); // copy line to storage buffer
6749 read_continued_line = TRUE;
6755 if (line[0] == '\0')
6758 // extract comment text from comment line
6761 for (line_ptr = line; *line_ptr; line_ptr++)
6762 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6765 strcpy(last_comment, line_ptr);
6770 // extract level title text from line containing level title
6771 if (line[0] == '\'')
6773 strcpy(level_name, &line[1]);
6775 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6776 level_name[strlen(level_name) - 1] = '\0';
6781 // skip lines containing only spaces (or empty lines)
6782 for (line_ptr = line; *line_ptr; line_ptr++)
6783 if (*line_ptr != ' ')
6785 if (*line_ptr == '\0')
6788 // at this point, we have found a line containing part of a playfield
6790 got_valid_playfield_line = TRUE;
6792 if (!reading_playfield)
6794 reading_playfield = TRUE;
6795 invalid_playfield_char = FALSE;
6797 for (x = 0; x < MAX_LEV_FIELDX; x++)
6798 for (y = 0; y < MAX_LEV_FIELDY; y++)
6799 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6804 // start with topmost tile row
6808 // skip playfield line if larger row than allowed
6809 if (y >= MAX_LEV_FIELDY)
6812 // start with leftmost tile column
6815 // read playfield elements from line
6816 for (line_ptr = line; *line_ptr; line_ptr++)
6818 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6820 // stop parsing playfield line if larger column than allowed
6821 if (x >= MAX_LEV_FIELDX)
6824 if (mapped_sb_element == EL_UNDEFINED)
6826 invalid_playfield_char = TRUE;
6831 level->field[x][y] = mapped_sb_element;
6833 // continue with next tile column
6836 level->fieldx = MAX(x, level->fieldx);
6839 if (invalid_playfield_char)
6841 // if first playfield line, treat invalid lines as comment lines
6843 reading_playfield = FALSE;
6848 // continue with next tile row
6856 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6857 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6859 if (!reading_playfield)
6861 level->no_valid_file = TRUE;
6863 Warn("cannot read level '%s' -- using empty level", filename);
6868 if (*level_name != '\0')
6870 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6871 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6873 else if (*last_comment != '\0')
6875 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6876 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6880 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6883 // set all empty fields beyond the border walls to invisible steel wall
6884 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6886 if ((x == 0 || x == level->fieldx - 1 ||
6887 y == 0 || y == level->fieldy - 1) &&
6888 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6889 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6890 level->field, level->fieldx, level->fieldy);
6893 // set special level settings for Sokoban levels
6894 SetLevelSettings_SB(level);
6896 if (load_xsb_to_ces)
6898 // special global settings can now be set in level template
6899 level->use_custom_template = TRUE;
6904 // -------------------------------------------------------------------------
6905 // functions for handling native levels
6906 // -------------------------------------------------------------------------
6908 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6909 struct LevelFileInfo *level_file_info,
6910 boolean level_info_only)
6914 // determine position of requested level inside level package
6915 if (level_file_info->packed)
6916 pos = level_file_info->nr - leveldir_current->first_level;
6918 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6919 level->no_valid_file = TRUE;
6922 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6923 struct LevelFileInfo *level_file_info,
6924 boolean level_info_only)
6926 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6927 level->no_valid_file = TRUE;
6930 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6931 struct LevelFileInfo *level_file_info,
6932 boolean level_info_only)
6936 // determine position of requested level inside level package
6937 if (level_file_info->packed)
6938 pos = level_file_info->nr - leveldir_current->first_level;
6940 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6941 level->no_valid_file = TRUE;
6944 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6945 struct LevelFileInfo *level_file_info,
6946 boolean level_info_only)
6948 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6949 level->no_valid_file = TRUE;
6952 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6954 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6955 CopyNativeLevel_RND_to_BD(level);
6956 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6957 CopyNativeLevel_RND_to_EM(level);
6958 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6959 CopyNativeLevel_RND_to_SP(level);
6960 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6961 CopyNativeLevel_RND_to_MM(level);
6964 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6966 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6967 CopyNativeLevel_BD_to_RND(level);
6968 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6969 CopyNativeLevel_EM_to_RND(level);
6970 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6971 CopyNativeLevel_SP_to_RND(level);
6972 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6973 CopyNativeLevel_MM_to_RND(level);
6976 void SaveNativeLevel(struct LevelInfo *level)
6978 // saving native level files only supported for some game engines
6979 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6980 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6983 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6984 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6985 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6986 char *filename = getLevelFilenameFromBasename(basename);
6988 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6991 boolean success = FALSE;
6993 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6995 CopyNativeLevel_RND_to_BD(level);
6996 // CopyNativeTape_RND_to_BD(level);
6998 success = SaveNativeLevel_BD(filename);
7000 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7002 CopyNativeLevel_RND_to_SP(level);
7003 CopyNativeTape_RND_to_SP(level);
7005 success = SaveNativeLevel_SP(filename);
7009 Request("Native level file saved!", REQ_CONFIRM);
7011 Request("Failed to save native level file!", REQ_CONFIRM);
7015 // ----------------------------------------------------------------------------
7016 // functions for loading generic level
7017 // ----------------------------------------------------------------------------
7019 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7020 struct LevelFileInfo *level_file_info,
7021 boolean level_info_only)
7023 // always start with reliable default values
7024 setLevelInfoToDefaults(level, level_info_only, TRUE);
7026 switch (level_file_info->type)
7028 case LEVEL_FILE_TYPE_RND:
7029 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7032 case LEVEL_FILE_TYPE_BD:
7033 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7034 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7037 case LEVEL_FILE_TYPE_EM:
7038 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7039 level->game_engine_type = GAME_ENGINE_TYPE_EM;
7042 case LEVEL_FILE_TYPE_SP:
7043 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7044 level->game_engine_type = GAME_ENGINE_TYPE_SP;
7047 case LEVEL_FILE_TYPE_MM:
7048 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7049 level->game_engine_type = GAME_ENGINE_TYPE_MM;
7052 case LEVEL_FILE_TYPE_DC:
7053 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7056 case LEVEL_FILE_TYPE_SB:
7057 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7061 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7065 // if level file is invalid, restore level structure to default values
7066 if (level->no_valid_file)
7067 setLevelInfoToDefaults(level, level_info_only, FALSE);
7069 if (check_special_flags("use_native_bd_game_engine"))
7070 level->game_engine_type = GAME_ENGINE_TYPE_BD;
7072 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7073 level->game_engine_type = GAME_ENGINE_TYPE_RND;
7075 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7076 CopyNativeLevel_Native_to_RND(level);
7079 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7081 static struct LevelFileInfo level_file_info;
7083 // always start with reliable default values
7084 setFileInfoToDefaults(&level_file_info);
7086 level_file_info.nr = 0; // unknown level number
7087 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
7089 setString(&level_file_info.filename, filename);
7091 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7094 static void LoadLevel_InitVersion(struct LevelInfo *level)
7098 if (leveldir_current == NULL) // only when dumping level
7101 // all engine modifications also valid for levels which use latest engine
7102 if (level->game_version < VERSION_IDENT(3,2,0,5))
7104 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7105 level->time_score_base = 10;
7108 if (leveldir_current->latest_engine)
7110 // ---------- use latest game engine --------------------------------------
7112 /* For all levels which are forced to use the latest game engine version
7113 (normally all but user contributed, private and undefined levels), set
7114 the game engine version to the actual version; this allows for actual
7115 corrections in the game engine to take effect for existing, converted
7116 levels (from "classic" or other existing games) to make the emulation
7117 of the corresponding game more accurate, while (hopefully) not breaking
7118 existing levels created from other players. */
7120 level->game_version = GAME_VERSION_ACTUAL;
7122 /* Set special EM style gems behaviour: EM style gems slip down from
7123 normal, steel and growing wall. As this is a more fundamental change,
7124 it seems better to set the default behaviour to "off" (as it is more
7125 natural) and make it configurable in the level editor (as a property
7126 of gem style elements). Already existing converted levels (neither
7127 private nor contributed levels) are changed to the new behaviour. */
7129 if (level->file_version < FILE_VERSION_2_0)
7130 level->em_slippery_gems = TRUE;
7135 // ---------- use game engine the level was created with --------------------
7137 /* For all levels which are not forced to use the latest game engine
7138 version (normally user contributed, private and undefined levels),
7139 use the version of the game engine the levels were created for.
7141 Since 2.0.1, the game engine version is now directly stored
7142 in the level file (chunk "VERS"), so there is no need anymore
7143 to set the game version from the file version (except for old,
7144 pre-2.0 levels, where the game version is still taken from the
7145 file format version used to store the level -- see above). */
7147 // player was faster than enemies in 1.0.0 and before
7148 if (level->file_version == FILE_VERSION_1_0)
7149 for (i = 0; i < MAX_PLAYERS; i++)
7150 level->initial_player_stepsize[i] = STEPSIZE_FAST;
7152 // default behaviour for EM style gems was "slippery" only in 2.0.1
7153 if (level->game_version == VERSION_IDENT(2,0,1,0))
7154 level->em_slippery_gems = TRUE;
7156 // springs could be pushed over pits before (pre-release version) 2.2.0
7157 if (level->game_version < VERSION_IDENT(2,2,0,0))
7158 level->use_spring_bug = TRUE;
7160 if (level->game_version < VERSION_IDENT(3,2,0,5))
7162 // time orb caused limited time in endless time levels before 3.2.0-5
7163 level->use_time_orb_bug = TRUE;
7165 // default behaviour for snapping was "no snap delay" before 3.2.0-5
7166 level->block_snap_field = FALSE;
7168 // extra time score was same value as time left score before 3.2.0-5
7169 level->extra_time_score = level->score[SC_TIME_BONUS];
7172 if (level->game_version < VERSION_IDENT(3,2,0,7))
7174 // default behaviour for snapping was "not continuous" before 3.2.0-7
7175 level->continuous_snapping = FALSE;
7178 // only few elements were able to actively move into acid before 3.1.0
7179 // trigger settings did not exist before 3.1.0; set to default "any"
7180 if (level->game_version < VERSION_IDENT(3,1,0,0))
7182 // correct "can move into acid" settings (all zero in old levels)
7184 level->can_move_into_acid_bits = 0; // nothing can move into acid
7185 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7187 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
7188 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7189 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
7190 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
7192 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7193 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7195 // correct trigger settings (stored as zero == "none" in old levels)
7197 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7199 int element = EL_CUSTOM_START + i;
7200 struct ElementInfo *ei = &element_info[element];
7202 for (j = 0; j < ei->num_change_pages; j++)
7204 struct ElementChangeInfo *change = &ei->change_page[j];
7206 change->trigger_player = CH_PLAYER_ANY;
7207 change->trigger_page = CH_PAGE_ANY;
7212 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7214 int element = EL_CUSTOM_256;
7215 struct ElementInfo *ei = &element_info[element];
7216 struct ElementChangeInfo *change = &ei->change_page[0];
7218 /* This is needed to fix a problem that was caused by a bugfix in function
7219 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7220 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7221 not replace walkable elements, but instead just placed the player on it,
7222 without placing the Sokoban field under the player). Unfortunately, this
7223 breaks "Snake Bite" style levels when the snake is halfway through a door
7224 that just closes (the snake head is still alive and can be moved in this
7225 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7226 player (without Sokoban element) which then gets killed as designed). */
7228 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7229 strncmp(ei->description, "pause b4 death", 14) == 0) &&
7230 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7231 change->target_element = EL_PLAYER_1;
7234 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7235 if (level->game_version < VERSION_IDENT(3,2,5,0))
7237 /* This is needed to fix a problem that was caused by a bugfix in function
7238 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7239 corrects the behaviour when a custom element changes to another custom
7240 element with a higher element number that has change actions defined.
7241 Normally, only one change per frame is allowed for custom elements.
7242 Therefore, it is checked if a custom element already changed in the
7243 current frame; if it did, subsequent changes are suppressed.
7244 Unfortunately, this is only checked for element changes, but not for
7245 change actions, which are still executed. As the function above loops
7246 through all custom elements from lower to higher, an element change
7247 resulting in a lower CE number won't be checked again, while a target
7248 element with a higher number will also be checked, and potential change
7249 actions will get executed for this CE, too (which is wrong), while
7250 further changes are ignored (which is correct). As this bugfix breaks
7251 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7252 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7253 behaviour for existing levels and tapes that make use of this bug */
7255 level->use_action_after_change_bug = TRUE;
7258 // not centering level after relocating player was default only in 3.2.3
7259 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
7260 level->shifted_relocation = TRUE;
7262 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7263 if (level->game_version < VERSION_IDENT(3,2,6,0))
7264 level->em_explodes_by_fire = TRUE;
7266 // levels were solved by the first player entering an exit up to 4.1.0.0
7267 if (level->game_version <= VERSION_IDENT(4,1,0,0))
7268 level->solved_by_one_player = TRUE;
7270 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7271 if (level->game_version < VERSION_IDENT(4,1,1,1))
7272 level->use_life_bugs = TRUE;
7274 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7275 if (level->game_version < VERSION_IDENT(4,1,1,1))
7276 level->sb_objects_needed = FALSE;
7278 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7279 if (level->game_version <= VERSION_IDENT(4,2,2,0))
7280 level->finish_dig_collect = FALSE;
7282 // CE changing to player was kept under the player if walkable up to 4.2.3.1
7283 if (level->game_version <= VERSION_IDENT(4,2,3,1))
7284 level->keep_walkable_ce = TRUE;
7287 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7289 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
7292 // check if this level is (not) a Sokoban level
7293 for (y = 0; y < level->fieldy; y++)
7294 for (x = 0; x < level->fieldx; x++)
7295 if (!IS_SB_ELEMENT(Tile[x][y]))
7296 is_sokoban_level = FALSE;
7298 if (is_sokoban_level)
7300 // set special level settings for Sokoban levels
7301 SetLevelSettings_SB(level);
7305 static void LoadLevel_InitSettings(struct LevelInfo *level)
7307 // adjust level settings for (non-native) Sokoban-style levels
7308 LoadLevel_InitSettings_SB(level);
7310 // rename levels with title "nameless level" or if renaming is forced
7311 if (leveldir_current->empty_level_name != NULL &&
7312 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7313 leveldir_current->force_level_name))
7314 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7315 leveldir_current->empty_level_name, level_nr);
7318 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7322 // map elements that have changed in newer versions
7323 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7324 level->game_version);
7325 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7326 for (x = 0; x < 3; x++)
7327 for (y = 0; y < 3; y++)
7328 level->yamyam_content[i].e[x][y] =
7329 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7330 level->game_version);
7334 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7338 // map custom element change events that have changed in newer versions
7339 // (these following values were accidentally changed in version 3.0.1)
7340 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7341 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7343 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7345 int element = EL_CUSTOM_START + i;
7347 // order of checking and copying events to be mapped is important
7348 // (do not change the start and end value -- they are constant)
7349 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7351 if (HAS_CHANGE_EVENT(element, j - 2))
7353 SET_CHANGE_EVENT(element, j - 2, FALSE);
7354 SET_CHANGE_EVENT(element, j, TRUE);
7358 // order of checking and copying events to be mapped is important
7359 // (do not change the start and end value -- they are constant)
7360 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7362 if (HAS_CHANGE_EVENT(element, j - 1))
7364 SET_CHANGE_EVENT(element, j - 1, FALSE);
7365 SET_CHANGE_EVENT(element, j, TRUE);
7371 // initialize "can_change" field for old levels with only one change page
7372 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7374 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7376 int element = EL_CUSTOM_START + i;
7378 if (CAN_CHANGE(element))
7379 element_info[element].change->can_change = TRUE;
7383 // correct custom element values (for old levels without these options)
7384 if (level->game_version < VERSION_IDENT(3,1,1,0))
7386 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7388 int element = EL_CUSTOM_START + i;
7389 struct ElementInfo *ei = &element_info[element];
7391 if (ei->access_direction == MV_NO_DIRECTION)
7392 ei->access_direction = MV_ALL_DIRECTIONS;
7396 // correct custom element values (fix invalid values for all versions)
7399 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7401 int element = EL_CUSTOM_START + i;
7402 struct ElementInfo *ei = &element_info[element];
7404 for (j = 0; j < ei->num_change_pages; j++)
7406 struct ElementChangeInfo *change = &ei->change_page[j];
7408 if (change->trigger_player == CH_PLAYER_NONE)
7409 change->trigger_player = CH_PLAYER_ANY;
7411 if (change->trigger_side == CH_SIDE_NONE)
7412 change->trigger_side = CH_SIDE_ANY;
7417 // initialize "can_explode" field for old levels which did not store this
7418 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7419 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7421 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7423 int element = EL_CUSTOM_START + i;
7425 if (EXPLODES_1X1_OLD(element))
7426 element_info[element].explosion_type = EXPLODES_1X1;
7428 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7429 EXPLODES_SMASHED(element) ||
7430 EXPLODES_IMPACT(element)));
7434 // correct previously hard-coded move delay values for maze runner style
7435 if (level->game_version < VERSION_IDENT(3,1,1,0))
7437 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7439 int element = EL_CUSTOM_START + i;
7441 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7443 // previously hard-coded and therefore ignored
7444 element_info[element].move_delay_fixed = 9;
7445 element_info[element].move_delay_random = 0;
7450 // set some other uninitialized values of custom elements in older levels
7451 if (level->game_version < VERSION_IDENT(3,1,0,0))
7453 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7455 int element = EL_CUSTOM_START + i;
7457 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7459 element_info[element].explosion_delay = 17;
7460 element_info[element].ignition_delay = 8;
7464 // set mouse click change events to work for left/middle/right mouse button
7465 if (level->game_version < VERSION_IDENT(4,2,3,0))
7467 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7469 int element = EL_CUSTOM_START + i;
7470 struct ElementInfo *ei = &element_info[element];
7472 for (j = 0; j < ei->num_change_pages; j++)
7474 struct ElementChangeInfo *change = &ei->change_page[j];
7476 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7477 change->has_event[CE_PRESSED_BY_MOUSE] ||
7478 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7479 change->has_event[CE_MOUSE_PRESSED_ON_X])
7480 change->trigger_side = CH_SIDE_ANY;
7486 static void LoadLevel_InitElements(struct LevelInfo *level)
7488 LoadLevel_InitStandardElements(level);
7490 if (level->file_has_custom_elements)
7491 LoadLevel_InitCustomElements(level);
7493 // initialize element properties for level editor etc.
7494 InitElementPropertiesEngine(level->game_version);
7495 InitElementPropertiesGfxElement();
7498 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7502 // map elements that have changed in newer versions
7503 for (y = 0; y < level->fieldy; y++)
7504 for (x = 0; x < level->fieldx; x++)
7505 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7506 level->game_version);
7508 // clear unused playfield data (nicer if level gets resized in editor)
7509 for (x = 0; x < MAX_LEV_FIELDX; x++)
7510 for (y = 0; y < MAX_LEV_FIELDY; y++)
7511 if (x >= level->fieldx || y >= level->fieldy)
7512 level->field[x][y] = EL_EMPTY;
7514 // copy elements to runtime playfield array
7515 for (x = 0; x < MAX_LEV_FIELDX; x++)
7516 for (y = 0; y < MAX_LEV_FIELDY; y++)
7517 Tile[x][y] = level->field[x][y];
7519 // initialize level size variables for faster access
7520 lev_fieldx = level->fieldx;
7521 lev_fieldy = level->fieldy;
7523 // determine border element for this level
7524 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7525 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7530 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7532 struct LevelFileInfo *level_file_info = &level->file_info;
7534 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7535 CopyNativeLevel_RND_to_Native(level);
7538 static void LoadLevelTemplate_LoadAndInit(void)
7540 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7542 LoadLevel_InitVersion(&level_template);
7543 LoadLevel_InitElements(&level_template);
7544 LoadLevel_InitSettings(&level_template);
7546 ActivateLevelTemplate();
7549 void LoadLevelTemplate(int nr)
7551 if (!fileExists(getGlobalLevelTemplateFilename()))
7553 Warn("no level template found for this level");
7558 setLevelFileInfo(&level_template.file_info, nr);
7560 LoadLevelTemplate_LoadAndInit();
7563 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7565 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7567 LoadLevelTemplate_LoadAndInit();
7570 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7572 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7574 if (level.use_custom_template)
7576 if (network_level != NULL)
7577 LoadNetworkLevelTemplate(network_level);
7579 LoadLevelTemplate(-1);
7582 LoadLevel_InitVersion(&level);
7583 LoadLevel_InitElements(&level);
7584 LoadLevel_InitPlayfield(&level);
7585 LoadLevel_InitSettings(&level);
7587 LoadLevel_InitNativeEngines(&level);
7590 void LoadLevel(int nr)
7592 SetLevelSetInfo(leveldir_current->identifier, nr);
7594 setLevelFileInfo(&level.file_info, nr);
7596 LoadLevel_LoadAndInit(NULL);
7599 void LoadLevelInfoOnly(int nr)
7601 setLevelFileInfo(&level.file_info, nr);
7603 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7606 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7608 SetLevelSetInfo(network_level->leveldir_identifier,
7609 network_level->file_info.nr);
7611 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7613 LoadLevel_LoadAndInit(network_level);
7616 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7620 chunk_size += putFileVersion(file, level->file_version);
7621 chunk_size += putFileVersion(file, level->game_version);
7626 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7630 chunk_size += putFile16BitBE(file, level->creation_date.year);
7631 chunk_size += putFile8Bit(file, level->creation_date.month);
7632 chunk_size += putFile8Bit(file, level->creation_date.day);
7637 #if ENABLE_HISTORIC_CHUNKS
7638 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7642 putFile8Bit(file, level->fieldx);
7643 putFile8Bit(file, level->fieldy);
7645 putFile16BitBE(file, level->time);
7646 putFile16BitBE(file, level->gems_needed);
7648 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7649 putFile8Bit(file, level->name[i]);
7651 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7652 putFile8Bit(file, level->score[i]);
7654 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7655 for (y = 0; y < 3; y++)
7656 for (x = 0; x < 3; x++)
7657 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7658 level->yamyam_content[i].e[x][y]));
7659 putFile8Bit(file, level->amoeba_speed);
7660 putFile8Bit(file, level->time_magic_wall);
7661 putFile8Bit(file, level->time_wheel);
7662 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7663 level->amoeba_content));
7664 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7665 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7666 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7667 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7669 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7671 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7672 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7673 putFile32BitBE(file, level->can_move_into_acid_bits);
7674 putFile8Bit(file, level->dont_collide_with_bits);
7676 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7677 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7679 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7680 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7681 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7683 putFile8Bit(file, level->game_engine_type);
7685 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7689 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7694 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7695 chunk_size += putFile8Bit(file, level->name[i]);
7700 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7705 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7706 chunk_size += putFile8Bit(file, level->author[i]);
7711 #if ENABLE_HISTORIC_CHUNKS
7712 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7717 for (y = 0; y < level->fieldy; y++)
7718 for (x = 0; x < level->fieldx; x++)
7719 if (level->encoding_16bit_field)
7720 chunk_size += putFile16BitBE(file, level->field[x][y]);
7722 chunk_size += putFile8Bit(file, level->field[x][y]);
7728 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7733 for (y = 0; y < level->fieldy; y++)
7734 for (x = 0; x < level->fieldx; x++)
7735 chunk_size += putFile16BitBE(file, level->field[x][y]);
7740 #if ENABLE_HISTORIC_CHUNKS
7741 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7745 putFile8Bit(file, EL_YAMYAM);
7746 putFile8Bit(file, level->num_yamyam_contents);
7747 putFile8Bit(file, 0);
7748 putFile8Bit(file, 0);
7750 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7751 for (y = 0; y < 3; y++)
7752 for (x = 0; x < 3; x++)
7753 if (level->encoding_16bit_field)
7754 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7756 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7760 #if ENABLE_HISTORIC_CHUNKS
7761 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7764 int num_contents, content_xsize, content_ysize;
7765 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7767 if (element == EL_YAMYAM)
7769 num_contents = level->num_yamyam_contents;
7773 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7774 for (y = 0; y < 3; y++)
7775 for (x = 0; x < 3; x++)
7776 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7778 else if (element == EL_BD_AMOEBA)
7784 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7785 for (y = 0; y < 3; y++)
7786 for (x = 0; x < 3; x++)
7787 content_array[i][x][y] = EL_EMPTY;
7788 content_array[0][0][0] = level->amoeba_content;
7792 // chunk header already written -- write empty chunk data
7793 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7795 Warn("cannot save content for element '%d'", element);
7800 putFile16BitBE(file, element);
7801 putFile8Bit(file, num_contents);
7802 putFile8Bit(file, content_xsize);
7803 putFile8Bit(file, content_ysize);
7805 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7807 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7808 for (y = 0; y < 3; y++)
7809 for (x = 0; x < 3; x++)
7810 putFile16BitBE(file, content_array[i][x][y]);
7814 #if ENABLE_HISTORIC_CHUNKS
7815 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7817 int envelope_nr = element - EL_ENVELOPE_1;
7818 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7822 chunk_size += putFile16BitBE(file, element);
7823 chunk_size += putFile16BitBE(file, envelope_len);
7824 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7825 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7827 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7828 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7830 for (i = 0; i < envelope_len; i++)
7831 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7837 #if ENABLE_HISTORIC_CHUNKS
7838 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7839 int num_changed_custom_elements)
7843 putFile16BitBE(file, num_changed_custom_elements);
7845 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7847 int element = EL_CUSTOM_START + i;
7849 struct ElementInfo *ei = &element_info[element];
7851 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7853 if (check < num_changed_custom_elements)
7855 putFile16BitBE(file, element);
7856 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7863 if (check != num_changed_custom_elements) // should not happen
7864 Warn("inconsistent number of custom element properties");
7868 #if ENABLE_HISTORIC_CHUNKS
7869 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7870 int num_changed_custom_elements)
7874 putFile16BitBE(file, num_changed_custom_elements);
7876 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7878 int element = EL_CUSTOM_START + i;
7880 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7882 if (check < num_changed_custom_elements)
7884 putFile16BitBE(file, element);
7885 putFile16BitBE(file, element_info[element].change->target_element);
7892 if (check != num_changed_custom_elements) // should not happen
7893 Warn("inconsistent number of custom target elements");
7897 #if ENABLE_HISTORIC_CHUNKS
7898 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7899 int num_changed_custom_elements)
7901 int i, j, x, y, check = 0;
7903 putFile16BitBE(file, num_changed_custom_elements);
7905 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7907 int element = EL_CUSTOM_START + i;
7908 struct ElementInfo *ei = &element_info[element];
7910 if (ei->modified_settings)
7912 if (check < num_changed_custom_elements)
7914 putFile16BitBE(file, element);
7916 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7917 putFile8Bit(file, ei->description[j]);
7919 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7921 // some free bytes for future properties and padding
7922 WriteUnusedBytesToFile(file, 7);
7924 putFile8Bit(file, ei->use_gfx_element);
7925 putFile16BitBE(file, ei->gfx_element_initial);
7927 putFile8Bit(file, ei->collect_score_initial);
7928 putFile8Bit(file, ei->collect_count_initial);
7930 putFile16BitBE(file, ei->push_delay_fixed);
7931 putFile16BitBE(file, ei->push_delay_random);
7932 putFile16BitBE(file, ei->move_delay_fixed);
7933 putFile16BitBE(file, ei->move_delay_random);
7935 putFile16BitBE(file, ei->move_pattern);
7936 putFile8Bit(file, ei->move_direction_initial);
7937 putFile8Bit(file, ei->move_stepsize);
7939 for (y = 0; y < 3; y++)
7940 for (x = 0; x < 3; x++)
7941 putFile16BitBE(file, ei->content.e[x][y]);
7943 putFile32BitBE(file, ei->change->events);
7945 putFile16BitBE(file, ei->change->target_element);
7947 putFile16BitBE(file, ei->change->delay_fixed);
7948 putFile16BitBE(file, ei->change->delay_random);
7949 putFile16BitBE(file, ei->change->delay_frames);
7951 putFile16BitBE(file, ei->change->initial_trigger_element);
7953 putFile8Bit(file, ei->change->explode);
7954 putFile8Bit(file, ei->change->use_target_content);
7955 putFile8Bit(file, ei->change->only_if_complete);
7956 putFile8Bit(file, ei->change->use_random_replace);
7958 putFile8Bit(file, ei->change->random_percentage);
7959 putFile8Bit(file, ei->change->replace_when);
7961 for (y = 0; y < 3; y++)
7962 for (x = 0; x < 3; x++)
7963 putFile16BitBE(file, ei->change->content.e[x][y]);
7965 putFile8Bit(file, ei->slippery_type);
7967 // some free bytes for future properties and padding
7968 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7975 if (check != num_changed_custom_elements) // should not happen
7976 Warn("inconsistent number of custom element properties");
7980 #if ENABLE_HISTORIC_CHUNKS
7981 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7983 struct ElementInfo *ei = &element_info[element];
7986 // ---------- custom element base property values (96 bytes) ----------------
7988 putFile16BitBE(file, element);
7990 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7991 putFile8Bit(file, ei->description[i]);
7993 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7995 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7997 putFile8Bit(file, ei->num_change_pages);
7999 putFile16BitBE(file, ei->ce_value_fixed_initial);
8000 putFile16BitBE(file, ei->ce_value_random_initial);
8001 putFile8Bit(file, ei->use_last_ce_value);
8003 putFile8Bit(file, ei->use_gfx_element);
8004 putFile16BitBE(file, ei->gfx_element_initial);
8006 putFile8Bit(file, ei->collect_score_initial);
8007 putFile8Bit(file, ei->collect_count_initial);
8009 putFile8Bit(file, ei->drop_delay_fixed);
8010 putFile8Bit(file, ei->push_delay_fixed);
8011 putFile8Bit(file, ei->drop_delay_random);
8012 putFile8Bit(file, ei->push_delay_random);
8013 putFile16BitBE(file, ei->move_delay_fixed);
8014 putFile16BitBE(file, ei->move_delay_random);
8016 // bits 0 - 15 of "move_pattern" ...
8017 putFile16BitBE(file, ei->move_pattern & 0xffff);
8018 putFile8Bit(file, ei->move_direction_initial);
8019 putFile8Bit(file, ei->move_stepsize);
8021 putFile8Bit(file, ei->slippery_type);
8023 for (y = 0; y < 3; y++)
8024 for (x = 0; x < 3; x++)
8025 putFile16BitBE(file, ei->content.e[x][y]);
8027 putFile16BitBE(file, ei->move_enter_element);
8028 putFile16BitBE(file, ei->move_leave_element);
8029 putFile8Bit(file, ei->move_leave_type);
8031 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8032 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8034 putFile8Bit(file, ei->access_direction);
8036 putFile8Bit(file, ei->explosion_delay);
8037 putFile8Bit(file, ei->ignition_delay);
8038 putFile8Bit(file, ei->explosion_type);
8040 // some free bytes for future custom property values and padding
8041 WriteUnusedBytesToFile(file, 1);
8043 // ---------- change page property values (48 bytes) ------------------------
8045 for (i = 0; i < ei->num_change_pages; i++)
8047 struct ElementChangeInfo *change = &ei->change_page[i];
8048 unsigned int event_bits;
8050 // bits 0 - 31 of "has_event[]" ...
8052 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8053 if (change->has_event[j])
8054 event_bits |= (1u << j);
8055 putFile32BitBE(file, event_bits);
8057 putFile16BitBE(file, change->target_element);
8059 putFile16BitBE(file, change->delay_fixed);
8060 putFile16BitBE(file, change->delay_random);
8061 putFile16BitBE(file, change->delay_frames);
8063 putFile16BitBE(file, change->initial_trigger_element);
8065 putFile8Bit(file, change->explode);
8066 putFile8Bit(file, change->use_target_content);
8067 putFile8Bit(file, change->only_if_complete);
8068 putFile8Bit(file, change->use_random_replace);
8070 putFile8Bit(file, change->random_percentage);
8071 putFile8Bit(file, change->replace_when);
8073 for (y = 0; y < 3; y++)
8074 for (x = 0; x < 3; x++)
8075 putFile16BitBE(file, change->target_content.e[x][y]);
8077 putFile8Bit(file, change->can_change);
8079 putFile8Bit(file, change->trigger_side);
8081 putFile8Bit(file, change->trigger_player);
8082 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8083 log_2(change->trigger_page)));
8085 putFile8Bit(file, change->has_action);
8086 putFile8Bit(file, change->action_type);
8087 putFile8Bit(file, change->action_mode);
8088 putFile16BitBE(file, change->action_arg);
8090 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8092 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8093 if (change->has_event[j])
8094 event_bits |= (1u << (j - 32));
8095 putFile8Bit(file, event_bits);
8100 #if ENABLE_HISTORIC_CHUNKS
8101 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8103 struct ElementInfo *ei = &element_info[element];
8104 struct ElementGroupInfo *group = ei->group;
8107 putFile16BitBE(file, element);
8109 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8110 putFile8Bit(file, ei->description[i]);
8112 putFile8Bit(file, group->num_elements);
8114 putFile8Bit(file, ei->use_gfx_element);
8115 putFile16BitBE(file, ei->gfx_element_initial);
8117 putFile8Bit(file, group->choice_mode);
8119 // some free bytes for future values and padding
8120 WriteUnusedBytesToFile(file, 3);
8122 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8123 putFile16BitBE(file, group->element[i]);
8127 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8128 boolean write_element)
8130 int save_type = entry->save_type;
8131 int data_type = entry->data_type;
8132 int conf_type = entry->conf_type;
8133 int byte_mask = conf_type & CONF_MASK_BYTES;
8134 int element = entry->element;
8135 int default_value = entry->default_value;
8137 boolean modified = FALSE;
8139 if (byte_mask != CONF_MASK_MULTI_BYTES)
8141 void *value_ptr = entry->value;
8142 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8145 // check if any settings have been modified before saving them
8146 if (value != default_value)
8149 // do not save if explicitly told or if unmodified default settings
8150 if ((save_type == SAVE_CONF_NEVER) ||
8151 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8155 num_bytes += putFile16BitBE(file, element);
8157 num_bytes += putFile8Bit(file, conf_type);
8158 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
8159 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8160 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8163 else if (data_type == TYPE_STRING)
8165 char *default_string = entry->default_string;
8166 char *string = (char *)(entry->value);
8167 int string_length = strlen(string);
8170 // check if any settings have been modified before saving them
8171 if (!strEqual(string, default_string))
8174 // do not save if explicitly told or if unmodified default settings
8175 if ((save_type == SAVE_CONF_NEVER) ||
8176 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8180 num_bytes += putFile16BitBE(file, element);
8182 num_bytes += putFile8Bit(file, conf_type);
8183 num_bytes += putFile16BitBE(file, string_length);
8185 for (i = 0; i < string_length; i++)
8186 num_bytes += putFile8Bit(file, string[i]);
8188 else if (data_type == TYPE_ELEMENT_LIST)
8190 int *element_array = (int *)(entry->value);
8191 int num_elements = *(int *)(entry->num_entities);
8194 // check if any settings have been modified before saving them
8195 for (i = 0; i < num_elements; i++)
8196 if (element_array[i] != default_value)
8199 // do not save if explicitly told or if unmodified default settings
8200 if ((save_type == SAVE_CONF_NEVER) ||
8201 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8205 num_bytes += putFile16BitBE(file, element);
8207 num_bytes += putFile8Bit(file, conf_type);
8208 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8210 for (i = 0; i < num_elements; i++)
8211 num_bytes += putFile16BitBE(file, element_array[i]);
8213 else if (data_type == TYPE_CONTENT_LIST)
8215 struct Content *content = (struct Content *)(entry->value);
8216 int num_contents = *(int *)(entry->num_entities);
8219 // check if any settings have been modified before saving them
8220 for (i = 0; i < num_contents; i++)
8221 for (y = 0; y < 3; y++)
8222 for (x = 0; x < 3; x++)
8223 if (content[i].e[x][y] != default_value)
8226 // do not save if explicitly told or if unmodified default settings
8227 if ((save_type == SAVE_CONF_NEVER) ||
8228 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8232 num_bytes += putFile16BitBE(file, element);
8234 num_bytes += putFile8Bit(file, conf_type);
8235 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8237 for (i = 0; i < num_contents; i++)
8238 for (y = 0; y < 3; y++)
8239 for (x = 0; x < 3; x++)
8240 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8246 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8251 li = *level; // copy level data into temporary buffer
8253 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8254 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8259 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8264 li = *level; // copy level data into temporary buffer
8266 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8267 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8272 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8274 int envelope_nr = element - EL_ENVELOPE_1;
8278 chunk_size += putFile16BitBE(file, element);
8280 // copy envelope data into temporary buffer
8281 xx_envelope = level->envelope[envelope_nr];
8283 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8284 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8289 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8291 struct ElementInfo *ei = &element_info[element];
8295 chunk_size += putFile16BitBE(file, element);
8297 xx_ei = *ei; // copy element data into temporary buffer
8299 // set default description string for this specific element
8300 strcpy(xx_default_description, getDefaultElementDescription(ei));
8302 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8303 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8305 for (i = 0; i < ei->num_change_pages; i++)
8307 struct ElementChangeInfo *change = &ei->change_page[i];
8309 xx_current_change_page = i;
8311 xx_change = *change; // copy change data into temporary buffer
8314 setEventBitsFromEventFlags(change);
8316 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8317 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8324 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8326 struct ElementInfo *ei = &element_info[element];
8327 struct ElementGroupInfo *group = ei->group;
8331 chunk_size += putFile16BitBE(file, element);
8333 xx_ei = *ei; // copy element data into temporary buffer
8334 xx_group = *group; // copy group data into temporary buffer
8336 // set default description string for this specific element
8337 strcpy(xx_default_description, getDefaultElementDescription(ei));
8339 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8340 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8345 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8347 struct ElementInfo *ei = &element_info[element];
8351 chunk_size += putFile16BitBE(file, element);
8353 xx_ei = *ei; // copy element data into temporary buffer
8355 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8356 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8361 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8362 boolean save_as_template)
8368 if (!(file = fopen(filename, MODE_WRITE)))
8370 Warn("cannot save level file '%s'", filename);
8375 level->file_version = FILE_VERSION_ACTUAL;
8376 level->game_version = GAME_VERSION_ACTUAL;
8378 level->creation_date = getCurrentDate();
8380 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8381 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8383 chunk_size = SaveLevel_VERS(NULL, level);
8384 putFileChunkBE(file, "VERS", chunk_size);
8385 SaveLevel_VERS(file, level);
8387 chunk_size = SaveLevel_DATE(NULL, level);
8388 putFileChunkBE(file, "DATE", chunk_size);
8389 SaveLevel_DATE(file, level);
8391 chunk_size = SaveLevel_NAME(NULL, level);
8392 putFileChunkBE(file, "NAME", chunk_size);
8393 SaveLevel_NAME(file, level);
8395 chunk_size = SaveLevel_AUTH(NULL, level);
8396 putFileChunkBE(file, "AUTH", chunk_size);
8397 SaveLevel_AUTH(file, level);
8399 chunk_size = SaveLevel_INFO(NULL, level);
8400 putFileChunkBE(file, "INFO", chunk_size);
8401 SaveLevel_INFO(file, level);
8403 chunk_size = SaveLevel_BODY(NULL, level);
8404 putFileChunkBE(file, "BODY", chunk_size);
8405 SaveLevel_BODY(file, level);
8407 chunk_size = SaveLevel_ELEM(NULL, level);
8408 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8410 putFileChunkBE(file, "ELEM", chunk_size);
8411 SaveLevel_ELEM(file, level);
8414 for (i = 0; i < NUM_ENVELOPES; i++)
8416 int element = EL_ENVELOPE_1 + i;
8418 chunk_size = SaveLevel_NOTE(NULL, level, element);
8419 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8421 putFileChunkBE(file, "NOTE", chunk_size);
8422 SaveLevel_NOTE(file, level, element);
8426 // if not using template level, check for non-default custom/group elements
8427 if (!level->use_custom_template || save_as_template)
8429 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8431 int element = EL_CUSTOM_START + i;
8433 chunk_size = SaveLevel_CUSX(NULL, level, element);
8434 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8436 putFileChunkBE(file, "CUSX", chunk_size);
8437 SaveLevel_CUSX(file, level, element);
8441 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8443 int element = EL_GROUP_START + i;
8445 chunk_size = SaveLevel_GRPX(NULL, level, element);
8446 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8448 putFileChunkBE(file, "GRPX", chunk_size);
8449 SaveLevel_GRPX(file, level, element);
8453 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8455 int element = GET_EMPTY_ELEMENT(i);
8457 chunk_size = SaveLevel_EMPX(NULL, level, element);
8458 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8460 putFileChunkBE(file, "EMPX", chunk_size);
8461 SaveLevel_EMPX(file, level, element);
8468 SetFilePermissions(filename, PERMS_PRIVATE);
8471 void SaveLevel(int nr)
8473 char *filename = getDefaultLevelFilename(nr);
8475 SaveLevelFromFilename(&level, filename, FALSE);
8478 void SaveLevelTemplate(void)
8480 char *filename = getLocalLevelTemplateFilename();
8482 SaveLevelFromFilename(&level, filename, TRUE);
8485 boolean SaveLevelChecked(int nr)
8487 char *filename = getDefaultLevelFilename(nr);
8488 boolean new_level = !fileExists(filename);
8489 boolean level_saved = FALSE;
8491 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8496 Request("Level saved!", REQ_CONFIRM);
8504 void DumpLevel(struct LevelInfo *level)
8506 if (level->no_level_file || level->no_valid_file)
8508 Warn("cannot dump -- no valid level file found");
8514 Print("Level xxx (file version %08d, game version %08d)\n",
8515 level->file_version, level->game_version);
8518 Print("Level author: '%s'\n", level->author);
8519 Print("Level title: '%s'\n", level->name);
8521 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8523 Print("Level time: %d seconds\n", level->time);
8524 Print("Gems needed: %d\n", level->gems_needed);
8526 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8527 Print("Time for wheel: %d seconds\n", level->time_wheel);
8528 Print("Time for light: %d seconds\n", level->time_light);
8529 Print("Time for timegate: %d seconds\n", level->time_timegate);
8531 Print("Amoeba speed: %d\n", level->amoeba_speed);
8534 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8535 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8536 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8537 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8538 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8539 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8545 for (i = 0; i < NUM_ENVELOPES; i++)
8547 char *text = level->envelope[i].text;
8548 int text_len = strlen(text);
8549 boolean has_text = FALSE;
8551 for (j = 0; j < text_len; j++)
8552 if (text[j] != ' ' && text[j] != '\n')
8558 Print("Envelope %d:\n'%s'\n", i + 1, text);
8566 void DumpLevels(void)
8568 static LevelDirTree *dumplevel_leveldir = NULL;
8570 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8571 global.dumplevel_leveldir);
8573 if (dumplevel_leveldir == NULL)
8574 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8576 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8577 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8578 Fail("no such level number: %d", global.dumplevel_level_nr);
8580 leveldir_current = dumplevel_leveldir;
8582 LoadLevel(global.dumplevel_level_nr);
8589 // ============================================================================
8590 // tape file functions
8591 // ============================================================================
8593 static void setTapeInfoToDefaults(void)
8597 // always start with reliable default values (empty tape)
8600 // default values (also for pre-1.2 tapes) with only the first player
8601 tape.player_participates[0] = TRUE;
8602 for (i = 1; i < MAX_PLAYERS; i++)
8603 tape.player_participates[i] = FALSE;
8605 // at least one (default: the first) player participates in every tape
8606 tape.num_participating_players = 1;
8608 tape.property_bits = TAPE_PROPERTY_NONE;
8610 tape.level_nr = level_nr;
8612 tape.changed = FALSE;
8613 tape.solved = FALSE;
8615 tape.recording = FALSE;
8616 tape.playing = FALSE;
8617 tape.pausing = FALSE;
8619 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8620 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8622 tape.no_info_chunk = TRUE;
8623 tape.no_valid_file = FALSE;
8626 static int getTapePosSize(struct TapeInfo *tape)
8628 int tape_pos_size = 0;
8630 if (tape->use_key_actions)
8631 tape_pos_size += tape->num_participating_players;
8633 if (tape->use_mouse_actions)
8634 tape_pos_size += 3; // x and y position and mouse button mask
8636 tape_pos_size += 1; // tape action delay value
8638 return tape_pos_size;
8641 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8643 tape->use_key_actions = FALSE;
8644 tape->use_mouse_actions = FALSE;
8646 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8647 tape->use_key_actions = TRUE;
8649 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8650 tape->use_mouse_actions = TRUE;
8653 static int getTapeActionValue(struct TapeInfo *tape)
8655 return (tape->use_key_actions &&
8656 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8657 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8658 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8659 TAPE_ACTIONS_DEFAULT);
8662 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8664 tape->file_version = getFileVersion(file);
8665 tape->game_version = getFileVersion(file);
8670 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8674 tape->random_seed = getFile32BitBE(file);
8675 tape->date = getFile32BitBE(file);
8676 tape->length = getFile32BitBE(file);
8678 // read header fields that are new since version 1.2
8679 if (tape->file_version >= FILE_VERSION_1_2)
8681 byte store_participating_players = getFile8Bit(file);
8684 // since version 1.2, tapes store which players participate in the tape
8685 tape->num_participating_players = 0;
8686 for (i = 0; i < MAX_PLAYERS; i++)
8688 tape->player_participates[i] = FALSE;
8690 if (store_participating_players & (1 << i))
8692 tape->player_participates[i] = TRUE;
8693 tape->num_participating_players++;
8697 setTapeActionFlags(tape, getFile8Bit(file));
8699 tape->property_bits = getFile8Bit(file);
8700 tape->solved = getFile8Bit(file);
8702 engine_version = getFileVersion(file);
8703 if (engine_version > 0)
8704 tape->engine_version = engine_version;
8706 tape->engine_version = tape->game_version;
8712 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8714 tape->scr_fieldx = getFile8Bit(file);
8715 tape->scr_fieldy = getFile8Bit(file);
8720 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8722 char *level_identifier = NULL;
8723 int level_identifier_size;
8726 tape->no_info_chunk = FALSE;
8728 level_identifier_size = getFile16BitBE(file);
8730 level_identifier = checked_malloc(level_identifier_size);
8732 for (i = 0; i < level_identifier_size; i++)
8733 level_identifier[i] = getFile8Bit(file);
8735 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8736 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8738 checked_free(level_identifier);
8740 tape->level_nr = getFile16BitBE(file);
8742 chunk_size = 2 + level_identifier_size + 2;
8747 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8750 int tape_pos_size = getTapePosSize(tape);
8751 int chunk_size_expected = tape_pos_size * tape->length;
8753 if (chunk_size_expected != chunk_size)
8755 ReadUnusedBytesFromFile(file, chunk_size);
8756 return chunk_size_expected;
8759 for (i = 0; i < tape->length; i++)
8761 if (i >= MAX_TAPE_LEN)
8763 Warn("tape truncated -- size exceeds maximum tape size %d",
8766 // tape too large; read and ignore remaining tape data from this chunk
8767 for (;i < tape->length; i++)
8768 ReadUnusedBytesFromFile(file, tape_pos_size);
8773 if (tape->use_key_actions)
8775 for (j = 0; j < MAX_PLAYERS; j++)
8777 tape->pos[i].action[j] = MV_NONE;
8779 if (tape->player_participates[j])
8780 tape->pos[i].action[j] = getFile8Bit(file);
8784 if (tape->use_mouse_actions)
8786 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8787 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8788 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8791 tape->pos[i].delay = getFile8Bit(file);
8793 if (tape->file_version == FILE_VERSION_1_0)
8795 // eliminate possible diagonal moves in old tapes
8796 // this is only for backward compatibility
8798 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8799 byte action = tape->pos[i].action[0];
8800 int k, num_moves = 0;
8802 for (k = 0; k < 4; k++)
8804 if (action & joy_dir[k])
8806 tape->pos[i + num_moves].action[0] = joy_dir[k];
8808 tape->pos[i + num_moves].delay = 0;
8817 tape->length += num_moves;
8820 else if (tape->file_version < FILE_VERSION_2_0)
8822 // convert pre-2.0 tapes to new tape format
8824 if (tape->pos[i].delay > 1)
8827 tape->pos[i + 1] = tape->pos[i];
8828 tape->pos[i + 1].delay = 1;
8831 for (j = 0; j < MAX_PLAYERS; j++)
8832 tape->pos[i].action[j] = MV_NONE;
8833 tape->pos[i].delay--;
8840 if (checkEndOfFile(file))
8844 if (i != tape->length)
8845 chunk_size = tape_pos_size * i;
8850 static void LoadTape_SokobanSolution(char *filename)
8853 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8855 if (!(file = openFile(filename, MODE_READ)))
8857 tape.no_valid_file = TRUE;
8862 while (!checkEndOfFile(file))
8864 unsigned char c = getByteFromFile(file);
8866 if (checkEndOfFile(file))
8873 tape.pos[tape.length].action[0] = MV_UP;
8874 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8880 tape.pos[tape.length].action[0] = MV_DOWN;
8881 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8887 tape.pos[tape.length].action[0] = MV_LEFT;
8888 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8894 tape.pos[tape.length].action[0] = MV_RIGHT;
8895 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8903 // ignore white-space characters
8907 tape.no_valid_file = TRUE;
8909 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8917 if (tape.no_valid_file)
8920 tape.length_frames = GetTapeLengthFrames();
8921 tape.length_seconds = GetTapeLengthSeconds();
8924 void LoadTapeFromFilename(char *filename)
8926 char cookie[MAX_LINE_LEN];
8927 char chunk_name[CHUNK_ID_LEN + 1];
8931 // always start with reliable default values
8932 setTapeInfoToDefaults();
8934 if (strSuffix(filename, ".sln"))
8936 LoadTape_SokobanSolution(filename);
8941 if (!(file = openFile(filename, MODE_READ)))
8943 tape.no_valid_file = TRUE;
8948 getFileChunkBE(file, chunk_name, NULL);
8949 if (strEqual(chunk_name, "RND1"))
8951 getFile32BitBE(file); // not used
8953 getFileChunkBE(file, chunk_name, NULL);
8954 if (!strEqual(chunk_name, "TAPE"))
8956 tape.no_valid_file = TRUE;
8958 Warn("unknown format of tape file '%s'", filename);
8965 else // check for pre-2.0 file format with cookie string
8967 strcpy(cookie, chunk_name);
8968 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8970 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8971 cookie[strlen(cookie) - 1] = '\0';
8973 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8975 tape.no_valid_file = TRUE;
8977 Warn("unknown format of tape file '%s'", filename);
8984 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8986 tape.no_valid_file = TRUE;
8988 Warn("unsupported version of tape file '%s'", filename);
8995 // pre-2.0 tape files have no game version, so use file version here
8996 tape.game_version = tape.file_version;
8999 if (tape.file_version < FILE_VERSION_1_2)
9001 // tape files from versions before 1.2.0 without chunk structure
9002 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9003 LoadTape_BODY(file, 2 * tape.length, &tape);
9011 int (*loader)(File *, int, struct TapeInfo *);
9015 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
9016 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
9017 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
9018 { "INFO", -1, LoadTape_INFO },
9019 { "BODY", -1, LoadTape_BODY },
9023 while (getFileChunkBE(file, chunk_name, &chunk_size))
9027 while (chunk_info[i].name != NULL &&
9028 !strEqual(chunk_name, chunk_info[i].name))
9031 if (chunk_info[i].name == NULL)
9033 Warn("unknown chunk '%s' in tape file '%s'",
9034 chunk_name, filename);
9036 ReadUnusedBytesFromFile(file, chunk_size);
9038 else if (chunk_info[i].size != -1 &&
9039 chunk_info[i].size != chunk_size)
9041 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9042 chunk_size, chunk_name, filename);
9044 ReadUnusedBytesFromFile(file, chunk_size);
9048 // call function to load this tape chunk
9049 int chunk_size_expected =
9050 (chunk_info[i].loader)(file, chunk_size, &tape);
9052 // the size of some chunks cannot be checked before reading other
9053 // chunks first (like "HEAD" and "BODY") that contain some header
9054 // information, so check them here
9055 if (chunk_size_expected != chunk_size)
9057 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9058 chunk_size, chunk_name, filename);
9066 tape.length_frames = GetTapeLengthFrames();
9067 tape.length_seconds = GetTapeLengthSeconds();
9070 Debug("files:LoadTapeFromFilename", "tape file version: %d",
9072 Debug("files:LoadTapeFromFilename", "tape game version: %d",
9074 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9075 tape.engine_version);
9079 void LoadTape(int nr)
9081 char *filename = getTapeFilename(nr);
9083 LoadTapeFromFilename(filename);
9086 void LoadSolutionTape(int nr)
9088 char *filename = getSolutionTapeFilename(nr);
9090 LoadTapeFromFilename(filename);
9092 if (TAPE_IS_EMPTY(tape))
9094 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9095 level.native_bd_level->replay != NULL)
9096 CopyNativeTape_BD_to_RND(&level);
9097 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9098 level.native_sp_level->demo.is_available)
9099 CopyNativeTape_SP_to_RND(&level);
9103 void LoadScoreTape(char *score_tape_basename, int nr)
9105 char *filename = getScoreTapeFilename(score_tape_basename, nr);
9107 LoadTapeFromFilename(filename);
9110 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9112 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9114 LoadTapeFromFilename(filename);
9117 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9119 // chunk required for team mode tapes with non-default screen size
9120 return (tape->num_participating_players > 1 &&
9121 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9122 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9125 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9127 putFileVersion(file, tape->file_version);
9128 putFileVersion(file, tape->game_version);
9131 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9134 byte store_participating_players = 0;
9136 // set bits for participating players for compact storage
9137 for (i = 0; i < MAX_PLAYERS; i++)
9138 if (tape->player_participates[i])
9139 store_participating_players |= (1 << i);
9141 putFile32BitBE(file, tape->random_seed);
9142 putFile32BitBE(file, tape->date);
9143 putFile32BitBE(file, tape->length);
9145 putFile8Bit(file, store_participating_players);
9147 putFile8Bit(file, getTapeActionValue(tape));
9149 putFile8Bit(file, tape->property_bits);
9150 putFile8Bit(file, tape->solved);
9152 putFileVersion(file, tape->engine_version);
9155 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9157 putFile8Bit(file, tape->scr_fieldx);
9158 putFile8Bit(file, tape->scr_fieldy);
9161 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9163 int level_identifier_size = strlen(tape->level_identifier) + 1;
9166 putFile16BitBE(file, level_identifier_size);
9168 for (i = 0; i < level_identifier_size; i++)
9169 putFile8Bit(file, tape->level_identifier[i]);
9171 putFile16BitBE(file, tape->level_nr);
9174 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9178 for (i = 0; i < tape->length; i++)
9180 if (tape->use_key_actions)
9182 for (j = 0; j < MAX_PLAYERS; j++)
9183 if (tape->player_participates[j])
9184 putFile8Bit(file, tape->pos[i].action[j]);
9187 if (tape->use_mouse_actions)
9189 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9190 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9191 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9194 putFile8Bit(file, tape->pos[i].delay);
9198 void SaveTapeToFilename(char *filename)
9202 int info_chunk_size;
9203 int body_chunk_size;
9205 if (!(file = fopen(filename, MODE_WRITE)))
9207 Warn("cannot save level recording file '%s'", filename);
9212 tape_pos_size = getTapePosSize(&tape);
9214 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9215 body_chunk_size = tape_pos_size * tape.length;
9217 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9218 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9220 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9221 SaveTape_VERS(file, &tape);
9223 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9224 SaveTape_HEAD(file, &tape);
9226 if (checkSaveTape_SCRN(&tape))
9228 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9229 SaveTape_SCRN(file, &tape);
9232 putFileChunkBE(file, "INFO", info_chunk_size);
9233 SaveTape_INFO(file, &tape);
9235 putFileChunkBE(file, "BODY", body_chunk_size);
9236 SaveTape_BODY(file, &tape);
9240 SetFilePermissions(filename, PERMS_PRIVATE);
9243 static void SaveTapeExt(char *filename)
9247 tape.file_version = FILE_VERSION_ACTUAL;
9248 tape.game_version = GAME_VERSION_ACTUAL;
9250 tape.num_participating_players = 0;
9252 // count number of participating players
9253 for (i = 0; i < MAX_PLAYERS; i++)
9254 if (tape.player_participates[i])
9255 tape.num_participating_players++;
9257 SaveTapeToFilename(filename);
9259 tape.changed = FALSE;
9262 void SaveTape(int nr)
9264 char *filename = getTapeFilename(nr);
9266 InitTapeDirectory(leveldir_current->subdir);
9268 SaveTapeExt(filename);
9271 void SaveScoreTape(int nr)
9273 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9275 // used instead of "leveldir_current->subdir" (for network games)
9276 InitScoreTapeDirectory(levelset.identifier, nr);
9278 SaveTapeExt(filename);
9281 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9282 unsigned int req_state_added)
9284 char *filename = getTapeFilename(nr);
9285 boolean new_tape = !fileExists(filename);
9286 boolean tape_saved = FALSE;
9288 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9293 Request(msg_saved, REQ_CONFIRM | req_state_added);
9301 boolean SaveTapeChecked(int nr)
9303 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9306 boolean SaveTapeChecked_LevelSolved(int nr)
9308 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9309 "Level solved! Tape saved!", REQ_STAY_OPEN);
9312 void DumpTape(struct TapeInfo *tape)
9314 int tape_frame_counter;
9317 if (tape->no_valid_file)
9319 Warn("cannot dump -- no valid tape file found");
9326 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9327 tape->level_nr, tape->file_version, tape->game_version);
9328 Print(" (effective engine version %08d)\n",
9329 tape->engine_version);
9330 Print("Level series identifier: '%s'\n", tape->level_identifier);
9332 Print("Solution tape: %s\n",
9333 tape->solved ? "yes" :
9334 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9336 Print("Special tape properties: ");
9337 if (tape->property_bits == TAPE_PROPERTY_NONE)
9339 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9340 Print("[em_random_bug]");
9341 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9342 Print("[game_speed]");
9343 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9345 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9346 Print("[single_step]");
9347 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9348 Print("[snapshot]");
9349 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9350 Print("[replayed]");
9351 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9352 Print("[tas_keys]");
9353 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9354 Print("[small_graphics]");
9357 int year2 = tape->date / 10000;
9358 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9359 int month_index_raw = (tape->date / 100) % 100;
9360 int month_index = month_index_raw % 12; // prevent invalid index
9361 int month = month_index + 1;
9362 int day = tape->date % 100;
9364 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9368 tape_frame_counter = 0;
9370 for (i = 0; i < tape->length; i++)
9372 if (i >= MAX_TAPE_LEN)
9377 for (j = 0; j < MAX_PLAYERS; j++)
9379 if (tape->player_participates[j])
9381 int action = tape->pos[i].action[j];
9383 Print("%d:%02x ", j, action);
9384 Print("[%c%c%c%c|%c%c] - ",
9385 (action & JOY_LEFT ? '<' : ' '),
9386 (action & JOY_RIGHT ? '>' : ' '),
9387 (action & JOY_UP ? '^' : ' '),
9388 (action & JOY_DOWN ? 'v' : ' '),
9389 (action & JOY_BUTTON_1 ? '1' : ' '),
9390 (action & JOY_BUTTON_2 ? '2' : ' '));
9394 Print("(%03d) ", tape->pos[i].delay);
9395 Print("[%05d]\n", tape_frame_counter);
9397 tape_frame_counter += tape->pos[i].delay;
9403 void DumpTapes(void)
9405 static LevelDirTree *dumptape_leveldir = NULL;
9407 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9408 global.dumptape_leveldir);
9410 if (dumptape_leveldir == NULL)
9411 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9413 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9414 global.dumptape_level_nr > dumptape_leveldir->last_level)
9415 Fail("no such level number: %d", global.dumptape_level_nr);
9417 leveldir_current = dumptape_leveldir;
9419 if (options.mytapes)
9420 LoadTape(global.dumptape_level_nr);
9422 LoadSolutionTape(global.dumptape_level_nr);
9430 // ============================================================================
9431 // score file functions
9432 // ============================================================================
9434 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9438 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9440 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9441 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9442 scores->entry[i].score = 0;
9443 scores->entry[i].time = 0;
9445 scores->entry[i].id = -1;
9446 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9447 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9448 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9449 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9450 strcpy(scores->entry[i].country_code, "??");
9453 scores->num_entries = 0;
9454 scores->last_added = -1;
9455 scores->last_added_local = -1;
9457 scores->updated = FALSE;
9458 scores->uploaded = FALSE;
9459 scores->tape_downloaded = FALSE;
9460 scores->force_last_added = FALSE;
9462 // The following values are intentionally not reset here:
9466 // - continue_playing
9467 // - continue_on_return
9470 static void setScoreInfoToDefaults(void)
9472 setScoreInfoToDefaultsExt(&scores);
9475 static void setServerScoreInfoToDefaults(void)
9477 setScoreInfoToDefaultsExt(&server_scores);
9480 static void LoadScore_OLD(int nr)
9483 char *filename = getScoreFilename(nr);
9484 char cookie[MAX_LINE_LEN];
9485 char line[MAX_LINE_LEN];
9489 if (!(file = fopen(filename, MODE_READ)))
9492 // check file identifier
9493 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9495 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9496 cookie[strlen(cookie) - 1] = '\0';
9498 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9500 Warn("unknown format of score file '%s'", filename);
9507 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9509 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9510 Warn("fscanf() failed; %s", strerror(errno));
9512 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9515 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9516 line[strlen(line) - 1] = '\0';
9518 for (line_ptr = line; *line_ptr; line_ptr++)
9520 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9522 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9523 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9532 static void ConvertScore_OLD(void)
9534 // only convert score to time for levels that rate playing time over score
9535 if (!level.rate_time_over_score)
9538 // convert old score to playing time for score-less levels (like Supaplex)
9539 int time_final_max = 999;
9542 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9544 int score = scores.entry[i].score;
9546 if (score > 0 && score < time_final_max)
9547 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9551 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9553 scores->file_version = getFileVersion(file);
9554 scores->game_version = getFileVersion(file);
9559 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9561 char *level_identifier = NULL;
9562 int level_identifier_size;
9565 level_identifier_size = getFile16BitBE(file);
9567 level_identifier = checked_malloc(level_identifier_size);
9569 for (i = 0; i < level_identifier_size; i++)
9570 level_identifier[i] = getFile8Bit(file);
9572 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9573 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9575 checked_free(level_identifier);
9577 scores->level_nr = getFile16BitBE(file);
9578 scores->num_entries = getFile16BitBE(file);
9580 chunk_size = 2 + level_identifier_size + 2 + 2;
9585 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9589 for (i = 0; i < scores->num_entries; i++)
9591 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9592 scores->entry[i].name[j] = getFile8Bit(file);
9594 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9597 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9602 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9606 for (i = 0; i < scores->num_entries; i++)
9607 scores->entry[i].score = getFile16BitBE(file);
9609 chunk_size = scores->num_entries * 2;
9614 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9618 for (i = 0; i < scores->num_entries; i++)
9619 scores->entry[i].score = getFile32BitBE(file);
9621 chunk_size = scores->num_entries * 4;
9626 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9630 for (i = 0; i < scores->num_entries; i++)
9631 scores->entry[i].time = getFile32BitBE(file);
9633 chunk_size = scores->num_entries * 4;
9638 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9642 for (i = 0; i < scores->num_entries; i++)
9644 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9645 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9647 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9650 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9655 void LoadScore(int nr)
9657 char *filename = getScoreFilename(nr);
9658 char cookie[MAX_LINE_LEN];
9659 char chunk_name[CHUNK_ID_LEN + 1];
9661 boolean old_score_file_format = FALSE;
9664 // always start with reliable default values
9665 setScoreInfoToDefaults();
9667 if (!(file = openFile(filename, MODE_READ)))
9670 getFileChunkBE(file, chunk_name, NULL);
9671 if (strEqual(chunk_name, "RND1"))
9673 getFile32BitBE(file); // not used
9675 getFileChunkBE(file, chunk_name, NULL);
9676 if (!strEqual(chunk_name, "SCOR"))
9678 Warn("unknown format of score file '%s'", filename);
9685 else // check for old file format with cookie string
9687 strcpy(cookie, chunk_name);
9688 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9690 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9691 cookie[strlen(cookie) - 1] = '\0';
9693 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9695 Warn("unknown format of score file '%s'", filename);
9702 old_score_file_format = TRUE;
9705 if (old_score_file_format)
9707 // score files from versions before 4.2.4.0 without chunk structure
9710 // convert score to time, if possible (mainly for Supaplex levels)
9719 int (*loader)(File *, int, struct ScoreInfo *);
9723 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9724 { "INFO", -1, LoadScore_INFO },
9725 { "NAME", -1, LoadScore_NAME },
9726 { "SCOR", -1, LoadScore_SCOR },
9727 { "SC4R", -1, LoadScore_SC4R },
9728 { "TIME", -1, LoadScore_TIME },
9729 { "TAPE", -1, LoadScore_TAPE },
9734 while (getFileChunkBE(file, chunk_name, &chunk_size))
9738 while (chunk_info[i].name != NULL &&
9739 !strEqual(chunk_name, chunk_info[i].name))
9742 if (chunk_info[i].name == NULL)
9744 Warn("unknown chunk '%s' in score file '%s'",
9745 chunk_name, filename);
9747 ReadUnusedBytesFromFile(file, chunk_size);
9749 else if (chunk_info[i].size != -1 &&
9750 chunk_info[i].size != chunk_size)
9752 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9753 chunk_size, chunk_name, filename);
9755 ReadUnusedBytesFromFile(file, chunk_size);
9759 // call function to load this score chunk
9760 int chunk_size_expected =
9761 (chunk_info[i].loader)(file, chunk_size, &scores);
9763 // the size of some chunks cannot be checked before reading other
9764 // chunks first (like "HEAD" and "BODY") that contain some header
9765 // information, so check them here
9766 if (chunk_size_expected != chunk_size)
9768 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9769 chunk_size, chunk_name, filename);
9778 #if ENABLE_HISTORIC_CHUNKS
9779 void SaveScore_OLD(int nr)
9782 char *filename = getScoreFilename(nr);
9785 // used instead of "leveldir_current->subdir" (for network games)
9786 InitScoreDirectory(levelset.identifier);
9788 if (!(file = fopen(filename, MODE_WRITE)))
9790 Warn("cannot save score for level %d", nr);
9795 fprintf(file, "%s\n\n", SCORE_COOKIE);
9797 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9798 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9802 SetFilePermissions(filename, PERMS_PRIVATE);
9806 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9808 putFileVersion(file, scores->file_version);
9809 putFileVersion(file, scores->game_version);
9812 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9814 int level_identifier_size = strlen(scores->level_identifier) + 1;
9817 putFile16BitBE(file, level_identifier_size);
9819 for (i = 0; i < level_identifier_size; i++)
9820 putFile8Bit(file, scores->level_identifier[i]);
9822 putFile16BitBE(file, scores->level_nr);
9823 putFile16BitBE(file, scores->num_entries);
9826 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9830 for (i = 0; i < scores->num_entries; i++)
9832 int name_size = strlen(scores->entry[i].name);
9834 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9835 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9839 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9843 for (i = 0; i < scores->num_entries; i++)
9844 putFile16BitBE(file, scores->entry[i].score);
9847 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9851 for (i = 0; i < scores->num_entries; i++)
9852 putFile32BitBE(file, scores->entry[i].score);
9855 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9859 for (i = 0; i < scores->num_entries; i++)
9860 putFile32BitBE(file, scores->entry[i].time);
9863 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9867 for (i = 0; i < scores->num_entries; i++)
9869 int size = strlen(scores->entry[i].tape_basename);
9871 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9872 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9876 static void SaveScoreToFilename(char *filename)
9879 int info_chunk_size;
9880 int name_chunk_size;
9881 int scor_chunk_size;
9882 int sc4r_chunk_size;
9883 int time_chunk_size;
9884 int tape_chunk_size;
9885 boolean has_large_score_values;
9888 if (!(file = fopen(filename, MODE_WRITE)))
9890 Warn("cannot save score file '%s'", filename);
9895 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9896 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9897 scor_chunk_size = scores.num_entries * 2;
9898 sc4r_chunk_size = scores.num_entries * 4;
9899 time_chunk_size = scores.num_entries * 4;
9900 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9902 has_large_score_values = FALSE;
9903 for (i = 0; i < scores.num_entries; i++)
9904 if (scores.entry[i].score > 0xffff)
9905 has_large_score_values = TRUE;
9907 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9908 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9910 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9911 SaveScore_VERS(file, &scores);
9913 putFileChunkBE(file, "INFO", info_chunk_size);
9914 SaveScore_INFO(file, &scores);
9916 putFileChunkBE(file, "NAME", name_chunk_size);
9917 SaveScore_NAME(file, &scores);
9919 if (has_large_score_values)
9921 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9922 SaveScore_SC4R(file, &scores);
9926 putFileChunkBE(file, "SCOR", scor_chunk_size);
9927 SaveScore_SCOR(file, &scores);
9930 putFileChunkBE(file, "TIME", time_chunk_size);
9931 SaveScore_TIME(file, &scores);
9933 putFileChunkBE(file, "TAPE", tape_chunk_size);
9934 SaveScore_TAPE(file, &scores);
9938 SetFilePermissions(filename, PERMS_PRIVATE);
9941 void SaveScore(int nr)
9943 char *filename = getScoreFilename(nr);
9946 // used instead of "leveldir_current->subdir" (for network games)
9947 InitScoreDirectory(levelset.identifier);
9949 scores.file_version = FILE_VERSION_ACTUAL;
9950 scores.game_version = GAME_VERSION_ACTUAL;
9952 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9953 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9954 scores.level_nr = level_nr;
9956 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9957 if (scores.entry[i].score == 0 &&
9958 scores.entry[i].time == 0 &&
9959 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9962 scores.num_entries = i;
9964 if (scores.num_entries == 0)
9967 SaveScoreToFilename(filename);
9970 static void LoadServerScoreFromCache(int nr)
9972 struct ScoreEntry score_entry;
9981 { &score_entry.score, FALSE, 0 },
9982 { &score_entry.time, FALSE, 0 },
9983 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9984 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9985 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9986 { &score_entry.id, FALSE, 0 },
9987 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9988 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9989 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9990 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9994 char *filename = getScoreCacheFilename(nr);
9995 SetupFileHash *score_hash = loadSetupFileHash(filename);
9998 server_scores.num_entries = 0;
10000 if (score_hash == NULL)
10003 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10005 score_entry = server_scores.entry[i];
10007 for (j = 0; score_mapping[j].value != NULL; j++)
10011 sprintf(token, "%02d.%d", i, j);
10013 char *value = getHashEntry(score_hash, token);
10018 if (score_mapping[j].is_string)
10020 char *score_value = (char *)score_mapping[j].value;
10021 int value_size = score_mapping[j].string_size;
10023 strncpy(score_value, value, value_size);
10024 score_value[value_size] = '\0';
10028 int *score_value = (int *)score_mapping[j].value;
10030 *score_value = atoi(value);
10033 server_scores.num_entries = i + 1;
10036 server_scores.entry[i] = score_entry;
10039 freeSetupFileHash(score_hash);
10042 void LoadServerScore(int nr, boolean download_score)
10044 if (!setup.use_api_server)
10047 // always start with reliable default values
10048 setServerScoreInfoToDefaults();
10050 // 1st step: load server scores from cache file (which may not exist)
10051 // (this should prevent reading it while the thread is writing to it)
10052 LoadServerScoreFromCache(nr);
10054 if (download_score && runtime.use_api_server)
10056 // 2nd step: download server scores from score server to cache file
10057 // (as thread, as it might time out if the server is not reachable)
10058 ApiGetScoreAsThread(nr);
10062 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10064 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10066 // if score tape not uploaded, ask for uploading missing tapes later
10067 if (!setup.has_remaining_tapes)
10068 setup.ask_for_remaining_tapes = TRUE;
10070 setup.provide_uploading_tapes = TRUE;
10071 setup.has_remaining_tapes = TRUE;
10073 SaveSetup_ServerSetup();
10076 void SaveServerScore(int nr, boolean tape_saved)
10078 if (!runtime.use_api_server)
10080 PrepareScoreTapesForUpload(leveldir_current->subdir);
10085 ApiAddScoreAsThread(nr, tape_saved, NULL);
10088 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10089 char *score_tape_filename)
10091 if (!runtime.use_api_server)
10094 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10097 void LoadLocalAndServerScore(int nr, boolean download_score)
10099 int last_added_local = scores.last_added_local;
10100 boolean force_last_added = scores.force_last_added;
10102 // needed if only showing server scores
10103 setScoreInfoToDefaults();
10105 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10108 // restore last added local score entry (before merging server scores)
10109 scores.last_added = scores.last_added_local = last_added_local;
10111 if (setup.use_api_server &&
10112 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10114 // load server scores from cache file and trigger update from server
10115 LoadServerScore(nr, download_score);
10117 // merge local scores with scores from server
10118 MergeServerScore();
10121 if (force_last_added)
10122 scores.force_last_added = force_last_added;
10126 // ============================================================================
10127 // setup file functions
10128 // ============================================================================
10130 #define TOKEN_STR_PLAYER_PREFIX "player_"
10133 static struct TokenInfo global_setup_tokens[] =
10137 &setup.player_name, "player_name"
10141 &setup.multiple_users, "multiple_users"
10145 &setup.sound, "sound"
10149 &setup.sound_loops, "repeating_sound_loops"
10153 &setup.sound_music, "background_music"
10157 &setup.sound_simple, "simple_sound_effects"
10161 &setup.toons, "toons"
10165 &setup.global_animations, "global_animations"
10169 &setup.scroll_delay, "scroll_delay"
10173 &setup.forced_scroll_delay, "forced_scroll_delay"
10177 &setup.scroll_delay_value, "scroll_delay_value"
10181 &setup.engine_snapshot_mode, "engine_snapshot_mode"
10185 &setup.engine_snapshot_memory, "engine_snapshot_memory"
10189 &setup.fade_screens, "fade_screens"
10193 &setup.autorecord, "automatic_tape_recording"
10197 &setup.autorecord_after_replay, "autorecord_after_replay"
10201 &setup.auto_pause_on_start, "auto_pause_on_start"
10205 &setup.show_titlescreen, "show_titlescreen"
10209 &setup.quick_doors, "quick_doors"
10213 &setup.team_mode, "team_mode"
10217 &setup.handicap, "handicap"
10221 &setup.skip_levels, "skip_levels"
10225 &setup.increment_levels, "increment_levels"
10229 &setup.auto_play_next_level, "auto_play_next_level"
10233 &setup.count_score_after_game, "count_score_after_game"
10237 &setup.show_scores_after_game, "show_scores_after_game"
10241 &setup.time_limit, "time_limit"
10245 &setup.fullscreen, "fullscreen"
10249 &setup.window_scaling_percent, "window_scaling_percent"
10253 &setup.window_scaling_quality, "window_scaling_quality"
10257 &setup.screen_rendering_mode, "screen_rendering_mode"
10261 &setup.vsync_mode, "vsync_mode"
10265 &setup.ask_on_escape, "ask_on_escape"
10269 &setup.ask_on_escape_editor, "ask_on_escape_editor"
10273 &setup.ask_on_game_over, "ask_on_game_over"
10277 &setup.ask_on_quit_game, "ask_on_quit_game"
10281 &setup.ask_on_quit_program, "ask_on_quit_program"
10285 &setup.quick_switch, "quick_player_switch"
10289 &setup.input_on_focus, "input_on_focus"
10293 &setup.prefer_aga_graphics, "prefer_aga_graphics"
10297 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
10301 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
10305 &setup.game_speed_extended, "game_speed_extended"
10309 &setup.game_frame_delay, "game_frame_delay"
10313 &setup.bd_skip_uncovering, "bd_skip_uncovering"
10317 &setup.bd_skip_hatching, "bd_skip_hatching"
10321 &setup.bd_scroll_delay, "bd_scroll_delay"
10325 &setup.bd_smooth_movements, "bd_smooth_movements"
10329 &setup.sp_show_border_elements, "sp_show_border_elements"
10333 &setup.small_game_graphics, "small_game_graphics"
10337 &setup.show_load_save_buttons, "show_load_save_buttons"
10341 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10345 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10349 &setup.graphics_set, "graphics_set"
10353 &setup.sounds_set, "sounds_set"
10357 &setup.music_set, "music_set"
10361 &setup.override_level_graphics, "override_level_graphics"
10365 &setup.override_level_sounds, "override_level_sounds"
10369 &setup.override_level_music, "override_level_music"
10373 &setup.volume_simple, "volume_simple"
10377 &setup.volume_loops, "volume_loops"
10381 &setup.volume_music, "volume_music"
10385 &setup.network_mode, "network_mode"
10389 &setup.network_player_nr, "network_player"
10393 &setup.network_server_hostname, "network_server_hostname"
10397 &setup.touch.control_type, "touch.control_type"
10401 &setup.touch.move_distance, "touch.move_distance"
10405 &setup.touch.drop_distance, "touch.drop_distance"
10409 &setup.touch.transparency, "touch.transparency"
10413 &setup.touch.draw_outlined, "touch.draw_outlined"
10417 &setup.touch.draw_pressed, "touch.draw_pressed"
10421 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10425 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10429 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10433 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10437 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10441 static struct TokenInfo auto_setup_tokens[] =
10445 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10449 static struct TokenInfo server_setup_tokens[] =
10453 &setup.player_uuid, "player_uuid"
10457 &setup.player_version, "player_version"
10461 &setup.use_api_server, TEST_PREFIX "use_api_server"
10465 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10469 &setup.api_server_password, TEST_PREFIX "api_server_password"
10473 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10477 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10481 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10485 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10489 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10493 static struct TokenInfo editor_setup_tokens[] =
10497 &setup.editor.el_classic, "editor.el_classic"
10501 &setup.editor.el_custom, "editor.el_custom"
10505 &setup.editor.el_user_defined, "editor.el_user_defined"
10509 &setup.editor.el_dynamic, "editor.el_dynamic"
10513 &setup.editor.el_headlines, "editor.el_headlines"
10517 &setup.editor.show_element_token, "editor.show_element_token"
10521 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10525 static struct TokenInfo editor_cascade_setup_tokens[] =
10529 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10533 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10537 &setup.editor_cascade.el_bd_effects, "editor.cascade.el_bd_effects"
10541 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10545 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10549 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10553 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10557 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10561 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10565 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10569 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10573 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10577 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10581 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10585 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10589 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10593 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10597 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10601 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10605 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10609 static struct TokenInfo shortcut_setup_tokens[] =
10613 &setup.shortcut.save_game, "shortcut.save_game"
10617 &setup.shortcut.load_game, "shortcut.load_game"
10621 &setup.shortcut.restart_game, "shortcut.restart_game"
10625 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10629 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10633 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10637 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10641 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10645 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10649 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10653 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10657 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10661 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10665 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10669 &setup.shortcut.tape_record, "shortcut.tape_record"
10673 &setup.shortcut.tape_play, "shortcut.tape_play"
10677 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10681 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10685 &setup.shortcut.sound_music, "shortcut.sound_music"
10689 &setup.shortcut.snap_left, "shortcut.snap_left"
10693 &setup.shortcut.snap_right, "shortcut.snap_right"
10697 &setup.shortcut.snap_up, "shortcut.snap_up"
10701 &setup.shortcut.snap_down, "shortcut.snap_down"
10705 static struct SetupInputInfo setup_input;
10706 static struct TokenInfo player_setup_tokens[] =
10710 &setup_input.use_joystick, ".use_joystick"
10714 &setup_input.joy.device_name, ".joy.device_name"
10718 &setup_input.joy.xleft, ".joy.xleft"
10722 &setup_input.joy.xmiddle, ".joy.xmiddle"
10726 &setup_input.joy.xright, ".joy.xright"
10730 &setup_input.joy.yupper, ".joy.yupper"
10734 &setup_input.joy.ymiddle, ".joy.ymiddle"
10738 &setup_input.joy.ylower, ".joy.ylower"
10742 &setup_input.joy.snap, ".joy.snap_field"
10746 &setup_input.joy.drop, ".joy.place_bomb"
10750 &setup_input.key.left, ".key.move_left"
10754 &setup_input.key.right, ".key.move_right"
10758 &setup_input.key.up, ".key.move_up"
10762 &setup_input.key.down, ".key.move_down"
10766 &setup_input.key.snap, ".key.snap_field"
10770 &setup_input.key.drop, ".key.place_bomb"
10774 static struct TokenInfo system_setup_tokens[] =
10778 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10782 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10786 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10790 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10794 static struct TokenInfo internal_setup_tokens[] =
10798 &setup.internal.program_title, "program_title"
10802 &setup.internal.program_version, "program_version"
10806 &setup.internal.program_author, "program_author"
10810 &setup.internal.program_email, "program_email"
10814 &setup.internal.program_website, "program_website"
10818 &setup.internal.program_copyright, "program_copyright"
10822 &setup.internal.program_company, "program_company"
10826 &setup.internal.program_icon_file, "program_icon_file"
10830 &setup.internal.default_graphics_set, "default_graphics_set"
10834 &setup.internal.default_sounds_set, "default_sounds_set"
10838 &setup.internal.default_music_set, "default_music_set"
10842 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10846 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10850 &setup.internal.fallback_music_file, "fallback_music_file"
10854 &setup.internal.default_level_series, "default_level_series"
10858 &setup.internal.default_window_width, "default_window_width"
10862 &setup.internal.default_window_height, "default_window_height"
10866 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10870 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10874 &setup.internal.create_user_levelset, "create_user_levelset"
10878 &setup.internal.info_screens_from_main, "info_screens_from_main"
10882 &setup.internal.menu_game, "menu_game"
10886 &setup.internal.menu_engines, "menu_engines"
10890 &setup.internal.menu_editor, "menu_editor"
10894 &setup.internal.menu_graphics, "menu_graphics"
10898 &setup.internal.menu_sound, "menu_sound"
10902 &setup.internal.menu_artwork, "menu_artwork"
10906 &setup.internal.menu_input, "menu_input"
10910 &setup.internal.menu_touch, "menu_touch"
10914 &setup.internal.menu_shortcuts, "menu_shortcuts"
10918 &setup.internal.menu_exit, "menu_exit"
10922 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10926 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10930 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10934 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10938 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10942 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10946 &setup.internal.info_title, "info_title"
10950 &setup.internal.info_elements, "info_elements"
10954 &setup.internal.info_music, "info_music"
10958 &setup.internal.info_credits, "info_credits"
10962 &setup.internal.info_program, "info_program"
10966 &setup.internal.info_version, "info_version"
10970 &setup.internal.info_levelset, "info_levelset"
10974 &setup.internal.info_exit, "info_exit"
10978 static struct TokenInfo debug_setup_tokens[] =
10982 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10986 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10990 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10994 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10998 &setup.debug.frame_delay[4], "debug.frame_delay_4"
11002 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11006 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11010 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11014 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11018 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11022 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11026 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11030 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11034 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11038 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11042 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11046 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11050 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11054 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11058 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11062 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11065 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11069 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11073 &setup.debug.xsn_mode, "debug.xsn_mode"
11077 &setup.debug.xsn_percent, "debug.xsn_percent"
11081 static struct TokenInfo options_setup_tokens[] =
11085 &setup.options.verbose, "options.verbose"
11089 &setup.options.debug, "options.debug"
11093 &setup.options.debug_mode, "options.debug_mode"
11097 static void setSetupInfoToDefaults(struct SetupInfo *si)
11101 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11103 si->multiple_users = TRUE;
11106 si->sound_loops = TRUE;
11107 si->sound_music = TRUE;
11108 si->sound_simple = TRUE;
11110 si->global_animations = TRUE;
11111 si->scroll_delay = TRUE;
11112 si->forced_scroll_delay = FALSE;
11113 si->scroll_delay_value = STD_SCROLL_DELAY;
11114 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11115 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11116 si->fade_screens = TRUE;
11117 si->autorecord = TRUE;
11118 si->autorecord_after_replay = TRUE;
11119 si->auto_pause_on_start = FALSE;
11120 si->show_titlescreen = TRUE;
11121 si->quick_doors = FALSE;
11122 si->team_mode = FALSE;
11123 si->handicap = TRUE;
11124 si->skip_levels = TRUE;
11125 si->increment_levels = TRUE;
11126 si->auto_play_next_level = TRUE;
11127 si->count_score_after_game = TRUE;
11128 si->show_scores_after_game = TRUE;
11129 si->time_limit = TRUE;
11130 si->fullscreen = FALSE;
11131 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11132 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11133 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11134 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11135 si->ask_on_escape = TRUE;
11136 si->ask_on_escape_editor = TRUE;
11137 si->ask_on_game_over = TRUE;
11138 si->ask_on_quit_game = TRUE;
11139 si->ask_on_quit_program = TRUE;
11140 si->quick_switch = FALSE;
11141 si->input_on_focus = FALSE;
11142 si->prefer_aga_graphics = TRUE;
11143 si->prefer_lowpass_sounds = FALSE;
11144 si->prefer_extra_panel_items = TRUE;
11145 si->game_speed_extended = FALSE;
11146 si->game_frame_delay = GAME_FRAME_DELAY;
11147 si->bd_skip_uncovering = FALSE;
11148 si->bd_skip_hatching = FALSE;
11149 si->bd_scroll_delay = TRUE;
11150 si->bd_smooth_movements = AUTO;
11151 si->sp_show_border_elements = FALSE;
11152 si->small_game_graphics = FALSE;
11153 si->show_load_save_buttons = FALSE;
11154 si->show_undo_redo_buttons = FALSE;
11155 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11157 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11158 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11159 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11161 si->override_level_graphics = FALSE;
11162 si->override_level_sounds = FALSE;
11163 si->override_level_music = FALSE;
11165 si->volume_simple = 100; // percent
11166 si->volume_loops = 100; // percent
11167 si->volume_music = 100; // percent
11169 si->network_mode = FALSE;
11170 si->network_player_nr = 0; // first player
11171 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11173 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11174 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11175 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11176 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11177 si->touch.draw_outlined = TRUE;
11178 si->touch.draw_pressed = TRUE;
11180 for (i = 0; i < 2; i++)
11182 char *default_grid_button[6][2] =
11188 { "111222", " vv " },
11189 { "111222", " vv " }
11191 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11192 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11193 int min_xsize = MIN(6, grid_xsize);
11194 int min_ysize = MIN(6, grid_ysize);
11195 int startx = grid_xsize - min_xsize;
11196 int starty = grid_ysize - min_ysize;
11199 // virtual buttons grid can only be set to defaults if video is initialized
11200 // (this will be repeated if virtual buttons are not loaded from setup file)
11201 if (video.initialized)
11203 si->touch.grid_xsize[i] = grid_xsize;
11204 si->touch.grid_ysize[i] = grid_ysize;
11208 si->touch.grid_xsize[i] = -1;
11209 si->touch.grid_ysize[i] = -1;
11212 for (x = 0; x < MAX_GRID_XSIZE; x++)
11213 for (y = 0; y < MAX_GRID_YSIZE; y++)
11214 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11216 for (x = 0; x < min_xsize; x++)
11217 for (y = 0; y < min_ysize; y++)
11218 si->touch.grid_button[i][x][starty + y] =
11219 default_grid_button[y][0][x];
11221 for (x = 0; x < min_xsize; x++)
11222 for (y = 0; y < min_ysize; y++)
11223 si->touch.grid_button[i][startx + x][starty + y] =
11224 default_grid_button[y][1][x];
11227 si->touch.grid_initialized = video.initialized;
11229 si->touch.overlay_buttons = FALSE;
11231 si->editor.el_boulderdash = TRUE;
11232 si->editor.el_boulderdash_native = TRUE;
11233 si->editor.el_boulderdash_effects = TRUE;
11234 si->editor.el_emerald_mine = TRUE;
11235 si->editor.el_emerald_mine_club = TRUE;
11236 si->editor.el_more = TRUE;
11237 si->editor.el_sokoban = TRUE;
11238 si->editor.el_supaplex = TRUE;
11239 si->editor.el_diamond_caves = TRUE;
11240 si->editor.el_dx_boulderdash = TRUE;
11242 si->editor.el_mirror_magic = TRUE;
11243 si->editor.el_deflektor = TRUE;
11245 si->editor.el_chars = TRUE;
11246 si->editor.el_steel_chars = TRUE;
11248 si->editor.el_classic = TRUE;
11249 si->editor.el_custom = TRUE;
11251 si->editor.el_user_defined = FALSE;
11252 si->editor.el_dynamic = TRUE;
11254 si->editor.el_headlines = TRUE;
11256 si->editor.show_element_token = FALSE;
11258 si->editor.show_read_only_warning = TRUE;
11260 si->editor.use_template_for_new_levels = TRUE;
11262 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11263 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11264 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11265 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11266 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11268 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11269 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11270 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11271 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11272 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11274 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11275 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11276 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11277 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11278 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11279 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11281 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11282 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11283 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11285 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11286 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11287 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11288 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11290 for (i = 0; i < MAX_PLAYERS; i++)
11292 si->input[i].use_joystick = FALSE;
11293 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11294 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11295 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11296 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11297 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11298 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11299 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11300 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11301 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11302 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11303 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11304 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11305 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11306 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11307 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11310 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11311 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11312 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11313 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11315 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11316 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11317 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11318 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11319 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11320 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11321 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11323 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11325 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11326 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11327 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11329 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11330 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11331 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11333 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11334 si->internal.choose_from_top_leveldir = FALSE;
11335 si->internal.show_scaling_in_title = TRUE;
11336 si->internal.create_user_levelset = TRUE;
11337 si->internal.info_screens_from_main = FALSE;
11339 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11340 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11342 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11343 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11344 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11345 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11346 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11347 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11348 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11349 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11350 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11351 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11353 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11354 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11355 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11356 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11357 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11358 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11359 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11360 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11361 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11362 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11364 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11365 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11367 si->debug.show_frames_per_second = FALSE;
11369 si->debug.xsn_mode = AUTO;
11370 si->debug.xsn_percent = 0;
11372 si->options.verbose = FALSE;
11373 si->options.debug = FALSE;
11374 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11376 #if defined(PLATFORM_ANDROID)
11377 si->fullscreen = TRUE;
11378 si->touch.overlay_buttons = TRUE;
11381 setHideSetupEntry(&setup.debug.xsn_mode);
11384 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11386 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11389 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11391 si->player_uuid = NULL; // (will be set later)
11392 si->player_version = 1; // (will be set later)
11394 si->use_api_server = TRUE;
11395 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11396 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11397 si->ask_for_uploading_tapes = TRUE;
11398 si->ask_for_remaining_tapes = FALSE;
11399 si->provide_uploading_tapes = TRUE;
11400 si->ask_for_using_api_server = TRUE;
11401 si->has_remaining_tapes = FALSE;
11404 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11406 si->editor_cascade.el_bd = TRUE;
11407 si->editor_cascade.el_bd_native = TRUE;
11408 si->editor_cascade.el_bd_effects = FALSE;
11409 si->editor_cascade.el_em = TRUE;
11410 si->editor_cascade.el_emc = TRUE;
11411 si->editor_cascade.el_rnd = TRUE;
11412 si->editor_cascade.el_sb = TRUE;
11413 si->editor_cascade.el_sp = TRUE;
11414 si->editor_cascade.el_dc = TRUE;
11415 si->editor_cascade.el_dx = TRUE;
11417 si->editor_cascade.el_mm = TRUE;
11418 si->editor_cascade.el_df = TRUE;
11420 si->editor_cascade.el_chars = FALSE;
11421 si->editor_cascade.el_steel_chars = FALSE;
11422 si->editor_cascade.el_ce = FALSE;
11423 si->editor_cascade.el_ge = FALSE;
11424 si->editor_cascade.el_es = FALSE;
11425 si->editor_cascade.el_ref = FALSE;
11426 si->editor_cascade.el_user = FALSE;
11427 si->editor_cascade.el_dynamic = FALSE;
11430 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11432 static char *getHideSetupToken(void *setup_value)
11434 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11436 if (setup_value != NULL)
11437 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11439 return hide_setup_token;
11442 void setHideSetupEntry(void *setup_value)
11444 char *hide_setup_token = getHideSetupToken(setup_value);
11446 if (hide_setup_hash == NULL)
11447 hide_setup_hash = newSetupFileHash();
11449 if (setup_value != NULL)
11450 setHashEntry(hide_setup_hash, hide_setup_token, "");
11453 void removeHideSetupEntry(void *setup_value)
11455 char *hide_setup_token = getHideSetupToken(setup_value);
11457 if (setup_value != NULL)
11458 removeHashEntry(hide_setup_hash, hide_setup_token);
11461 boolean hideSetupEntry(void *setup_value)
11463 char *hide_setup_token = getHideSetupToken(setup_value);
11465 return (setup_value != NULL &&
11466 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11469 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11470 struct TokenInfo *token_info,
11471 int token_nr, char *token_text)
11473 char *token_hide_text = getStringCat2(token_text, ".hide");
11474 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11476 // set the value of this setup option in the setup option structure
11477 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11479 // check if this setup option should be hidden in the setup menu
11480 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11481 setHideSetupEntry(token_info[token_nr].value);
11483 free(token_hide_text);
11486 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11487 struct TokenInfo *token_info,
11490 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11491 token_info[token_nr].text);
11494 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11498 if (!setup_file_hash)
11501 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11502 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11504 setup.touch.grid_initialized = TRUE;
11505 for (i = 0; i < 2; i++)
11507 int grid_xsize = setup.touch.grid_xsize[i];
11508 int grid_ysize = setup.touch.grid_ysize[i];
11511 // if virtual buttons are not loaded from setup file, repeat initializing
11512 // virtual buttons grid with default values later when video is initialized
11513 if (grid_xsize == -1 ||
11516 setup.touch.grid_initialized = FALSE;
11521 for (y = 0; y < grid_ysize; y++)
11523 char token_string[MAX_LINE_LEN];
11525 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11527 char *value_string = getHashEntry(setup_file_hash, token_string);
11529 if (value_string == NULL)
11532 for (x = 0; x < grid_xsize; x++)
11534 char c = value_string[x];
11536 setup.touch.grid_button[i][x][y] =
11537 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11542 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11543 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11545 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11546 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11548 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11552 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11554 setup_input = setup.input[pnr];
11555 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11557 char full_token[100];
11559 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11560 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11563 setup.input[pnr] = setup_input;
11566 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11567 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11569 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11570 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11572 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11573 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11575 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11576 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11578 setHideRelatedSetupEntries();
11581 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11585 if (!setup_file_hash)
11588 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11589 setSetupInfo(auto_setup_tokens, i,
11590 getHashEntry(setup_file_hash,
11591 auto_setup_tokens[i].text));
11594 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11598 if (!setup_file_hash)
11601 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11602 setSetupInfo(server_setup_tokens, i,
11603 getHashEntry(setup_file_hash,
11604 server_setup_tokens[i].text));
11607 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11611 if (!setup_file_hash)
11614 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11615 setSetupInfo(editor_cascade_setup_tokens, i,
11616 getHashEntry(setup_file_hash,
11617 editor_cascade_setup_tokens[i].text));
11620 void LoadUserNames(void)
11622 int last_user_nr = user.nr;
11625 if (global.user_names != NULL)
11627 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11628 checked_free(global.user_names[i]);
11630 checked_free(global.user_names);
11633 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11635 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11639 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11641 if (setup_file_hash)
11643 char *player_name = getHashEntry(setup_file_hash, "player_name");
11645 global.user_names[i] = getFixedUserName(player_name);
11647 freeSetupFileHash(setup_file_hash);
11650 if (global.user_names[i] == NULL)
11651 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11654 user.nr = last_user_nr;
11657 void LoadSetupFromFilename(char *filename)
11659 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11661 if (setup_file_hash)
11663 decodeSetupFileHash_Default(setup_file_hash);
11665 freeSetupFileHash(setup_file_hash);
11669 Debug("setup", "using default setup values");
11673 static void LoadSetup_SpecialPostProcessing(void)
11675 char *player_name_new;
11677 // needed to work around problems with fixed length strings
11678 player_name_new = getFixedUserName(setup.player_name);
11679 free(setup.player_name);
11680 setup.player_name = player_name_new;
11682 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11683 if (setup.scroll_delay == FALSE)
11685 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11686 setup.scroll_delay = TRUE; // now always "on"
11689 // make sure that scroll delay value stays inside valid range
11690 setup.scroll_delay_value =
11691 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11694 void LoadSetup_Default(void)
11698 // always start with reliable default values
11699 setSetupInfoToDefaults(&setup);
11701 // try to load setup values from default setup file
11702 filename = getDefaultSetupFilename();
11704 if (fileExists(filename))
11705 LoadSetupFromFilename(filename);
11707 // try to load setup values from platform setup file
11708 filename = getPlatformSetupFilename();
11710 if (fileExists(filename))
11711 LoadSetupFromFilename(filename);
11713 // try to load setup values from user setup file
11714 filename = getSetupFilename();
11716 LoadSetupFromFilename(filename);
11718 LoadSetup_SpecialPostProcessing();
11721 void LoadSetup_AutoSetup(void)
11723 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11724 SetupFileHash *setup_file_hash = NULL;
11726 // always start with reliable default values
11727 setSetupInfoToDefaults_AutoSetup(&setup);
11729 setup_file_hash = loadSetupFileHash(filename);
11731 if (setup_file_hash)
11733 decodeSetupFileHash_AutoSetup(setup_file_hash);
11735 freeSetupFileHash(setup_file_hash);
11741 void LoadSetup_ServerSetup(void)
11743 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11744 SetupFileHash *setup_file_hash = NULL;
11746 // always start with reliable default values
11747 setSetupInfoToDefaults_ServerSetup(&setup);
11749 setup_file_hash = loadSetupFileHash(filename);
11751 if (setup_file_hash)
11753 decodeSetupFileHash_ServerSetup(setup_file_hash);
11755 freeSetupFileHash(setup_file_hash);
11760 if (setup.player_uuid == NULL)
11762 // player UUID does not yet exist in setup file
11763 setup.player_uuid = getStringCopy(getUUID());
11764 setup.player_version = 2;
11766 SaveSetup_ServerSetup();
11770 void LoadSetup_EditorCascade(void)
11772 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11773 SetupFileHash *setup_file_hash = NULL;
11775 // always start with reliable default values
11776 setSetupInfoToDefaults_EditorCascade(&setup);
11778 setup_file_hash = loadSetupFileHash(filename);
11780 if (setup_file_hash)
11782 decodeSetupFileHash_EditorCascade(setup_file_hash);
11784 freeSetupFileHash(setup_file_hash);
11790 void LoadSetup(void)
11792 LoadSetup_Default();
11793 LoadSetup_AutoSetup();
11794 LoadSetup_ServerSetup();
11795 LoadSetup_EditorCascade();
11798 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11799 char *mapping_line)
11801 char mapping_guid[MAX_LINE_LEN];
11802 char *mapping_start, *mapping_end;
11804 // get GUID from game controller mapping line: copy complete line
11805 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11806 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11808 // get GUID from game controller mapping line: cut after GUID part
11809 mapping_start = strchr(mapping_guid, ',');
11810 if (mapping_start != NULL)
11811 *mapping_start = '\0';
11813 // cut newline from game controller mapping line
11814 mapping_end = strchr(mapping_line, '\n');
11815 if (mapping_end != NULL)
11816 *mapping_end = '\0';
11818 // add mapping entry to game controller mappings hash
11819 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11822 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11827 if (!(file = fopen(filename, MODE_READ)))
11829 Warn("cannot read game controller mappings file '%s'", filename);
11834 while (!feof(file))
11836 char line[MAX_LINE_LEN];
11838 if (!fgets(line, MAX_LINE_LEN, file))
11841 addGameControllerMappingToHash(mappings_hash, line);
11847 void SaveSetup_Default(void)
11849 char *filename = getSetupFilename();
11853 InitUserDataDirectory();
11855 if (!(file = fopen(filename, MODE_WRITE)))
11857 Warn("cannot write setup file '%s'", filename);
11862 fprintFileHeader(file, SETUP_FILENAME);
11864 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11866 // just to make things nicer :)
11867 if (global_setup_tokens[i].value == &setup.multiple_users ||
11868 global_setup_tokens[i].value == &setup.sound ||
11869 global_setup_tokens[i].value == &setup.graphics_set ||
11870 global_setup_tokens[i].value == &setup.volume_simple ||
11871 global_setup_tokens[i].value == &setup.network_mode ||
11872 global_setup_tokens[i].value == &setup.touch.control_type ||
11873 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11874 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11875 fprintf(file, "\n");
11877 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11880 for (i = 0; i < 2; i++)
11882 int grid_xsize = setup.touch.grid_xsize[i];
11883 int grid_ysize = setup.touch.grid_ysize[i];
11886 fprintf(file, "\n");
11888 for (y = 0; y < grid_ysize; y++)
11890 char token_string[MAX_LINE_LEN];
11891 char value_string[MAX_LINE_LEN];
11893 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11895 for (x = 0; x < grid_xsize; x++)
11897 char c = setup.touch.grid_button[i][x][y];
11899 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11902 value_string[grid_xsize] = '\0';
11904 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11908 fprintf(file, "\n");
11909 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11910 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11912 fprintf(file, "\n");
11913 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11914 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11916 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11920 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11921 fprintf(file, "\n");
11923 setup_input = setup.input[pnr];
11924 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11925 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11928 fprintf(file, "\n");
11929 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11930 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11932 // (internal setup values not saved to user setup file)
11934 fprintf(file, "\n");
11935 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11936 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11937 setup.debug.xsn_mode != AUTO)
11938 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11940 fprintf(file, "\n");
11941 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11942 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11946 SetFilePermissions(filename, PERMS_PRIVATE);
11949 void SaveSetup_AutoSetup(void)
11951 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11955 InitUserDataDirectory();
11957 if (!(file = fopen(filename, MODE_WRITE)))
11959 Warn("cannot write auto setup file '%s'", filename);
11966 fprintFileHeader(file, AUTOSETUP_FILENAME);
11968 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11969 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11973 SetFilePermissions(filename, PERMS_PRIVATE);
11978 void SaveSetup_ServerSetup(void)
11980 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11984 InitUserDataDirectory();
11986 if (!(file = fopen(filename, MODE_WRITE)))
11988 Warn("cannot write server setup file '%s'", filename);
11995 fprintFileHeader(file, SERVERSETUP_FILENAME);
11997 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11999 // just to make things nicer :)
12000 if (server_setup_tokens[i].value == &setup.use_api_server)
12001 fprintf(file, "\n");
12003 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12008 SetFilePermissions(filename, PERMS_PRIVATE);
12013 void SaveSetup_EditorCascade(void)
12015 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12019 InitUserDataDirectory();
12021 if (!(file = fopen(filename, MODE_WRITE)))
12023 Warn("cannot write editor cascade state file '%s'", filename);
12030 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12032 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12033 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12037 SetFilePermissions(filename, PERMS_PRIVATE);
12042 void SaveSetup(void)
12044 SaveSetup_Default();
12045 SaveSetup_AutoSetup();
12046 SaveSetup_ServerSetup();
12047 SaveSetup_EditorCascade();
12050 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12055 if (!(file = fopen(filename, MODE_WRITE)))
12057 Warn("cannot write game controller mappings file '%s'", filename);
12062 BEGIN_HASH_ITERATION(mappings_hash, itr)
12064 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12066 END_HASH_ITERATION(mappings_hash, itr)
12071 void SaveSetup_AddGameControllerMapping(char *mapping)
12073 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12074 SetupFileHash *mappings_hash = newSetupFileHash();
12076 InitUserDataDirectory();
12078 // load existing personal game controller mappings
12079 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12081 // add new mapping to personal game controller mappings
12082 addGameControllerMappingToHash(mappings_hash, mapping);
12084 // save updated personal game controller mappings
12085 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12087 freeSetupFileHash(mappings_hash);
12091 void LoadCustomElementDescriptions(void)
12093 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12094 SetupFileHash *setup_file_hash;
12097 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12099 if (element_info[i].custom_description != NULL)
12101 free(element_info[i].custom_description);
12102 element_info[i].custom_description = NULL;
12106 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12109 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12111 char *token = getStringCat2(element_info[i].token_name, ".name");
12112 char *value = getHashEntry(setup_file_hash, token);
12115 element_info[i].custom_description = getStringCopy(value);
12120 freeSetupFileHash(setup_file_hash);
12123 static int getElementFromToken(char *token)
12125 char *value = getHashEntry(element_token_hash, token);
12128 return atoi(value);
12130 Warn("unknown element token '%s'", token);
12132 return EL_UNDEFINED;
12135 void FreeGlobalAnimEventInfo(void)
12137 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12139 if (gaei->event_list == NULL)
12144 for (i = 0; i < gaei->num_event_lists; i++)
12146 checked_free(gaei->event_list[i]->event_value);
12147 checked_free(gaei->event_list[i]);
12150 checked_free(gaei->event_list);
12152 gaei->event_list = NULL;
12153 gaei->num_event_lists = 0;
12156 static int AddGlobalAnimEventList(void)
12158 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12159 int list_pos = gaei->num_event_lists++;
12161 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12162 sizeof(struct GlobalAnimEventListInfo *));
12164 gaei->event_list[list_pos] =
12165 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12167 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12169 gaeli->event_value = NULL;
12170 gaeli->num_event_values = 0;
12175 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12177 // do not add empty global animation events
12178 if (event_value == ANIM_EVENT_NONE)
12181 // if list position is undefined, create new list
12182 if (list_pos == ANIM_EVENT_UNDEFINED)
12183 list_pos = AddGlobalAnimEventList();
12185 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12186 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12187 int value_pos = gaeli->num_event_values++;
12189 gaeli->event_value = checked_realloc(gaeli->event_value,
12190 gaeli->num_event_values * sizeof(int *));
12192 gaeli->event_value[value_pos] = event_value;
12197 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12199 if (list_pos == ANIM_EVENT_UNDEFINED)
12200 return ANIM_EVENT_NONE;
12202 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12203 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12205 return gaeli->event_value[value_pos];
12208 int GetGlobalAnimEventValueCount(int list_pos)
12210 if (list_pos == ANIM_EVENT_UNDEFINED)
12213 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12214 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12216 return gaeli->num_event_values;
12219 // This function checks if a string <s> of the format "string1, string2, ..."
12220 // exactly contains a string <s_contained>.
12222 static boolean string_has_parameter(char *s, char *s_contained)
12226 if (s == NULL || s_contained == NULL)
12229 if (strlen(s_contained) > strlen(s))
12232 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12234 char next_char = s[strlen(s_contained)];
12236 // check if next character is delimiter or whitespace
12237 if (next_char == ',' || next_char == '\0' ||
12238 next_char == ' ' || next_char == '\t')
12242 // check if string contains another parameter string after a comma
12243 substring = strchr(s, ',');
12244 if (substring == NULL) // string does not contain a comma
12247 // advance string pointer to next character after the comma
12250 // skip potential whitespaces after the comma
12251 while (*substring == ' ' || *substring == '\t')
12254 return string_has_parameter(substring, s_contained);
12257 static int get_anim_parameter_value_ce(char *s)
12260 char *pattern_1 = "ce_change:custom_";
12261 char *pattern_2 = ".page_";
12262 int pattern_1_len = strlen(pattern_1);
12263 char *matching_char = strstr(s_ptr, pattern_1);
12264 int result = ANIM_EVENT_NONE;
12266 if (matching_char == NULL)
12267 return ANIM_EVENT_NONE;
12269 result = ANIM_EVENT_CE_CHANGE;
12271 s_ptr = matching_char + pattern_1_len;
12273 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12274 if (*s_ptr >= '0' && *s_ptr <= '9')
12276 int gic_ce_nr = (*s_ptr++ - '0');
12278 if (*s_ptr >= '0' && *s_ptr <= '9')
12280 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12282 if (*s_ptr >= '0' && *s_ptr <= '9')
12283 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12286 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12287 return ANIM_EVENT_NONE;
12289 // custom element stored as 0 to 255
12292 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12296 // invalid custom element number specified
12298 return ANIM_EVENT_NONE;
12301 // check for change page number ("page_X" or "page_XX") (optional)
12302 if (strPrefix(s_ptr, pattern_2))
12304 s_ptr += strlen(pattern_2);
12306 if (*s_ptr >= '0' && *s_ptr <= '9')
12308 int gic_page_nr = (*s_ptr++ - '0');
12310 if (*s_ptr >= '0' && *s_ptr <= '9')
12311 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12313 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12314 return ANIM_EVENT_NONE;
12316 // change page stored as 1 to 32 (0 means "all change pages")
12318 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12322 // invalid animation part number specified
12324 return ANIM_EVENT_NONE;
12328 // discard result if next character is neither delimiter nor whitespace
12329 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12330 *s_ptr == ' ' || *s_ptr == '\t'))
12331 return ANIM_EVENT_NONE;
12336 static int get_anim_parameter_value(char *s)
12338 int event_value[] =
12346 char *pattern_1[] =
12354 char *pattern_2 = ".part_";
12355 char *matching_char = NULL;
12357 int pattern_1_len = 0;
12358 int result = ANIM_EVENT_NONE;
12361 result = get_anim_parameter_value_ce(s);
12363 if (result != ANIM_EVENT_NONE)
12366 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12368 matching_char = strstr(s_ptr, pattern_1[i]);
12369 pattern_1_len = strlen(pattern_1[i]);
12370 result = event_value[i];
12372 if (matching_char != NULL)
12376 if (matching_char == NULL)
12377 return ANIM_EVENT_NONE;
12379 s_ptr = matching_char + pattern_1_len;
12381 // check for main animation number ("anim_X" or "anim_XX")
12382 if (*s_ptr >= '0' && *s_ptr <= '9')
12384 int gic_anim_nr = (*s_ptr++ - '0');
12386 if (*s_ptr >= '0' && *s_ptr <= '9')
12387 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12389 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12390 return ANIM_EVENT_NONE;
12392 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12396 // invalid main animation number specified
12398 return ANIM_EVENT_NONE;
12401 // check for animation part number ("part_X" or "part_XX") (optional)
12402 if (strPrefix(s_ptr, pattern_2))
12404 s_ptr += strlen(pattern_2);
12406 if (*s_ptr >= '0' && *s_ptr <= '9')
12408 int gic_part_nr = (*s_ptr++ - '0');
12410 if (*s_ptr >= '0' && *s_ptr <= '9')
12411 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12413 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12414 return ANIM_EVENT_NONE;
12416 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12420 // invalid animation part number specified
12422 return ANIM_EVENT_NONE;
12426 // discard result if next character is neither delimiter nor whitespace
12427 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12428 *s_ptr == ' ' || *s_ptr == '\t'))
12429 return ANIM_EVENT_NONE;
12434 static int get_anim_parameter_values(char *s)
12436 int list_pos = ANIM_EVENT_UNDEFINED;
12437 int event_value = ANIM_EVENT_DEFAULT;
12439 if (string_has_parameter(s, "any"))
12440 event_value |= ANIM_EVENT_ANY;
12442 if (string_has_parameter(s, "click:self") ||
12443 string_has_parameter(s, "click") ||
12444 string_has_parameter(s, "self"))
12445 event_value |= ANIM_EVENT_SELF;
12447 if (string_has_parameter(s, "unclick:any"))
12448 event_value |= ANIM_EVENT_UNCLICK_ANY;
12450 // if animation event found, add it to global animation event list
12451 if (event_value != ANIM_EVENT_NONE)
12452 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12456 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12457 event_value = get_anim_parameter_value(s);
12459 // if animation event found, add it to global animation event list
12460 if (event_value != ANIM_EVENT_NONE)
12461 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12463 // continue with next part of the string, starting with next comma
12464 s = strchr(s + 1, ',');
12470 static int get_anim_action_parameter_value(char *token)
12472 // check most common default case first to massively speed things up
12473 if (strEqual(token, ARG_UNDEFINED))
12474 return ANIM_EVENT_ACTION_NONE;
12476 int result = getImageIDFromToken(token);
12480 char *gfx_token = getStringCat2("gfx.", token);
12482 result = getImageIDFromToken(gfx_token);
12484 checked_free(gfx_token);
12489 Key key = getKeyFromX11KeyName(token);
12491 if (key != KSYM_UNDEFINED)
12492 result = -(int)key;
12499 result = get_hash_from_string(token); // unsigned int => int
12500 result = ABS(result); // may be negative now
12501 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12503 setHashEntry(anim_url_hash, int2str(result, 0), token);
12508 result = ANIM_EVENT_ACTION_NONE;
12513 int get_parameter_value(char *value_raw, char *suffix, int type)
12515 char *value = getStringToLower(value_raw);
12516 int result = 0; // probably a save default value
12518 if (strEqual(suffix, ".direction"))
12520 result = (strEqual(value, "left") ? MV_LEFT :
12521 strEqual(value, "right") ? MV_RIGHT :
12522 strEqual(value, "up") ? MV_UP :
12523 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12525 else if (strEqual(suffix, ".position"))
12527 result = (strEqual(value, "left") ? POS_LEFT :
12528 strEqual(value, "right") ? POS_RIGHT :
12529 strEqual(value, "top") ? POS_TOP :
12530 strEqual(value, "upper") ? POS_UPPER :
12531 strEqual(value, "middle") ? POS_MIDDLE :
12532 strEqual(value, "lower") ? POS_LOWER :
12533 strEqual(value, "bottom") ? POS_BOTTOM :
12534 strEqual(value, "any") ? POS_ANY :
12535 strEqual(value, "ce") ? POS_CE :
12536 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12537 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12539 else if (strEqual(suffix, ".align"))
12541 result = (strEqual(value, "left") ? ALIGN_LEFT :
12542 strEqual(value, "right") ? ALIGN_RIGHT :
12543 strEqual(value, "center") ? ALIGN_CENTER :
12544 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12546 else if (strEqual(suffix, ".valign"))
12548 result = (strEqual(value, "top") ? VALIGN_TOP :
12549 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12550 strEqual(value, "middle") ? VALIGN_MIDDLE :
12551 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12553 else if (strEqual(suffix, ".anim_mode"))
12555 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12556 string_has_parameter(value, "loop") ? ANIM_LOOP :
12557 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12558 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12559 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12560 string_has_parameter(value, "random") ? ANIM_RANDOM :
12561 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12562 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12563 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12564 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12565 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12566 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12567 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12568 string_has_parameter(value, "all") ? ANIM_ALL :
12569 string_has_parameter(value, "tiled") ? ANIM_TILED :
12570 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12573 if (string_has_parameter(value, "once"))
12574 result |= ANIM_ONCE;
12576 if (string_has_parameter(value, "reverse"))
12577 result |= ANIM_REVERSE;
12579 if (string_has_parameter(value, "opaque_player"))
12580 result |= ANIM_OPAQUE_PLAYER;
12582 if (string_has_parameter(value, "static_panel"))
12583 result |= ANIM_STATIC_PANEL;
12585 else if (strEqual(suffix, ".init_event") ||
12586 strEqual(suffix, ".anim_event"))
12588 result = get_anim_parameter_values(value);
12590 else if (strEqual(suffix, ".init_delay_action") ||
12591 strEqual(suffix, ".anim_delay_action") ||
12592 strEqual(suffix, ".post_delay_action") ||
12593 strEqual(suffix, ".init_event_action") ||
12594 strEqual(suffix, ".anim_event_action"))
12596 result = get_anim_action_parameter_value(value_raw);
12598 else if (strEqual(suffix, ".class"))
12600 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12601 get_hash_from_string(value));
12603 else if (strEqual(suffix, ".style"))
12605 result = STYLE_DEFAULT;
12607 if (string_has_parameter(value, "accurate_borders"))
12608 result |= STYLE_ACCURATE_BORDERS;
12610 if (string_has_parameter(value, "inner_corners"))
12611 result |= STYLE_INNER_CORNERS;
12613 if (string_has_parameter(value, "reverse"))
12614 result |= STYLE_REVERSE;
12616 if (string_has_parameter(value, "leftmost_position"))
12617 result |= STYLE_LEFTMOST_POSITION;
12619 if (string_has_parameter(value, "block_clicks"))
12620 result |= STYLE_BLOCK;
12622 if (string_has_parameter(value, "passthrough_clicks"))
12623 result |= STYLE_PASSTHROUGH;
12625 if (string_has_parameter(value, "multiple_actions"))
12626 result |= STYLE_MULTIPLE_ACTIONS;
12628 if (string_has_parameter(value, "consume_ce_event"))
12629 result |= STYLE_CONSUME_CE_EVENT;
12631 else if (strEqual(suffix, ".fade_mode"))
12633 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12634 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12635 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12636 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12637 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12638 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12639 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12640 FADE_MODE_DEFAULT);
12642 else if (strEqual(suffix, ".auto_delay_unit"))
12644 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12645 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12646 AUTO_DELAY_UNIT_DEFAULT);
12648 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12650 result = gfx.get_font_from_token_function(value);
12652 else // generic parameter of type integer or boolean
12654 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12655 type == TYPE_INTEGER ? get_integer_from_string(value) :
12656 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12657 ARG_UNDEFINED_VALUE);
12665 static int get_token_parameter_value(char *token, char *value_raw)
12669 if (token == NULL || value_raw == NULL)
12670 return ARG_UNDEFINED_VALUE;
12672 suffix = strrchr(token, '.');
12673 if (suffix == NULL)
12676 if (strEqual(suffix, ".element"))
12677 return getElementFromToken(value_raw);
12679 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12680 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12683 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12684 boolean ignore_defaults)
12688 for (i = 0; image_config_vars[i].token != NULL; i++)
12690 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12692 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12693 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12697 *image_config_vars[i].value =
12698 get_token_parameter_value(image_config_vars[i].token, value);
12702 void InitMenuDesignSettings_Static(void)
12704 // always start with reliable default values from static default config
12705 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12708 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12712 // the following initializes hierarchical values from static configuration
12714 // special case: initialize "ARG_DEFAULT" values in static default config
12715 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12716 titlescreen_initial_first_default.fade_mode =
12717 title_initial_first_default.fade_mode;
12718 titlescreen_initial_first_default.fade_delay =
12719 title_initial_first_default.fade_delay;
12720 titlescreen_initial_first_default.post_delay =
12721 title_initial_first_default.post_delay;
12722 titlescreen_initial_first_default.auto_delay =
12723 title_initial_first_default.auto_delay;
12724 titlescreen_initial_first_default.auto_delay_unit =
12725 title_initial_first_default.auto_delay_unit;
12726 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12727 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12728 titlescreen_first_default.post_delay = title_first_default.post_delay;
12729 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12730 titlescreen_first_default.auto_delay_unit =
12731 title_first_default.auto_delay_unit;
12732 titlemessage_initial_first_default.fade_mode =
12733 title_initial_first_default.fade_mode;
12734 titlemessage_initial_first_default.fade_delay =
12735 title_initial_first_default.fade_delay;
12736 titlemessage_initial_first_default.post_delay =
12737 title_initial_first_default.post_delay;
12738 titlemessage_initial_first_default.auto_delay =
12739 title_initial_first_default.auto_delay;
12740 titlemessage_initial_first_default.auto_delay_unit =
12741 title_initial_first_default.auto_delay_unit;
12742 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12743 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12744 titlemessage_first_default.post_delay = title_first_default.post_delay;
12745 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12746 titlemessage_first_default.auto_delay_unit =
12747 title_first_default.auto_delay_unit;
12749 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12750 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12751 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12752 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12753 titlescreen_initial_default.auto_delay_unit =
12754 title_initial_default.auto_delay_unit;
12755 titlescreen_default.fade_mode = title_default.fade_mode;
12756 titlescreen_default.fade_delay = title_default.fade_delay;
12757 titlescreen_default.post_delay = title_default.post_delay;
12758 titlescreen_default.auto_delay = title_default.auto_delay;
12759 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12760 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12761 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12762 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12763 titlemessage_initial_default.auto_delay_unit =
12764 title_initial_default.auto_delay_unit;
12765 titlemessage_default.fade_mode = title_default.fade_mode;
12766 titlemessage_default.fade_delay = title_default.fade_delay;
12767 titlemessage_default.post_delay = title_default.post_delay;
12768 titlemessage_default.auto_delay = title_default.auto_delay;
12769 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12771 // special case: initialize "ARG_DEFAULT" values in static default config
12772 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12773 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12775 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12776 titlescreen_first[i] = titlescreen_first_default;
12777 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12778 titlemessage_first[i] = titlemessage_first_default;
12780 titlescreen_initial[i] = titlescreen_initial_default;
12781 titlescreen[i] = titlescreen_default;
12782 titlemessage_initial[i] = titlemessage_initial_default;
12783 titlemessage[i] = titlemessage_default;
12786 // special case: initialize "ARG_DEFAULT" values in static default config
12787 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12788 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12790 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12793 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12794 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12795 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12798 // special case: initialize "ARG_DEFAULT" values in static default config
12799 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12800 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12802 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12803 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12804 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12806 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12809 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12813 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12817 struct XY *dst, *src;
12819 game_buttons_xy[] =
12821 { &game.button.save, &game.button.stop },
12822 { &game.button.pause2, &game.button.pause },
12823 { &game.button.load, &game.button.play },
12824 { &game.button.undo, &game.button.stop },
12825 { &game.button.redo, &game.button.play },
12831 // special case: initialize later added SETUP list size from LEVELS value
12832 if (menu.list_size[GAME_MODE_SETUP] == -1)
12833 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12835 // set default position for snapshot buttons to stop/pause/play buttons
12836 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12837 if ((*game_buttons_xy[i].dst).x == -1 &&
12838 (*game_buttons_xy[i].dst).y == -1)
12839 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12841 // --------------------------------------------------------------------------
12842 // dynamic viewports (including playfield margins, borders and alignments)
12843 // --------------------------------------------------------------------------
12845 // dynamic viewports currently only supported for landscape mode
12846 int display_width = MAX(video.display_width, video.display_height);
12847 int display_height = MIN(video.display_width, video.display_height);
12849 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12851 struct RectWithBorder *vp_window = &viewport.window[i];
12852 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12853 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12854 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12855 boolean dynamic_window_width = (vp_window->min_width != -1);
12856 boolean dynamic_window_height = (vp_window->min_height != -1);
12857 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12858 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12860 // adjust window size if min/max width/height is specified
12862 if (vp_window->min_width != -1)
12864 int window_width = display_width;
12866 // when using static window height, use aspect ratio of display
12867 if (vp_window->min_height == -1)
12868 window_width = vp_window->height * display_width / display_height;
12870 vp_window->width = MAX(vp_window->min_width, window_width);
12873 if (vp_window->min_height != -1)
12875 int window_height = display_height;
12877 // when using static window width, use aspect ratio of display
12878 if (vp_window->min_width == -1)
12879 window_height = vp_window->width * display_height / display_width;
12881 vp_window->height = MAX(vp_window->min_height, window_height);
12884 if (vp_window->max_width != -1)
12885 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12887 if (vp_window->max_height != -1)
12888 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12890 int playfield_width = vp_window->width;
12891 int playfield_height = vp_window->height;
12893 // adjust playfield size and position according to specified margins
12895 playfield_width -= vp_playfield->margin_left;
12896 playfield_width -= vp_playfield->margin_right;
12898 playfield_height -= vp_playfield->margin_top;
12899 playfield_height -= vp_playfield->margin_bottom;
12901 // adjust playfield size if min/max width/height is specified
12903 if (vp_playfield->min_width != -1)
12904 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12906 if (vp_playfield->min_height != -1)
12907 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12909 if (vp_playfield->max_width != -1)
12910 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12912 if (vp_playfield->max_height != -1)
12913 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12915 // adjust playfield position according to specified alignment
12917 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12918 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12919 else if (vp_playfield->align == ALIGN_CENTER)
12920 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12921 else if (vp_playfield->align == ALIGN_RIGHT)
12922 vp_playfield->x += playfield_width - vp_playfield->width;
12924 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12925 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12926 else if (vp_playfield->valign == VALIGN_MIDDLE)
12927 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12928 else if (vp_playfield->valign == VALIGN_BOTTOM)
12929 vp_playfield->y += playfield_height - vp_playfield->height;
12931 vp_playfield->x += vp_playfield->margin_left;
12932 vp_playfield->y += vp_playfield->margin_top;
12934 // adjust individual playfield borders if only default border is specified
12936 if (vp_playfield->border_left == -1)
12937 vp_playfield->border_left = vp_playfield->border_size;
12938 if (vp_playfield->border_right == -1)
12939 vp_playfield->border_right = vp_playfield->border_size;
12940 if (vp_playfield->border_top == -1)
12941 vp_playfield->border_top = vp_playfield->border_size;
12942 if (vp_playfield->border_bottom == -1)
12943 vp_playfield->border_bottom = vp_playfield->border_size;
12945 // set dynamic playfield borders if borders are specified as undefined
12946 // (but only if window size was dynamic and playfield size was static)
12948 if (dynamic_window_width && !dynamic_playfield_width)
12950 if (vp_playfield->border_left == -1)
12952 vp_playfield->border_left = (vp_playfield->x -
12953 vp_playfield->margin_left);
12954 vp_playfield->x -= vp_playfield->border_left;
12955 vp_playfield->width += vp_playfield->border_left;
12958 if (vp_playfield->border_right == -1)
12960 vp_playfield->border_right = (vp_window->width -
12962 vp_playfield->width -
12963 vp_playfield->margin_right);
12964 vp_playfield->width += vp_playfield->border_right;
12968 if (dynamic_window_height && !dynamic_playfield_height)
12970 if (vp_playfield->border_top == -1)
12972 vp_playfield->border_top = (vp_playfield->y -
12973 vp_playfield->margin_top);
12974 vp_playfield->y -= vp_playfield->border_top;
12975 vp_playfield->height += vp_playfield->border_top;
12978 if (vp_playfield->border_bottom == -1)
12980 vp_playfield->border_bottom = (vp_window->height -
12982 vp_playfield->height -
12983 vp_playfield->margin_bottom);
12984 vp_playfield->height += vp_playfield->border_bottom;
12988 // adjust playfield size to be a multiple of a defined alignment tile size
12990 int align_size = vp_playfield->align_size;
12991 int playfield_xtiles = vp_playfield->width / align_size;
12992 int playfield_ytiles = vp_playfield->height / align_size;
12993 int playfield_width_corrected = playfield_xtiles * align_size;
12994 int playfield_height_corrected = playfield_ytiles * align_size;
12995 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12996 i == GFX_SPECIAL_ARG_EDITOR);
12998 if (is_playfield_mode &&
12999 dynamic_playfield_width &&
13000 vp_playfield->width != playfield_width_corrected)
13002 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13004 vp_playfield->width = playfield_width_corrected;
13006 if (vp_playfield->align == ALIGN_LEFT)
13008 vp_playfield->border_left += playfield_xdiff;
13010 else if (vp_playfield->align == ALIGN_RIGHT)
13012 vp_playfield->border_right += playfield_xdiff;
13014 else if (vp_playfield->align == ALIGN_CENTER)
13016 int border_left_diff = playfield_xdiff / 2;
13017 int border_right_diff = playfield_xdiff - border_left_diff;
13019 vp_playfield->border_left += border_left_diff;
13020 vp_playfield->border_right += border_right_diff;
13024 if (is_playfield_mode &&
13025 dynamic_playfield_height &&
13026 vp_playfield->height != playfield_height_corrected)
13028 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13030 vp_playfield->height = playfield_height_corrected;
13032 if (vp_playfield->valign == VALIGN_TOP)
13034 vp_playfield->border_top += playfield_ydiff;
13036 else if (vp_playfield->align == VALIGN_BOTTOM)
13038 vp_playfield->border_right += playfield_ydiff;
13040 else if (vp_playfield->align == VALIGN_MIDDLE)
13042 int border_top_diff = playfield_ydiff / 2;
13043 int border_bottom_diff = playfield_ydiff - border_top_diff;
13045 vp_playfield->border_top += border_top_diff;
13046 vp_playfield->border_bottom += border_bottom_diff;
13050 // adjust door positions according to specified alignment
13052 for (j = 0; j < 2; j++)
13054 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13056 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13057 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13058 else if (vp_door->align == ALIGN_CENTER)
13059 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13060 else if (vp_door->align == ALIGN_RIGHT)
13061 vp_door->x += vp_window->width - vp_door->width;
13063 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13064 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13065 else if (vp_door->valign == VALIGN_MIDDLE)
13066 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13067 else if (vp_door->valign == VALIGN_BOTTOM)
13068 vp_door->y += vp_window->height - vp_door->height;
13073 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13077 struct XYTileSize *dst, *src;
13080 editor_buttons_xy[] =
13083 &editor.button.element_left, &editor.palette.element_left,
13084 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13087 &editor.button.element_middle, &editor.palette.element_middle,
13088 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13091 &editor.button.element_right, &editor.palette.element_right,
13092 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13099 // set default position for element buttons to element graphics
13100 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13102 if ((*editor_buttons_xy[i].dst).x == -1 &&
13103 (*editor_buttons_xy[i].dst).y == -1)
13105 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13107 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13109 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13113 // adjust editor palette rows and columns if specified to be dynamic
13115 if (editor.palette.cols == -1)
13117 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13118 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13119 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13121 editor.palette.cols = (vp_width - sc_width) / bt_width;
13123 if (editor.palette.x == -1)
13125 int palette_width = editor.palette.cols * bt_width + sc_width;
13127 editor.palette.x = (vp_width - palette_width) / 2;
13131 if (editor.palette.rows == -1)
13133 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13134 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13135 int tx_height = getFontHeight(FONT_TEXT_2);
13137 editor.palette.rows = (vp_height - tx_height) / bt_height;
13139 if (editor.palette.y == -1)
13141 int palette_height = editor.palette.rows * bt_height + tx_height;
13143 editor.palette.y = (vp_height - palette_height) / 2;
13148 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13149 boolean initialize)
13151 // special case: check if network and preview player positions are redefined,
13152 // to compare this later against the main menu level preview being redefined
13153 struct TokenIntPtrInfo menu_config_players[] =
13155 { "main.network_players.x", &menu.main.network_players.redefined },
13156 { "main.network_players.y", &menu.main.network_players.redefined },
13157 { "main.preview_players.x", &menu.main.preview_players.redefined },
13158 { "main.preview_players.y", &menu.main.preview_players.redefined },
13159 { "preview.x", &preview.redefined },
13160 { "preview.y", &preview.redefined }
13166 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13167 *menu_config_players[i].value = FALSE;
13171 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13172 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13173 *menu_config_players[i].value = TRUE;
13177 static void InitMenuDesignSettings_PreviewPlayers(void)
13179 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13182 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13184 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13187 static void LoadMenuDesignSettingsFromFilename(char *filename)
13189 static struct TitleFadingInfo tfi;
13190 static struct TitleMessageInfo tmi;
13191 static struct TokenInfo title_tokens[] =
13193 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13194 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13195 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13196 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13197 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13201 static struct TokenInfo titlemessage_tokens[] =
13203 { TYPE_INTEGER, &tmi.x, ".x" },
13204 { TYPE_INTEGER, &tmi.y, ".y" },
13205 { TYPE_INTEGER, &tmi.width, ".width" },
13206 { TYPE_INTEGER, &tmi.height, ".height" },
13207 { TYPE_INTEGER, &tmi.chars, ".chars" },
13208 { TYPE_INTEGER, &tmi.lines, ".lines" },
13209 { TYPE_INTEGER, &tmi.align, ".align" },
13210 { TYPE_INTEGER, &tmi.valign, ".valign" },
13211 { TYPE_INTEGER, &tmi.font, ".font" },
13212 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13213 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13214 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13215 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13216 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13217 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13218 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13219 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13220 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13226 struct TitleFadingInfo *info;
13231 // initialize first titles from "enter screen" definitions, if defined
13232 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13233 { &title_first_default, "menu.enter_screen.TITLE" },
13235 // initialize title screens from "next screen" definitions, if defined
13236 { &title_initial_default, "menu.next_screen.TITLE" },
13237 { &title_default, "menu.next_screen.TITLE" },
13243 struct TitleMessageInfo *array;
13246 titlemessage_arrays[] =
13248 // initialize first titles from "enter screen" definitions, if defined
13249 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13250 { titlescreen_first, "menu.enter_screen.TITLE" },
13251 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13252 { titlemessage_first, "menu.enter_screen.TITLE" },
13254 // initialize titles from "next screen" definitions, if defined
13255 { titlescreen_initial, "menu.next_screen.TITLE" },
13256 { titlescreen, "menu.next_screen.TITLE" },
13257 { titlemessage_initial, "menu.next_screen.TITLE" },
13258 { titlemessage, "menu.next_screen.TITLE" },
13260 // overwrite titles with title definitions, if defined
13261 { titlescreen_initial_first, "[title_initial]" },
13262 { titlescreen_first, "[title]" },
13263 { titlemessage_initial_first, "[title_initial]" },
13264 { titlemessage_first, "[title]" },
13266 { titlescreen_initial, "[title_initial]" },
13267 { titlescreen, "[title]" },
13268 { titlemessage_initial, "[title_initial]" },
13269 { titlemessage, "[title]" },
13271 // overwrite titles with title screen/message definitions, if defined
13272 { titlescreen_initial_first, "[titlescreen_initial]" },
13273 { titlescreen_first, "[titlescreen]" },
13274 { titlemessage_initial_first, "[titlemessage_initial]" },
13275 { titlemessage_first, "[titlemessage]" },
13277 { titlescreen_initial, "[titlescreen_initial]" },
13278 { titlescreen, "[titlescreen]" },
13279 { titlemessage_initial, "[titlemessage_initial]" },
13280 { titlemessage, "[titlemessage]" },
13284 SetupFileHash *setup_file_hash;
13287 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13290 // the following initializes hierarchical values from dynamic configuration
13292 // special case: initialize with default values that may be overwritten
13293 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13294 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13296 struct TokenIntPtrInfo menu_config[] =
13298 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13299 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13300 { "menu.list_size", &menu.list_size[i] }
13303 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13305 char *token = menu_config[j].token;
13306 char *value = getHashEntry(setup_file_hash, token);
13309 *menu_config[j].value = get_integer_from_string(value);
13313 // special case: initialize with default values that may be overwritten
13314 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13315 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13317 struct TokenIntPtrInfo menu_config[] =
13319 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13320 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13321 { "menu.list_size.INFO", &menu.list_size_info[i] },
13322 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13323 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13326 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13328 char *token = menu_config[j].token;
13329 char *value = getHashEntry(setup_file_hash, token);
13332 *menu_config[j].value = get_integer_from_string(value);
13336 // special case: initialize with default values that may be overwritten
13337 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13338 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13340 struct TokenIntPtrInfo menu_config[] =
13342 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13343 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13346 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13348 char *token = menu_config[j].token;
13349 char *value = getHashEntry(setup_file_hash, token);
13352 *menu_config[j].value = get_integer_from_string(value);
13356 // special case: initialize with default values that may be overwritten
13357 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13358 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13360 struct TokenIntPtrInfo menu_config[] =
13362 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13363 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13364 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13365 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13366 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13367 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13368 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13369 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13370 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13371 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13374 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13376 char *token = menu_config[j].token;
13377 char *value = getHashEntry(setup_file_hash, token);
13380 *menu_config[j].value = get_integer_from_string(value);
13384 // special case: initialize with default values that may be overwritten
13385 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13386 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13388 struct TokenIntPtrInfo menu_config[] =
13390 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13391 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13392 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13393 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13394 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13395 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13396 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13397 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13398 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13401 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13403 char *token = menu_config[j].token;
13404 char *value = getHashEntry(setup_file_hash, token);
13407 *menu_config[j].value = get_token_parameter_value(token, value);
13411 // special case: initialize with default values that may be overwritten
13412 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13413 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13417 char *token_prefix;
13418 struct RectWithBorder *struct_ptr;
13422 { "viewport.window", &viewport.window[i] },
13423 { "viewport.playfield", &viewport.playfield[i] },
13424 { "viewport.door_1", &viewport.door_1[i] },
13425 { "viewport.door_2", &viewport.door_2[i] }
13428 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13430 struct TokenIntPtrInfo vp_config[] =
13432 { ".x", &vp_struct[j].struct_ptr->x },
13433 { ".y", &vp_struct[j].struct_ptr->y },
13434 { ".width", &vp_struct[j].struct_ptr->width },
13435 { ".height", &vp_struct[j].struct_ptr->height },
13436 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13437 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13438 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13439 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13440 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13441 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13442 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13443 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13444 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13445 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13446 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13447 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13448 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13449 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13450 { ".align", &vp_struct[j].struct_ptr->align },
13451 { ".valign", &vp_struct[j].struct_ptr->valign }
13454 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13456 char *token = getStringCat2(vp_struct[j].token_prefix,
13457 vp_config[k].token);
13458 char *value = getHashEntry(setup_file_hash, token);
13461 *vp_config[k].value = get_token_parameter_value(token, value);
13468 // special case: initialize with default values that may be overwritten
13469 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13470 for (i = 0; title_info[i].info != NULL; i++)
13472 struct TitleFadingInfo *info = title_info[i].info;
13473 char *base_token = title_info[i].text;
13475 for (j = 0; title_tokens[j].type != -1; j++)
13477 char *token = getStringCat2(base_token, title_tokens[j].text);
13478 char *value = getHashEntry(setup_file_hash, token);
13482 int parameter_value = get_token_parameter_value(token, value);
13486 *(int *)title_tokens[j].value = (int)parameter_value;
13495 // special case: initialize with default values that may be overwritten
13496 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13497 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13499 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13500 char *base_token = titlemessage_arrays[i].text;
13502 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13504 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13505 char *value = getHashEntry(setup_file_hash, token);
13509 int parameter_value = get_token_parameter_value(token, value);
13511 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13515 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13516 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13518 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13528 // read (and overwrite with) values that may be specified in config file
13529 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13531 // special case: check if network and preview player positions are redefined
13532 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13534 freeSetupFileHash(setup_file_hash);
13537 void LoadMenuDesignSettings(void)
13539 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13541 InitMenuDesignSettings_Static();
13542 InitMenuDesignSettings_SpecialPreProcessing();
13543 InitMenuDesignSettings_PreviewPlayers();
13545 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13547 // first look for special settings configured in level series config
13548 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13550 if (fileExists(filename_base))
13551 LoadMenuDesignSettingsFromFilename(filename_base);
13554 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13556 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13557 LoadMenuDesignSettingsFromFilename(filename_local);
13559 InitMenuDesignSettings_SpecialPostProcessing();
13562 void LoadMenuDesignSettings_AfterGraphics(void)
13564 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13567 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13568 boolean ignore_defaults)
13572 for (i = 0; sound_config_vars[i].token != NULL; i++)
13574 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13576 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13577 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13581 *sound_config_vars[i].value =
13582 get_token_parameter_value(sound_config_vars[i].token, value);
13586 void InitSoundSettings_Static(void)
13588 // always start with reliable default values from static default config
13589 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13592 static void LoadSoundSettingsFromFilename(char *filename)
13594 SetupFileHash *setup_file_hash;
13596 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13599 // read (and overwrite with) values that may be specified in config file
13600 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13602 freeSetupFileHash(setup_file_hash);
13605 void LoadSoundSettings(void)
13607 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13609 InitSoundSettings_Static();
13611 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13613 // first look for special settings configured in level series config
13614 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13616 if (fileExists(filename_base))
13617 LoadSoundSettingsFromFilename(filename_base);
13620 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13622 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13623 LoadSoundSettingsFromFilename(filename_local);
13626 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13628 char *filename = getEditorSetupFilename();
13629 SetupFileList *setup_file_list, *list;
13630 SetupFileHash *element_hash;
13631 int num_unknown_tokens = 0;
13634 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13637 element_hash = newSetupFileHash();
13639 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13640 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13642 // determined size may be larger than needed (due to unknown elements)
13644 for (list = setup_file_list; list != NULL; list = list->next)
13647 // add space for up to 3 more elements for padding that may be needed
13648 *num_elements += 3;
13650 // free memory for old list of elements, if needed
13651 checked_free(*elements);
13653 // allocate memory for new list of elements
13654 *elements = checked_malloc(*num_elements * sizeof(int));
13657 for (list = setup_file_list; list != NULL; list = list->next)
13659 char *value = getHashEntry(element_hash, list->token);
13661 if (value == NULL) // try to find obsolete token mapping
13663 char *mapped_token = get_mapped_token(list->token);
13665 if (mapped_token != NULL)
13667 value = getHashEntry(element_hash, mapped_token);
13669 free(mapped_token);
13675 (*elements)[(*num_elements)++] = atoi(value);
13679 if (num_unknown_tokens == 0)
13682 Warn("unknown token(s) found in config file:");
13683 Warn("- config file: '%s'", filename);
13685 num_unknown_tokens++;
13688 Warn("- token: '%s'", list->token);
13692 if (num_unknown_tokens > 0)
13695 while (*num_elements % 4) // pad with empty elements, if needed
13696 (*elements)[(*num_elements)++] = EL_EMPTY;
13698 freeSetupFileList(setup_file_list);
13699 freeSetupFileHash(element_hash);
13702 for (i = 0; i < *num_elements; i++)
13703 Debug("editor", "element '%s' [%d]\n",
13704 element_info[(*elements)[i]].token_name, (*elements)[i]);
13708 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13711 SetupFileHash *setup_file_hash = NULL;
13712 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13713 char *filename_music, *filename_prefix, *filename_info;
13719 token_to_value_ptr[] =
13721 { "title_header", &tmp_music_file_info.title_header },
13722 { "artist_header", &tmp_music_file_info.artist_header },
13723 { "album_header", &tmp_music_file_info.album_header },
13724 { "year_header", &tmp_music_file_info.year_header },
13725 { "played_header", &tmp_music_file_info.played_header },
13727 { "title", &tmp_music_file_info.title },
13728 { "artist", &tmp_music_file_info.artist },
13729 { "album", &tmp_music_file_info.album },
13730 { "year", &tmp_music_file_info.year },
13731 { "played", &tmp_music_file_info.played },
13737 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13738 getCustomMusicFilename(basename));
13740 if (filename_music == NULL)
13743 // ---------- try to replace file extension ----------
13745 filename_prefix = getStringCopy(filename_music);
13746 if (strrchr(filename_prefix, '.') != NULL)
13747 *strrchr(filename_prefix, '.') = '\0';
13748 filename_info = getStringCat2(filename_prefix, ".txt");
13750 if (fileExists(filename_info))
13751 setup_file_hash = loadSetupFileHash(filename_info);
13753 free(filename_prefix);
13754 free(filename_info);
13756 if (setup_file_hash == NULL)
13758 // ---------- try to add file extension ----------
13760 filename_prefix = getStringCopy(filename_music);
13761 filename_info = getStringCat2(filename_prefix, ".txt");
13763 if (fileExists(filename_info))
13764 setup_file_hash = loadSetupFileHash(filename_info);
13766 free(filename_prefix);
13767 free(filename_info);
13770 if (setup_file_hash == NULL)
13773 // ---------- music file info found ----------
13775 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13777 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13779 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13781 *token_to_value_ptr[i].value_ptr =
13782 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13785 tmp_music_file_info.basename = getStringCopy(basename);
13786 tmp_music_file_info.music = music;
13787 tmp_music_file_info.is_sound = is_sound;
13789 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13790 *new_music_file_info = tmp_music_file_info;
13792 return new_music_file_info;
13795 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13797 return get_music_file_info_ext(basename, music, FALSE);
13800 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13802 return get_music_file_info_ext(basename, sound, TRUE);
13805 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13806 char *basename, boolean is_sound)
13808 for (; list != NULL; list = list->next)
13809 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13815 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13817 return music_info_listed_ext(list, basename, FALSE);
13820 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13822 return music_info_listed_ext(list, basename, TRUE);
13825 void LoadMusicInfo(void)
13827 int num_music_noconf = getMusicListSize_NoConf();
13828 int num_music = getMusicListSize();
13829 int num_sounds = getSoundListSize();
13830 struct FileInfo *music, *sound;
13831 struct MusicFileInfo *next, **new;
13835 while (music_file_info != NULL)
13837 next = music_file_info->next;
13839 checked_free(music_file_info->basename);
13841 checked_free(music_file_info->title_header);
13842 checked_free(music_file_info->artist_header);
13843 checked_free(music_file_info->album_header);
13844 checked_free(music_file_info->year_header);
13845 checked_free(music_file_info->played_header);
13847 checked_free(music_file_info->title);
13848 checked_free(music_file_info->artist);
13849 checked_free(music_file_info->album);
13850 checked_free(music_file_info->year);
13851 checked_free(music_file_info->played);
13853 free(music_file_info);
13855 music_file_info = next;
13858 new = &music_file_info;
13860 // get (configured or unconfigured) music file info for all levels
13861 for (i = leveldir_current->first_level;
13862 i <= leveldir_current->last_level; i++)
13866 if (levelset.music[i] != MUS_UNDEFINED)
13868 // get music file info for configured level music
13869 music_nr = levelset.music[i];
13871 else if (num_music_noconf > 0)
13873 // get music file info for unconfigured level music
13874 int level_pos = i - leveldir_current->first_level;
13876 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13883 char *basename = getMusicInfoEntryFilename(music_nr);
13885 if (basename == NULL)
13888 if (!music_info_listed(music_file_info, basename))
13890 *new = get_music_file_info(basename, music_nr);
13893 new = &(*new)->next;
13897 // get music file info for all remaining configured music files
13898 for (i = 0; i < num_music; i++)
13900 music = getMusicListEntry(i);
13902 if (music->filename == NULL)
13905 if (strEqual(music->filename, UNDEFINED_FILENAME))
13908 // a configured file may be not recognized as music
13909 if (!FileIsMusic(music->filename))
13912 if (!music_info_listed(music_file_info, music->filename))
13914 *new = get_music_file_info(music->filename, i);
13917 new = &(*new)->next;
13921 // get sound file info for all configured sound files
13922 for (i = 0; i < num_sounds; i++)
13924 sound = getSoundListEntry(i);
13926 if (sound->filename == NULL)
13929 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13932 // a configured file may be not recognized as sound
13933 if (!FileIsSound(sound->filename))
13936 if (!sound_info_listed(music_file_info, sound->filename))
13938 *new = get_sound_file_info(sound->filename, i);
13940 new = &(*new)->next;
13944 // add pointers to previous list nodes
13946 struct MusicFileInfo *node = music_file_info;
13948 while (node != NULL)
13951 node->next->prev = node;
13957 static void add_helpanim_entry(int element, int action, int direction,
13958 int delay, int *num_list_entries)
13960 struct HelpAnimInfo *new_list_entry;
13961 (*num_list_entries)++;
13964 checked_realloc(helpanim_info,
13965 *num_list_entries * sizeof(struct HelpAnimInfo));
13966 new_list_entry = &helpanim_info[*num_list_entries - 1];
13968 new_list_entry->element = element;
13969 new_list_entry->action = action;
13970 new_list_entry->direction = direction;
13971 new_list_entry->delay = delay;
13974 static void print_unknown_token(char *filename, char *token, int token_nr)
13979 Warn("unknown token(s) found in config file:");
13980 Warn("- config file: '%s'", filename);
13983 Warn("- token: '%s'", token);
13986 static void print_unknown_token_end(int token_nr)
13992 void LoadHelpAnimInfo(void)
13994 char *filename = getHelpAnimFilename();
13995 SetupFileList *setup_file_list = NULL, *list;
13996 SetupFileHash *element_hash, *action_hash, *direction_hash;
13997 int num_list_entries = 0;
13998 int num_unknown_tokens = 0;
14001 if (fileExists(filename))
14002 setup_file_list = loadSetupFileList(filename);
14004 if (setup_file_list == NULL)
14006 // use reliable default values from static configuration
14007 SetupFileList *insert_ptr;
14009 insert_ptr = setup_file_list =
14010 newSetupFileList(helpanim_config[0].token,
14011 helpanim_config[0].value);
14013 for (i = 1; helpanim_config[i].token; i++)
14014 insert_ptr = addListEntry(insert_ptr,
14015 helpanim_config[i].token,
14016 helpanim_config[i].value);
14019 element_hash = newSetupFileHash();
14020 action_hash = newSetupFileHash();
14021 direction_hash = newSetupFileHash();
14023 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14024 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14026 for (i = 0; i < NUM_ACTIONS; i++)
14027 setHashEntry(action_hash, element_action_info[i].suffix,
14028 i_to_a(element_action_info[i].value));
14030 // do not store direction index (bit) here, but direction value!
14031 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14032 setHashEntry(direction_hash, element_direction_info[i].suffix,
14033 i_to_a(1 << element_direction_info[i].value));
14035 for (list = setup_file_list; list != NULL; list = list->next)
14037 char *element_token, *action_token, *direction_token;
14038 char *element_value, *action_value, *direction_value;
14039 int delay = atoi(list->value);
14041 if (strEqual(list->token, "end"))
14043 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14048 /* first try to break element into element/action/direction parts;
14049 if this does not work, also accept combined "element[.act][.dir]"
14050 elements (like "dynamite.active"), which are unique elements */
14052 if (strchr(list->token, '.') == NULL) // token contains no '.'
14054 element_value = getHashEntry(element_hash, list->token);
14055 if (element_value != NULL) // element found
14056 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14057 &num_list_entries);
14060 // no further suffixes found -- this is not an element
14061 print_unknown_token(filename, list->token, num_unknown_tokens++);
14067 // token has format "<prefix>.<something>"
14069 action_token = strchr(list->token, '.'); // suffix may be action ...
14070 direction_token = action_token; // ... or direction
14072 element_token = getStringCopy(list->token);
14073 *strchr(element_token, '.') = '\0';
14075 element_value = getHashEntry(element_hash, element_token);
14077 if (element_value == NULL) // this is no element
14079 element_value = getHashEntry(element_hash, list->token);
14080 if (element_value != NULL) // combined element found
14081 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14082 &num_list_entries);
14084 print_unknown_token(filename, list->token, num_unknown_tokens++);
14086 free(element_token);
14091 action_value = getHashEntry(action_hash, action_token);
14093 if (action_value != NULL) // action found
14095 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14096 &num_list_entries);
14098 free(element_token);
14103 direction_value = getHashEntry(direction_hash, direction_token);
14105 if (direction_value != NULL) // direction found
14107 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14108 &num_list_entries);
14110 free(element_token);
14115 if (strchr(action_token + 1, '.') == NULL)
14117 // no further suffixes found -- this is not an action nor direction
14119 element_value = getHashEntry(element_hash, list->token);
14120 if (element_value != NULL) // combined element found
14121 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14122 &num_list_entries);
14124 print_unknown_token(filename, list->token, num_unknown_tokens++);
14126 free(element_token);
14131 // token has format "<prefix>.<suffix>.<something>"
14133 direction_token = strchr(action_token + 1, '.');
14135 action_token = getStringCopy(action_token);
14136 *strchr(action_token + 1, '.') = '\0';
14138 action_value = getHashEntry(action_hash, action_token);
14140 if (action_value == NULL) // this is no action
14142 element_value = getHashEntry(element_hash, list->token);
14143 if (element_value != NULL) // combined element found
14144 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14145 &num_list_entries);
14147 print_unknown_token(filename, list->token, num_unknown_tokens++);
14149 free(element_token);
14150 free(action_token);
14155 direction_value = getHashEntry(direction_hash, direction_token);
14157 if (direction_value != NULL) // direction found
14159 add_helpanim_entry(atoi(element_value), atoi(action_value),
14160 atoi(direction_value), delay, &num_list_entries);
14162 free(element_token);
14163 free(action_token);
14168 // this is no direction
14170 element_value = getHashEntry(element_hash, list->token);
14171 if (element_value != NULL) // combined element found
14172 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14173 &num_list_entries);
14175 print_unknown_token(filename, list->token, num_unknown_tokens++);
14177 free(element_token);
14178 free(action_token);
14181 print_unknown_token_end(num_unknown_tokens);
14183 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14184 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14186 freeSetupFileList(setup_file_list);
14187 freeSetupFileHash(element_hash);
14188 freeSetupFileHash(action_hash);
14189 freeSetupFileHash(direction_hash);
14192 for (i = 0; i < num_list_entries; i++)
14193 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14194 EL_NAME(helpanim_info[i].element),
14195 helpanim_info[i].element,
14196 helpanim_info[i].action,
14197 helpanim_info[i].direction,
14198 helpanim_info[i].delay);
14202 void LoadHelpTextInfo(void)
14204 char *filename = getHelpTextFilename();
14207 if (helptext_info != NULL)
14209 freeSetupFileHash(helptext_info);
14210 helptext_info = NULL;
14213 if (fileExists(filename))
14214 helptext_info = loadSetupFileHash(filename);
14216 if (helptext_info == NULL)
14218 // use reliable default values from static configuration
14219 helptext_info = newSetupFileHash();
14221 for (i = 0; helptext_config[i].token; i++)
14222 setHashEntry(helptext_info,
14223 helptext_config[i].token,
14224 helptext_config[i].value);
14228 BEGIN_HASH_ITERATION(helptext_info, itr)
14230 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14231 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14233 END_HASH_ITERATION(hash, itr)
14238 // ----------------------------------------------------------------------------
14240 // ----------------------------------------------------------------------------
14242 #define MAX_NUM_CONVERT_LEVELS 1000
14244 void ConvertLevels(void)
14246 static LevelDirTree *convert_leveldir = NULL;
14247 static int convert_level_nr = -1;
14248 static int num_levels_handled = 0;
14249 static int num_levels_converted = 0;
14250 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14253 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14254 global.convert_leveldir);
14256 if (convert_leveldir == NULL)
14257 Fail("no such level identifier: '%s'", global.convert_leveldir);
14259 leveldir_current = convert_leveldir;
14261 if (global.convert_level_nr != -1)
14263 convert_leveldir->first_level = global.convert_level_nr;
14264 convert_leveldir->last_level = global.convert_level_nr;
14267 convert_level_nr = convert_leveldir->first_level;
14269 PrintLine("=", 79);
14270 Print("Converting levels\n");
14271 PrintLine("-", 79);
14272 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14273 Print("Level series name: '%s'\n", convert_leveldir->name);
14274 Print("Level series author: '%s'\n", convert_leveldir->author);
14275 Print("Number of levels: %d\n", convert_leveldir->levels);
14276 PrintLine("=", 79);
14279 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14280 levels_failed[i] = FALSE;
14282 while (convert_level_nr <= convert_leveldir->last_level)
14284 char *level_filename;
14287 level_nr = convert_level_nr++;
14289 Print("Level %03d: ", level_nr);
14291 LoadLevel(level_nr);
14292 if (level.no_level_file || level.no_valid_file)
14294 Print("(no level)\n");
14298 Print("converting level ... ");
14301 // special case: conversion of some EMC levels as requested by ACME
14302 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14305 level_filename = getDefaultLevelFilename(level_nr);
14306 new_level = !fileExists(level_filename);
14310 SaveLevel(level_nr);
14312 num_levels_converted++;
14314 Print("converted.\n");
14318 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14319 levels_failed[level_nr] = TRUE;
14321 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14324 num_levels_handled++;
14328 PrintLine("=", 79);
14329 Print("Number of levels handled: %d\n", num_levels_handled);
14330 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14331 (num_levels_handled ?
14332 num_levels_converted * 100 / num_levels_handled : 0));
14333 PrintLine("-", 79);
14334 Print("Summary (for automatic parsing by scripts):\n");
14335 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14336 convert_leveldir->identifier, num_levels_converted,
14337 num_levels_handled,
14338 (num_levels_handled ?
14339 num_levels_converted * 100 / num_levels_handled : 0));
14341 if (num_levels_handled != num_levels_converted)
14343 Print(", FAILED:");
14344 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14345 if (levels_failed[i])
14350 PrintLine("=", 79);
14352 CloseAllAndExit(0);
14356 // ----------------------------------------------------------------------------
14357 // create and save images for use in level sketches (raw BMP format)
14358 // ----------------------------------------------------------------------------
14360 void CreateLevelSketchImages(void)
14366 InitElementPropertiesGfxElement();
14368 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14369 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14371 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14373 int element = getMappedElement(i);
14374 char basename1[16];
14375 char basename2[16];
14379 sprintf(basename1, "%04d.bmp", i);
14380 sprintf(basename2, "%04ds.bmp", i);
14382 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14383 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14385 DrawSizedElement(0, 0, element, TILESIZE);
14386 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14388 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14389 Fail("cannot save level sketch image file '%s'", filename1);
14391 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14392 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14394 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14395 Fail("cannot save level sketch image file '%s'", filename2);
14400 // create corresponding SQL statements (for normal and small images)
14403 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14404 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14407 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14408 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14410 // optional: create content for forum level sketch demonstration post
14412 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14415 FreeBitmap(bitmap1);
14416 FreeBitmap(bitmap2);
14419 fprintf(stderr, "\n");
14421 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14423 CloseAllAndExit(0);
14427 // ----------------------------------------------------------------------------
14428 // create and save images for element collecting animations (raw BMP format)
14429 // ----------------------------------------------------------------------------
14431 static boolean createCollectImage(int element)
14433 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14436 void CreateCollectElementImages(void)
14440 int anim_frames = num_steps - 1;
14441 int tile_size = TILESIZE;
14442 int anim_width = tile_size * anim_frames;
14443 int anim_height = tile_size;
14444 int num_collect_images = 0;
14445 int pos_collect_images = 0;
14447 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14448 if (createCollectImage(i))
14449 num_collect_images++;
14451 Info("Creating %d element collecting animation images ...",
14452 num_collect_images);
14454 int dst_width = anim_width * 2;
14455 int dst_height = anim_height * num_collect_images / 2;
14456 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14457 char *basename_bmp = "RocksCollect.bmp";
14458 char *basename_png = "RocksCollect.png";
14459 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14460 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14461 int len_filename_bmp = strlen(filename_bmp);
14462 int len_filename_png = strlen(filename_png);
14463 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14464 char cmd_convert[max_command_len];
14466 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14470 // force using RGBA surface for destination bitmap
14471 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14472 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14474 dst_bitmap->surface =
14475 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14477 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14479 if (!createCollectImage(i))
14482 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14483 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14484 int graphic = el2img(i);
14485 char *token_name = element_info[i].token_name;
14486 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14487 Bitmap *src_bitmap;
14490 Info("- creating collecting image for '%s' ...", token_name);
14492 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14494 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14495 tile_size, tile_size, 0, 0);
14497 // force using RGBA surface for temporary bitmap (using transparent black)
14498 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14499 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14501 tmp_bitmap->surface =
14502 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14504 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14506 for (j = 0; j < anim_frames; j++)
14508 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14509 int frame_size = frame_size_final * num_steps;
14510 int offset = (tile_size - frame_size_final) / 2;
14511 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14513 while (frame_size > frame_size_final)
14517 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14519 FreeBitmap(frame_bitmap);
14521 frame_bitmap = half_bitmap;
14524 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14525 frame_size_final, frame_size_final,
14526 dst_x + j * tile_size + offset, dst_y + offset);
14528 FreeBitmap(frame_bitmap);
14531 tmp_bitmap->surface_masked = NULL;
14533 FreeBitmap(tmp_bitmap);
14535 pos_collect_images++;
14538 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14539 Fail("cannot save element collecting image file '%s'", filename_bmp);
14541 FreeBitmap(dst_bitmap);
14543 Info("Converting image file from BMP to PNG ...");
14545 if (system(cmd_convert) != 0)
14546 Fail("converting image file failed");
14548 unlink(filename_bmp);
14552 CloseAllAndExit(0);
14556 // ----------------------------------------------------------------------------
14557 // create and save images for custom and group elements (raw BMP format)
14558 // ----------------------------------------------------------------------------
14560 void CreateCustomElementImages(char *directory)
14562 char *src_basename = "RocksCE-template.ilbm";
14563 char *dst_basename = "RocksCE.bmp";
14564 char *src_filename = getPath2(directory, src_basename);
14565 char *dst_filename = getPath2(directory, dst_basename);
14566 Bitmap *src_bitmap;
14568 int yoffset_ce = 0;
14569 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14572 InitVideoDefaults();
14574 ReCreateBitmap(&backbuffer, video.width, video.height);
14576 src_bitmap = LoadImage(src_filename);
14578 bitmap = CreateBitmap(TILEX * 16 * 2,
14579 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14582 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14589 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14590 TILEX * x, TILEY * y + yoffset_ce);
14592 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14594 TILEX * x + TILEX * 16,
14595 TILEY * y + yoffset_ce);
14597 for (j = 2; j >= 0; j--)
14601 BlitBitmap(src_bitmap, bitmap,
14602 TILEX + c * 7, 0, 6, 10,
14603 TILEX * x + 6 + j * 7,
14604 TILEY * y + 11 + yoffset_ce);
14606 BlitBitmap(src_bitmap, bitmap,
14607 TILEX + c * 8, TILEY, 6, 10,
14608 TILEX * 16 + TILEX * x + 6 + j * 8,
14609 TILEY * y + 10 + yoffset_ce);
14615 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14622 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14623 TILEX * x, TILEY * y + yoffset_ge);
14625 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14627 TILEX * x + TILEX * 16,
14628 TILEY * y + yoffset_ge);
14630 for (j = 1; j >= 0; j--)
14634 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14635 TILEX * x + 6 + j * 10,
14636 TILEY * y + 11 + yoffset_ge);
14638 BlitBitmap(src_bitmap, bitmap,
14639 TILEX + c * 8, TILEY + 12, 6, 10,
14640 TILEX * 16 + TILEX * x + 10 + j * 8,
14641 TILEY * y + 10 + yoffset_ge);
14647 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14648 Fail("cannot save CE graphics file '%s'", dst_filename);
14650 FreeBitmap(bitmap);
14652 CloseAllAndExit(0);