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(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(level->bd_amoeba_content_too_big);
4052 cave->amoeba_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_content_enclosed);
4053 cave->amoeba_2_too_big_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_too_big);
4054 cave->amoeba_2_enclosed_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_enclosed);
4055 cave->amoeba_2_explosion_effect = map_element_RND_to_BD(level->bd_amoeba_2_content_exploding);
4056 cave->amoeba_2_looks_like = map_element_RND_to_BD(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(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(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(level->bd_biter_eats_element);
4072 cave->bladder_converts_by = map_element_RND_to_BD(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(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->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->amoeba_too_big_effect);
4161 level->bd_amoeba_content_enclosed = map_element_BD_to_RND(cave->amoeba_enclosed_effect);
4162 level->bd_amoeba_2_content_too_big = map_element_BD_to_RND(cave->amoeba_2_too_big_effect);
4163 level->bd_amoeba_2_content_enclosed = map_element_BD_to_RND(cave->amoeba_2_enclosed_effect);
4164 level->bd_amoeba_2_content_exploding = map_element_BD_to_RND(cave->amoeba_2_explosion_effect);
4165 level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND(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->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->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->biter_eat);
4181 level->bd_bladder_converts_by_element = map_element_BD_to_RND(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->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_em, "editor.cascade.el_em"
10541 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10545 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10549 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10553 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10557 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10561 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10565 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10569 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10573 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10577 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10581 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10585 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10589 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10593 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10597 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10601 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10605 static struct TokenInfo shortcut_setup_tokens[] =
10609 &setup.shortcut.save_game, "shortcut.save_game"
10613 &setup.shortcut.load_game, "shortcut.load_game"
10617 &setup.shortcut.restart_game, "shortcut.restart_game"
10621 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10625 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10629 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10633 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10637 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10641 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10645 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10649 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10653 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10657 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10661 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10665 &setup.shortcut.tape_record, "shortcut.tape_record"
10669 &setup.shortcut.tape_play, "shortcut.tape_play"
10673 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10677 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10681 &setup.shortcut.sound_music, "shortcut.sound_music"
10685 &setup.shortcut.snap_left, "shortcut.snap_left"
10689 &setup.shortcut.snap_right, "shortcut.snap_right"
10693 &setup.shortcut.snap_up, "shortcut.snap_up"
10697 &setup.shortcut.snap_down, "shortcut.snap_down"
10701 static struct SetupInputInfo setup_input;
10702 static struct TokenInfo player_setup_tokens[] =
10706 &setup_input.use_joystick, ".use_joystick"
10710 &setup_input.joy.device_name, ".joy.device_name"
10714 &setup_input.joy.xleft, ".joy.xleft"
10718 &setup_input.joy.xmiddle, ".joy.xmiddle"
10722 &setup_input.joy.xright, ".joy.xright"
10726 &setup_input.joy.yupper, ".joy.yupper"
10730 &setup_input.joy.ymiddle, ".joy.ymiddle"
10734 &setup_input.joy.ylower, ".joy.ylower"
10738 &setup_input.joy.snap, ".joy.snap_field"
10742 &setup_input.joy.drop, ".joy.place_bomb"
10746 &setup_input.key.left, ".key.move_left"
10750 &setup_input.key.right, ".key.move_right"
10754 &setup_input.key.up, ".key.move_up"
10758 &setup_input.key.down, ".key.move_down"
10762 &setup_input.key.snap, ".key.snap_field"
10766 &setup_input.key.drop, ".key.place_bomb"
10770 static struct TokenInfo system_setup_tokens[] =
10774 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10778 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10782 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10786 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10790 static struct TokenInfo internal_setup_tokens[] =
10794 &setup.internal.program_title, "program_title"
10798 &setup.internal.program_version, "program_version"
10802 &setup.internal.program_author, "program_author"
10806 &setup.internal.program_email, "program_email"
10810 &setup.internal.program_website, "program_website"
10814 &setup.internal.program_copyright, "program_copyright"
10818 &setup.internal.program_company, "program_company"
10822 &setup.internal.program_icon_file, "program_icon_file"
10826 &setup.internal.default_graphics_set, "default_graphics_set"
10830 &setup.internal.default_sounds_set, "default_sounds_set"
10834 &setup.internal.default_music_set, "default_music_set"
10838 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10842 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10846 &setup.internal.fallback_music_file, "fallback_music_file"
10850 &setup.internal.default_level_series, "default_level_series"
10854 &setup.internal.default_window_width, "default_window_width"
10858 &setup.internal.default_window_height, "default_window_height"
10862 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10866 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10870 &setup.internal.create_user_levelset, "create_user_levelset"
10874 &setup.internal.info_screens_from_main, "info_screens_from_main"
10878 &setup.internal.menu_game, "menu_game"
10882 &setup.internal.menu_engines, "menu_engines"
10886 &setup.internal.menu_editor, "menu_editor"
10890 &setup.internal.menu_graphics, "menu_graphics"
10894 &setup.internal.menu_sound, "menu_sound"
10898 &setup.internal.menu_artwork, "menu_artwork"
10902 &setup.internal.menu_input, "menu_input"
10906 &setup.internal.menu_touch, "menu_touch"
10910 &setup.internal.menu_shortcuts, "menu_shortcuts"
10914 &setup.internal.menu_exit, "menu_exit"
10918 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10922 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10926 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10930 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10934 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10938 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10942 &setup.internal.info_title, "info_title"
10946 &setup.internal.info_elements, "info_elements"
10950 &setup.internal.info_music, "info_music"
10954 &setup.internal.info_credits, "info_credits"
10958 &setup.internal.info_program, "info_program"
10962 &setup.internal.info_version, "info_version"
10966 &setup.internal.info_levelset, "info_levelset"
10970 &setup.internal.info_exit, "info_exit"
10974 static struct TokenInfo debug_setup_tokens[] =
10978 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10982 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10986 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10990 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10994 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10998 &setup.debug.frame_delay[5], "debug.frame_delay_5"
11002 &setup.debug.frame_delay[6], "debug.frame_delay_6"
11006 &setup.debug.frame_delay[7], "debug.frame_delay_7"
11010 &setup.debug.frame_delay[8], "debug.frame_delay_8"
11014 &setup.debug.frame_delay[9], "debug.frame_delay_9"
11018 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
11022 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
11026 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
11030 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
11034 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
11038 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
11042 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
11046 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
11050 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
11054 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
11058 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
11061 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
11065 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
11069 &setup.debug.xsn_mode, "debug.xsn_mode"
11073 &setup.debug.xsn_percent, "debug.xsn_percent"
11077 static struct TokenInfo options_setup_tokens[] =
11081 &setup.options.verbose, "options.verbose"
11085 &setup.options.debug, "options.debug"
11089 &setup.options.debug_mode, "options.debug_mode"
11093 static void setSetupInfoToDefaults(struct SetupInfo *si)
11097 si->player_name = getStringCopy(getDefaultUserName(user.nr));
11099 si->multiple_users = TRUE;
11102 si->sound_loops = TRUE;
11103 si->sound_music = TRUE;
11104 si->sound_simple = TRUE;
11106 si->global_animations = TRUE;
11107 si->scroll_delay = TRUE;
11108 si->forced_scroll_delay = FALSE;
11109 si->scroll_delay_value = STD_SCROLL_DELAY;
11110 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11111 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11112 si->fade_screens = TRUE;
11113 si->autorecord = TRUE;
11114 si->autorecord_after_replay = TRUE;
11115 si->auto_pause_on_start = FALSE;
11116 si->show_titlescreen = TRUE;
11117 si->quick_doors = FALSE;
11118 si->team_mode = FALSE;
11119 si->handicap = TRUE;
11120 si->skip_levels = TRUE;
11121 si->increment_levels = TRUE;
11122 si->auto_play_next_level = TRUE;
11123 si->count_score_after_game = TRUE;
11124 si->show_scores_after_game = TRUE;
11125 si->time_limit = TRUE;
11126 si->fullscreen = FALSE;
11127 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11128 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11129 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11130 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11131 si->ask_on_escape = TRUE;
11132 si->ask_on_escape_editor = TRUE;
11133 si->ask_on_game_over = TRUE;
11134 si->ask_on_quit_game = TRUE;
11135 si->ask_on_quit_program = TRUE;
11136 si->quick_switch = FALSE;
11137 si->input_on_focus = FALSE;
11138 si->prefer_aga_graphics = TRUE;
11139 si->prefer_lowpass_sounds = FALSE;
11140 si->prefer_extra_panel_items = TRUE;
11141 si->game_speed_extended = FALSE;
11142 si->game_frame_delay = GAME_FRAME_DELAY;
11143 si->bd_skip_uncovering = FALSE;
11144 si->bd_skip_hatching = FALSE;
11145 si->bd_scroll_delay = TRUE;
11146 si->bd_smooth_movements = AUTO;
11147 si->sp_show_border_elements = FALSE;
11148 si->small_game_graphics = FALSE;
11149 si->show_load_save_buttons = FALSE;
11150 si->show_undo_redo_buttons = FALSE;
11151 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11153 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11154 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11155 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11157 si->override_level_graphics = FALSE;
11158 si->override_level_sounds = FALSE;
11159 si->override_level_music = FALSE;
11161 si->volume_simple = 100; // percent
11162 si->volume_loops = 100; // percent
11163 si->volume_music = 100; // percent
11165 si->network_mode = FALSE;
11166 si->network_player_nr = 0; // first player
11167 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11169 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11170 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
11171 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
11172 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
11173 si->touch.draw_outlined = TRUE;
11174 si->touch.draw_pressed = TRUE;
11176 for (i = 0; i < 2; i++)
11178 char *default_grid_button[6][2] =
11184 { "111222", " vv " },
11185 { "111222", " vv " }
11187 int grid_xsize = DEFAULT_GRID_XSIZE(i);
11188 int grid_ysize = DEFAULT_GRID_YSIZE(i);
11189 int min_xsize = MIN(6, grid_xsize);
11190 int min_ysize = MIN(6, grid_ysize);
11191 int startx = grid_xsize - min_xsize;
11192 int starty = grid_ysize - min_ysize;
11195 // virtual buttons grid can only be set to defaults if video is initialized
11196 // (this will be repeated if virtual buttons are not loaded from setup file)
11197 if (video.initialized)
11199 si->touch.grid_xsize[i] = grid_xsize;
11200 si->touch.grid_ysize[i] = grid_ysize;
11204 si->touch.grid_xsize[i] = -1;
11205 si->touch.grid_ysize[i] = -1;
11208 for (x = 0; x < MAX_GRID_XSIZE; x++)
11209 for (y = 0; y < MAX_GRID_YSIZE; y++)
11210 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11212 for (x = 0; x < min_xsize; x++)
11213 for (y = 0; y < min_ysize; y++)
11214 si->touch.grid_button[i][x][starty + y] =
11215 default_grid_button[y][0][x];
11217 for (x = 0; x < min_xsize; x++)
11218 for (y = 0; y < min_ysize; y++)
11219 si->touch.grid_button[i][startx + x][starty + y] =
11220 default_grid_button[y][1][x];
11223 si->touch.grid_initialized = video.initialized;
11225 si->touch.overlay_buttons = FALSE;
11227 si->editor.el_boulderdash = TRUE;
11228 si->editor.el_boulderdash_native = TRUE;
11229 si->editor.el_emerald_mine = TRUE;
11230 si->editor.el_emerald_mine_club = TRUE;
11231 si->editor.el_more = TRUE;
11232 si->editor.el_sokoban = TRUE;
11233 si->editor.el_supaplex = TRUE;
11234 si->editor.el_diamond_caves = TRUE;
11235 si->editor.el_dx_boulderdash = TRUE;
11237 si->editor.el_mirror_magic = TRUE;
11238 si->editor.el_deflektor = TRUE;
11240 si->editor.el_chars = TRUE;
11241 si->editor.el_steel_chars = TRUE;
11243 si->editor.el_classic = TRUE;
11244 si->editor.el_custom = TRUE;
11246 si->editor.el_user_defined = FALSE;
11247 si->editor.el_dynamic = TRUE;
11249 si->editor.el_headlines = TRUE;
11251 si->editor.show_element_token = FALSE;
11253 si->editor.show_read_only_warning = TRUE;
11255 si->editor.use_template_for_new_levels = TRUE;
11257 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
11258 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
11259 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
11260 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11261 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
11263 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
11264 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
11265 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
11266 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
11267 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11269 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
11270 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
11271 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
11272 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
11273 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
11274 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
11276 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
11277 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
11278 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
11280 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
11281 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
11282 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
11283 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
11285 for (i = 0; i < MAX_PLAYERS; i++)
11287 si->input[i].use_joystick = FALSE;
11288 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11289 si->input[i].joy.xleft = JOYSTICK_XLEFT;
11290 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11291 si->input[i].joy.xright = JOYSTICK_XRIGHT;
11292 si->input[i].joy.yupper = JOYSTICK_YUPPER;
11293 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11294 si->input[i].joy.ylower = JOYSTICK_YLOWER;
11295 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
11296 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
11297 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
11298 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11299 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
11300 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
11301 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
11302 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
11305 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11306 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11307 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11308 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11310 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
11311 si->internal.program_version = getStringCopy(getProgramRealVersionString());
11312 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
11313 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
11314 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
11315 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11316 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
11318 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11320 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11321 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
11322 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
11324 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11325 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
11326 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
11328 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11329 si->internal.choose_from_top_leveldir = FALSE;
11330 si->internal.show_scaling_in_title = TRUE;
11331 si->internal.create_user_levelset = TRUE;
11332 si->internal.info_screens_from_main = FALSE;
11334 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
11335 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11337 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11338 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11339 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11340 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11341 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11342 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11343 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11344 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11345 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11346 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11348 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11349 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11350 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11351 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11352 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11353 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11354 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11355 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11356 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11357 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11359 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11360 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11362 si->debug.show_frames_per_second = FALSE;
11364 si->debug.xsn_mode = AUTO;
11365 si->debug.xsn_percent = 0;
11367 si->options.verbose = FALSE;
11368 si->options.debug = FALSE;
11369 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11371 #if defined(PLATFORM_ANDROID)
11372 si->fullscreen = TRUE;
11373 si->touch.overlay_buttons = TRUE;
11376 setHideSetupEntry(&setup.debug.xsn_mode);
11379 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11381 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11384 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11386 si->player_uuid = NULL; // (will be set later)
11387 si->player_version = 1; // (will be set later)
11389 si->use_api_server = TRUE;
11390 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11391 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11392 si->ask_for_uploading_tapes = TRUE;
11393 si->ask_for_remaining_tapes = FALSE;
11394 si->provide_uploading_tapes = TRUE;
11395 si->ask_for_using_api_server = TRUE;
11396 si->has_remaining_tapes = FALSE;
11399 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11401 si->editor_cascade.el_bd = TRUE;
11402 si->editor_cascade.el_bd_native = TRUE;
11403 si->editor_cascade.el_em = TRUE;
11404 si->editor_cascade.el_emc = TRUE;
11405 si->editor_cascade.el_rnd = TRUE;
11406 si->editor_cascade.el_sb = TRUE;
11407 si->editor_cascade.el_sp = TRUE;
11408 si->editor_cascade.el_dc = TRUE;
11409 si->editor_cascade.el_dx = TRUE;
11411 si->editor_cascade.el_mm = TRUE;
11412 si->editor_cascade.el_df = TRUE;
11414 si->editor_cascade.el_chars = FALSE;
11415 si->editor_cascade.el_steel_chars = FALSE;
11416 si->editor_cascade.el_ce = FALSE;
11417 si->editor_cascade.el_ge = FALSE;
11418 si->editor_cascade.el_es = FALSE;
11419 si->editor_cascade.el_ref = FALSE;
11420 si->editor_cascade.el_user = FALSE;
11421 si->editor_cascade.el_dynamic = FALSE;
11424 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11426 static char *getHideSetupToken(void *setup_value)
11428 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11430 if (setup_value != NULL)
11431 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11433 return hide_setup_token;
11436 void setHideSetupEntry(void *setup_value)
11438 char *hide_setup_token = getHideSetupToken(setup_value);
11440 if (hide_setup_hash == NULL)
11441 hide_setup_hash = newSetupFileHash();
11443 if (setup_value != NULL)
11444 setHashEntry(hide_setup_hash, hide_setup_token, "");
11447 void removeHideSetupEntry(void *setup_value)
11449 char *hide_setup_token = getHideSetupToken(setup_value);
11451 if (setup_value != NULL)
11452 removeHashEntry(hide_setup_hash, hide_setup_token);
11455 boolean hideSetupEntry(void *setup_value)
11457 char *hide_setup_token = getHideSetupToken(setup_value);
11459 return (setup_value != NULL &&
11460 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11463 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11464 struct TokenInfo *token_info,
11465 int token_nr, char *token_text)
11467 char *token_hide_text = getStringCat2(token_text, ".hide");
11468 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11470 // set the value of this setup option in the setup option structure
11471 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11473 // check if this setup option should be hidden in the setup menu
11474 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11475 setHideSetupEntry(token_info[token_nr].value);
11477 free(token_hide_text);
11480 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11481 struct TokenInfo *token_info,
11484 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11485 token_info[token_nr].text);
11488 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11492 if (!setup_file_hash)
11495 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11496 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11498 setup.touch.grid_initialized = TRUE;
11499 for (i = 0; i < 2; i++)
11501 int grid_xsize = setup.touch.grid_xsize[i];
11502 int grid_ysize = setup.touch.grid_ysize[i];
11505 // if virtual buttons are not loaded from setup file, repeat initializing
11506 // virtual buttons grid with default values later when video is initialized
11507 if (grid_xsize == -1 ||
11510 setup.touch.grid_initialized = FALSE;
11515 for (y = 0; y < grid_ysize; y++)
11517 char token_string[MAX_LINE_LEN];
11519 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11521 char *value_string = getHashEntry(setup_file_hash, token_string);
11523 if (value_string == NULL)
11526 for (x = 0; x < grid_xsize; x++)
11528 char c = value_string[x];
11530 setup.touch.grid_button[i][x][y] =
11531 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11536 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11537 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11539 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11540 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11542 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11546 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11548 setup_input = setup.input[pnr];
11549 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11551 char full_token[100];
11553 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11554 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11557 setup.input[pnr] = setup_input;
11560 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11561 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11563 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11564 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11566 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11567 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11569 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11570 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11572 setHideRelatedSetupEntries();
11575 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11579 if (!setup_file_hash)
11582 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11583 setSetupInfo(auto_setup_tokens, i,
11584 getHashEntry(setup_file_hash,
11585 auto_setup_tokens[i].text));
11588 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11592 if (!setup_file_hash)
11595 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11596 setSetupInfo(server_setup_tokens, i,
11597 getHashEntry(setup_file_hash,
11598 server_setup_tokens[i].text));
11601 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11605 if (!setup_file_hash)
11608 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11609 setSetupInfo(editor_cascade_setup_tokens, i,
11610 getHashEntry(setup_file_hash,
11611 editor_cascade_setup_tokens[i].text));
11614 void LoadUserNames(void)
11616 int last_user_nr = user.nr;
11619 if (global.user_names != NULL)
11621 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11622 checked_free(global.user_names[i]);
11624 checked_free(global.user_names);
11627 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11629 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11633 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11635 if (setup_file_hash)
11637 char *player_name = getHashEntry(setup_file_hash, "player_name");
11639 global.user_names[i] = getFixedUserName(player_name);
11641 freeSetupFileHash(setup_file_hash);
11644 if (global.user_names[i] == NULL)
11645 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11648 user.nr = last_user_nr;
11651 void LoadSetupFromFilename(char *filename)
11653 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11655 if (setup_file_hash)
11657 decodeSetupFileHash_Default(setup_file_hash);
11659 freeSetupFileHash(setup_file_hash);
11663 Debug("setup", "using default setup values");
11667 static void LoadSetup_SpecialPostProcessing(void)
11669 char *player_name_new;
11671 // needed to work around problems with fixed length strings
11672 player_name_new = getFixedUserName(setup.player_name);
11673 free(setup.player_name);
11674 setup.player_name = player_name_new;
11676 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11677 if (setup.scroll_delay == FALSE)
11679 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11680 setup.scroll_delay = TRUE; // now always "on"
11683 // make sure that scroll delay value stays inside valid range
11684 setup.scroll_delay_value =
11685 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11688 void LoadSetup_Default(void)
11692 // always start with reliable default values
11693 setSetupInfoToDefaults(&setup);
11695 // try to load setup values from default setup file
11696 filename = getDefaultSetupFilename();
11698 if (fileExists(filename))
11699 LoadSetupFromFilename(filename);
11701 // try to load setup values from platform setup file
11702 filename = getPlatformSetupFilename();
11704 if (fileExists(filename))
11705 LoadSetupFromFilename(filename);
11707 // try to load setup values from user setup file
11708 filename = getSetupFilename();
11710 LoadSetupFromFilename(filename);
11712 LoadSetup_SpecialPostProcessing();
11715 void LoadSetup_AutoSetup(void)
11717 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11718 SetupFileHash *setup_file_hash = NULL;
11720 // always start with reliable default values
11721 setSetupInfoToDefaults_AutoSetup(&setup);
11723 setup_file_hash = loadSetupFileHash(filename);
11725 if (setup_file_hash)
11727 decodeSetupFileHash_AutoSetup(setup_file_hash);
11729 freeSetupFileHash(setup_file_hash);
11735 void LoadSetup_ServerSetup(void)
11737 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11738 SetupFileHash *setup_file_hash = NULL;
11740 // always start with reliable default values
11741 setSetupInfoToDefaults_ServerSetup(&setup);
11743 setup_file_hash = loadSetupFileHash(filename);
11745 if (setup_file_hash)
11747 decodeSetupFileHash_ServerSetup(setup_file_hash);
11749 freeSetupFileHash(setup_file_hash);
11754 if (setup.player_uuid == NULL)
11756 // player UUID does not yet exist in setup file
11757 setup.player_uuid = getStringCopy(getUUID());
11758 setup.player_version = 2;
11760 SaveSetup_ServerSetup();
11764 void LoadSetup_EditorCascade(void)
11766 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11767 SetupFileHash *setup_file_hash = NULL;
11769 // always start with reliable default values
11770 setSetupInfoToDefaults_EditorCascade(&setup);
11772 setup_file_hash = loadSetupFileHash(filename);
11774 if (setup_file_hash)
11776 decodeSetupFileHash_EditorCascade(setup_file_hash);
11778 freeSetupFileHash(setup_file_hash);
11784 void LoadSetup(void)
11786 LoadSetup_Default();
11787 LoadSetup_AutoSetup();
11788 LoadSetup_ServerSetup();
11789 LoadSetup_EditorCascade();
11792 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11793 char *mapping_line)
11795 char mapping_guid[MAX_LINE_LEN];
11796 char *mapping_start, *mapping_end;
11798 // get GUID from game controller mapping line: copy complete line
11799 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11800 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11802 // get GUID from game controller mapping line: cut after GUID part
11803 mapping_start = strchr(mapping_guid, ',');
11804 if (mapping_start != NULL)
11805 *mapping_start = '\0';
11807 // cut newline from game controller mapping line
11808 mapping_end = strchr(mapping_line, '\n');
11809 if (mapping_end != NULL)
11810 *mapping_end = '\0';
11812 // add mapping entry to game controller mappings hash
11813 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11816 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11821 if (!(file = fopen(filename, MODE_READ)))
11823 Warn("cannot read game controller mappings file '%s'", filename);
11828 while (!feof(file))
11830 char line[MAX_LINE_LEN];
11832 if (!fgets(line, MAX_LINE_LEN, file))
11835 addGameControllerMappingToHash(mappings_hash, line);
11841 void SaveSetup_Default(void)
11843 char *filename = getSetupFilename();
11847 InitUserDataDirectory();
11849 if (!(file = fopen(filename, MODE_WRITE)))
11851 Warn("cannot write setup file '%s'", filename);
11856 fprintFileHeader(file, SETUP_FILENAME);
11858 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11860 // just to make things nicer :)
11861 if (global_setup_tokens[i].value == &setup.multiple_users ||
11862 global_setup_tokens[i].value == &setup.sound ||
11863 global_setup_tokens[i].value == &setup.graphics_set ||
11864 global_setup_tokens[i].value == &setup.volume_simple ||
11865 global_setup_tokens[i].value == &setup.network_mode ||
11866 global_setup_tokens[i].value == &setup.touch.control_type ||
11867 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11868 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11869 fprintf(file, "\n");
11871 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11874 for (i = 0; i < 2; i++)
11876 int grid_xsize = setup.touch.grid_xsize[i];
11877 int grid_ysize = setup.touch.grid_ysize[i];
11880 fprintf(file, "\n");
11882 for (y = 0; y < grid_ysize; y++)
11884 char token_string[MAX_LINE_LEN];
11885 char value_string[MAX_LINE_LEN];
11887 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11889 for (x = 0; x < grid_xsize; x++)
11891 char c = setup.touch.grid_button[i][x][y];
11893 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11896 value_string[grid_xsize] = '\0';
11898 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11902 fprintf(file, "\n");
11903 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11904 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11906 fprintf(file, "\n");
11907 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11908 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11910 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11914 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11915 fprintf(file, "\n");
11917 setup_input = setup.input[pnr];
11918 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11919 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11922 fprintf(file, "\n");
11923 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11924 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11926 // (internal setup values not saved to user setup file)
11928 fprintf(file, "\n");
11929 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11930 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11931 setup.debug.xsn_mode != AUTO)
11932 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11934 fprintf(file, "\n");
11935 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11936 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11940 SetFilePermissions(filename, PERMS_PRIVATE);
11943 void SaveSetup_AutoSetup(void)
11945 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11949 InitUserDataDirectory();
11951 if (!(file = fopen(filename, MODE_WRITE)))
11953 Warn("cannot write auto setup file '%s'", filename);
11960 fprintFileHeader(file, AUTOSETUP_FILENAME);
11962 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11963 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11967 SetFilePermissions(filename, PERMS_PRIVATE);
11972 void SaveSetup_ServerSetup(void)
11974 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11978 InitUserDataDirectory();
11980 if (!(file = fopen(filename, MODE_WRITE)))
11982 Warn("cannot write server setup file '%s'", filename);
11989 fprintFileHeader(file, SERVERSETUP_FILENAME);
11991 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11993 // just to make things nicer :)
11994 if (server_setup_tokens[i].value == &setup.use_api_server)
11995 fprintf(file, "\n");
11997 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12002 SetFilePermissions(filename, PERMS_PRIVATE);
12007 void SaveSetup_EditorCascade(void)
12009 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12013 InitUserDataDirectory();
12015 if (!(file = fopen(filename, MODE_WRITE)))
12017 Warn("cannot write editor cascade state file '%s'", filename);
12024 fprintFileHeader(file, EDITORCASCADE_FILENAME);
12026 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12027 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12031 SetFilePermissions(filename, PERMS_PRIVATE);
12036 void SaveSetup(void)
12038 SaveSetup_Default();
12039 SaveSetup_AutoSetup();
12040 SaveSetup_ServerSetup();
12041 SaveSetup_EditorCascade();
12044 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12049 if (!(file = fopen(filename, MODE_WRITE)))
12051 Warn("cannot write game controller mappings file '%s'", filename);
12056 BEGIN_HASH_ITERATION(mappings_hash, itr)
12058 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12060 END_HASH_ITERATION(mappings_hash, itr)
12065 void SaveSetup_AddGameControllerMapping(char *mapping)
12067 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12068 SetupFileHash *mappings_hash = newSetupFileHash();
12070 InitUserDataDirectory();
12072 // load existing personal game controller mappings
12073 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12075 // add new mapping to personal game controller mappings
12076 addGameControllerMappingToHash(mappings_hash, mapping);
12078 // save updated personal game controller mappings
12079 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12081 freeSetupFileHash(mappings_hash);
12085 void LoadCustomElementDescriptions(void)
12087 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12088 SetupFileHash *setup_file_hash;
12091 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12093 if (element_info[i].custom_description != NULL)
12095 free(element_info[i].custom_description);
12096 element_info[i].custom_description = NULL;
12100 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12103 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12105 char *token = getStringCat2(element_info[i].token_name, ".name");
12106 char *value = getHashEntry(setup_file_hash, token);
12109 element_info[i].custom_description = getStringCopy(value);
12114 freeSetupFileHash(setup_file_hash);
12117 static int getElementFromToken(char *token)
12119 char *value = getHashEntry(element_token_hash, token);
12122 return atoi(value);
12124 Warn("unknown element token '%s'", token);
12126 return EL_UNDEFINED;
12129 void FreeGlobalAnimEventInfo(void)
12131 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12133 if (gaei->event_list == NULL)
12138 for (i = 0; i < gaei->num_event_lists; i++)
12140 checked_free(gaei->event_list[i]->event_value);
12141 checked_free(gaei->event_list[i]);
12144 checked_free(gaei->event_list);
12146 gaei->event_list = NULL;
12147 gaei->num_event_lists = 0;
12150 static int AddGlobalAnimEventList(void)
12152 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12153 int list_pos = gaei->num_event_lists++;
12155 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12156 sizeof(struct GlobalAnimEventListInfo *));
12158 gaei->event_list[list_pos] =
12159 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12161 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12163 gaeli->event_value = NULL;
12164 gaeli->num_event_values = 0;
12169 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12171 // do not add empty global animation events
12172 if (event_value == ANIM_EVENT_NONE)
12175 // if list position is undefined, create new list
12176 if (list_pos == ANIM_EVENT_UNDEFINED)
12177 list_pos = AddGlobalAnimEventList();
12179 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12180 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12181 int value_pos = gaeli->num_event_values++;
12183 gaeli->event_value = checked_realloc(gaeli->event_value,
12184 gaeli->num_event_values * sizeof(int *));
12186 gaeli->event_value[value_pos] = event_value;
12191 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12193 if (list_pos == ANIM_EVENT_UNDEFINED)
12194 return ANIM_EVENT_NONE;
12196 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12197 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12199 return gaeli->event_value[value_pos];
12202 int GetGlobalAnimEventValueCount(int list_pos)
12204 if (list_pos == ANIM_EVENT_UNDEFINED)
12207 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12208 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12210 return gaeli->num_event_values;
12213 // This function checks if a string <s> of the format "string1, string2, ..."
12214 // exactly contains a string <s_contained>.
12216 static boolean string_has_parameter(char *s, char *s_contained)
12220 if (s == NULL || s_contained == NULL)
12223 if (strlen(s_contained) > strlen(s))
12226 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12228 char next_char = s[strlen(s_contained)];
12230 // check if next character is delimiter or whitespace
12231 if (next_char == ',' || next_char == '\0' ||
12232 next_char == ' ' || next_char == '\t')
12236 // check if string contains another parameter string after a comma
12237 substring = strchr(s, ',');
12238 if (substring == NULL) // string does not contain a comma
12241 // advance string pointer to next character after the comma
12244 // skip potential whitespaces after the comma
12245 while (*substring == ' ' || *substring == '\t')
12248 return string_has_parameter(substring, s_contained);
12251 static int get_anim_parameter_value_ce(char *s)
12254 char *pattern_1 = "ce_change:custom_";
12255 char *pattern_2 = ".page_";
12256 int pattern_1_len = strlen(pattern_1);
12257 char *matching_char = strstr(s_ptr, pattern_1);
12258 int result = ANIM_EVENT_NONE;
12260 if (matching_char == NULL)
12261 return ANIM_EVENT_NONE;
12263 result = ANIM_EVENT_CE_CHANGE;
12265 s_ptr = matching_char + pattern_1_len;
12267 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12268 if (*s_ptr >= '0' && *s_ptr <= '9')
12270 int gic_ce_nr = (*s_ptr++ - '0');
12272 if (*s_ptr >= '0' && *s_ptr <= '9')
12274 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12276 if (*s_ptr >= '0' && *s_ptr <= '9')
12277 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12280 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12281 return ANIM_EVENT_NONE;
12283 // custom element stored as 0 to 255
12286 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12290 // invalid custom element number specified
12292 return ANIM_EVENT_NONE;
12295 // check for change page number ("page_X" or "page_XX") (optional)
12296 if (strPrefix(s_ptr, pattern_2))
12298 s_ptr += strlen(pattern_2);
12300 if (*s_ptr >= '0' && *s_ptr <= '9')
12302 int gic_page_nr = (*s_ptr++ - '0');
12304 if (*s_ptr >= '0' && *s_ptr <= '9')
12305 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12307 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12308 return ANIM_EVENT_NONE;
12310 // change page stored as 1 to 32 (0 means "all change pages")
12312 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12316 // invalid animation part number specified
12318 return ANIM_EVENT_NONE;
12322 // discard result if next character is neither delimiter nor whitespace
12323 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12324 *s_ptr == ' ' || *s_ptr == '\t'))
12325 return ANIM_EVENT_NONE;
12330 static int get_anim_parameter_value(char *s)
12332 int event_value[] =
12340 char *pattern_1[] =
12348 char *pattern_2 = ".part_";
12349 char *matching_char = NULL;
12351 int pattern_1_len = 0;
12352 int result = ANIM_EVENT_NONE;
12355 result = get_anim_parameter_value_ce(s);
12357 if (result != ANIM_EVENT_NONE)
12360 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12362 matching_char = strstr(s_ptr, pattern_1[i]);
12363 pattern_1_len = strlen(pattern_1[i]);
12364 result = event_value[i];
12366 if (matching_char != NULL)
12370 if (matching_char == NULL)
12371 return ANIM_EVENT_NONE;
12373 s_ptr = matching_char + pattern_1_len;
12375 // check for main animation number ("anim_X" or "anim_XX")
12376 if (*s_ptr >= '0' && *s_ptr <= '9')
12378 int gic_anim_nr = (*s_ptr++ - '0');
12380 if (*s_ptr >= '0' && *s_ptr <= '9')
12381 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12383 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12384 return ANIM_EVENT_NONE;
12386 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12390 // invalid main animation number specified
12392 return ANIM_EVENT_NONE;
12395 // check for animation part number ("part_X" or "part_XX") (optional)
12396 if (strPrefix(s_ptr, pattern_2))
12398 s_ptr += strlen(pattern_2);
12400 if (*s_ptr >= '0' && *s_ptr <= '9')
12402 int gic_part_nr = (*s_ptr++ - '0');
12404 if (*s_ptr >= '0' && *s_ptr <= '9')
12405 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12407 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12408 return ANIM_EVENT_NONE;
12410 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12414 // invalid animation part number specified
12416 return ANIM_EVENT_NONE;
12420 // discard result if next character is neither delimiter nor whitespace
12421 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12422 *s_ptr == ' ' || *s_ptr == '\t'))
12423 return ANIM_EVENT_NONE;
12428 static int get_anim_parameter_values(char *s)
12430 int list_pos = ANIM_EVENT_UNDEFINED;
12431 int event_value = ANIM_EVENT_DEFAULT;
12433 if (string_has_parameter(s, "any"))
12434 event_value |= ANIM_EVENT_ANY;
12436 if (string_has_parameter(s, "click:self") ||
12437 string_has_parameter(s, "click") ||
12438 string_has_parameter(s, "self"))
12439 event_value |= ANIM_EVENT_SELF;
12441 if (string_has_parameter(s, "unclick:any"))
12442 event_value |= ANIM_EVENT_UNCLICK_ANY;
12444 // if animation event found, add it to global animation event list
12445 if (event_value != ANIM_EVENT_NONE)
12446 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12450 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12451 event_value = get_anim_parameter_value(s);
12453 // if animation event found, add it to global animation event list
12454 if (event_value != ANIM_EVENT_NONE)
12455 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12457 // continue with next part of the string, starting with next comma
12458 s = strchr(s + 1, ',');
12464 static int get_anim_action_parameter_value(char *token)
12466 // check most common default case first to massively speed things up
12467 if (strEqual(token, ARG_UNDEFINED))
12468 return ANIM_EVENT_ACTION_NONE;
12470 int result = getImageIDFromToken(token);
12474 char *gfx_token = getStringCat2("gfx.", token);
12476 result = getImageIDFromToken(gfx_token);
12478 checked_free(gfx_token);
12483 Key key = getKeyFromX11KeyName(token);
12485 if (key != KSYM_UNDEFINED)
12486 result = -(int)key;
12493 result = get_hash_from_string(token); // unsigned int => int
12494 result = ABS(result); // may be negative now
12495 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12497 setHashEntry(anim_url_hash, int2str(result, 0), token);
12502 result = ANIM_EVENT_ACTION_NONE;
12507 int get_parameter_value(char *value_raw, char *suffix, int type)
12509 char *value = getStringToLower(value_raw);
12510 int result = 0; // probably a save default value
12512 if (strEqual(suffix, ".direction"))
12514 result = (strEqual(value, "left") ? MV_LEFT :
12515 strEqual(value, "right") ? MV_RIGHT :
12516 strEqual(value, "up") ? MV_UP :
12517 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12519 else if (strEqual(suffix, ".position"))
12521 result = (strEqual(value, "left") ? POS_LEFT :
12522 strEqual(value, "right") ? POS_RIGHT :
12523 strEqual(value, "top") ? POS_TOP :
12524 strEqual(value, "upper") ? POS_UPPER :
12525 strEqual(value, "middle") ? POS_MIDDLE :
12526 strEqual(value, "lower") ? POS_LOWER :
12527 strEqual(value, "bottom") ? POS_BOTTOM :
12528 strEqual(value, "any") ? POS_ANY :
12529 strEqual(value, "ce") ? POS_CE :
12530 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12531 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12533 else if (strEqual(suffix, ".align"))
12535 result = (strEqual(value, "left") ? ALIGN_LEFT :
12536 strEqual(value, "right") ? ALIGN_RIGHT :
12537 strEqual(value, "center") ? ALIGN_CENTER :
12538 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12540 else if (strEqual(suffix, ".valign"))
12542 result = (strEqual(value, "top") ? VALIGN_TOP :
12543 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12544 strEqual(value, "middle") ? VALIGN_MIDDLE :
12545 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12547 else if (strEqual(suffix, ".anim_mode"))
12549 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12550 string_has_parameter(value, "loop") ? ANIM_LOOP :
12551 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12552 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12553 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12554 string_has_parameter(value, "random") ? ANIM_RANDOM :
12555 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12556 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12557 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12558 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12559 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12560 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12561 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12562 string_has_parameter(value, "all") ? ANIM_ALL :
12563 string_has_parameter(value, "tiled") ? ANIM_TILED :
12564 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12567 if (string_has_parameter(value, "once"))
12568 result |= ANIM_ONCE;
12570 if (string_has_parameter(value, "reverse"))
12571 result |= ANIM_REVERSE;
12573 if (string_has_parameter(value, "opaque_player"))
12574 result |= ANIM_OPAQUE_PLAYER;
12576 if (string_has_parameter(value, "static_panel"))
12577 result |= ANIM_STATIC_PANEL;
12579 else if (strEqual(suffix, ".init_event") ||
12580 strEqual(suffix, ".anim_event"))
12582 result = get_anim_parameter_values(value);
12584 else if (strEqual(suffix, ".init_delay_action") ||
12585 strEqual(suffix, ".anim_delay_action") ||
12586 strEqual(suffix, ".post_delay_action") ||
12587 strEqual(suffix, ".init_event_action") ||
12588 strEqual(suffix, ".anim_event_action"))
12590 result = get_anim_action_parameter_value(value_raw);
12592 else if (strEqual(suffix, ".class"))
12594 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12595 get_hash_from_string(value));
12597 else if (strEqual(suffix, ".style"))
12599 result = STYLE_DEFAULT;
12601 if (string_has_parameter(value, "accurate_borders"))
12602 result |= STYLE_ACCURATE_BORDERS;
12604 if (string_has_parameter(value, "inner_corners"))
12605 result |= STYLE_INNER_CORNERS;
12607 if (string_has_parameter(value, "reverse"))
12608 result |= STYLE_REVERSE;
12610 if (string_has_parameter(value, "leftmost_position"))
12611 result |= STYLE_LEFTMOST_POSITION;
12613 if (string_has_parameter(value, "block_clicks"))
12614 result |= STYLE_BLOCK;
12616 if (string_has_parameter(value, "passthrough_clicks"))
12617 result |= STYLE_PASSTHROUGH;
12619 if (string_has_parameter(value, "multiple_actions"))
12620 result |= STYLE_MULTIPLE_ACTIONS;
12622 if (string_has_parameter(value, "consume_ce_event"))
12623 result |= STYLE_CONSUME_CE_EVENT;
12625 else if (strEqual(suffix, ".fade_mode"))
12627 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12628 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12629 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12630 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12631 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12632 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12633 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12634 FADE_MODE_DEFAULT);
12636 else if (strEqual(suffix, ".auto_delay_unit"))
12638 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12639 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12640 AUTO_DELAY_UNIT_DEFAULT);
12642 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12644 result = gfx.get_font_from_token_function(value);
12646 else // generic parameter of type integer or boolean
12648 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12649 type == TYPE_INTEGER ? get_integer_from_string(value) :
12650 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12651 ARG_UNDEFINED_VALUE);
12659 static int get_token_parameter_value(char *token, char *value_raw)
12663 if (token == NULL || value_raw == NULL)
12664 return ARG_UNDEFINED_VALUE;
12666 suffix = strrchr(token, '.');
12667 if (suffix == NULL)
12670 if (strEqual(suffix, ".element"))
12671 return getElementFromToken(value_raw);
12673 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12674 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12677 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12678 boolean ignore_defaults)
12682 for (i = 0; image_config_vars[i].token != NULL; i++)
12684 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12686 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12687 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12691 *image_config_vars[i].value =
12692 get_token_parameter_value(image_config_vars[i].token, value);
12696 void InitMenuDesignSettings_Static(void)
12698 // always start with reliable default values from static default config
12699 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12702 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12706 // the following initializes hierarchical values from static configuration
12708 // special case: initialize "ARG_DEFAULT" values in static default config
12709 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12710 titlescreen_initial_first_default.fade_mode =
12711 title_initial_first_default.fade_mode;
12712 titlescreen_initial_first_default.fade_delay =
12713 title_initial_first_default.fade_delay;
12714 titlescreen_initial_first_default.post_delay =
12715 title_initial_first_default.post_delay;
12716 titlescreen_initial_first_default.auto_delay =
12717 title_initial_first_default.auto_delay;
12718 titlescreen_initial_first_default.auto_delay_unit =
12719 title_initial_first_default.auto_delay_unit;
12720 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12721 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12722 titlescreen_first_default.post_delay = title_first_default.post_delay;
12723 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12724 titlescreen_first_default.auto_delay_unit =
12725 title_first_default.auto_delay_unit;
12726 titlemessage_initial_first_default.fade_mode =
12727 title_initial_first_default.fade_mode;
12728 titlemessage_initial_first_default.fade_delay =
12729 title_initial_first_default.fade_delay;
12730 titlemessage_initial_first_default.post_delay =
12731 title_initial_first_default.post_delay;
12732 titlemessage_initial_first_default.auto_delay =
12733 title_initial_first_default.auto_delay;
12734 titlemessage_initial_first_default.auto_delay_unit =
12735 title_initial_first_default.auto_delay_unit;
12736 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12737 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12738 titlemessage_first_default.post_delay = title_first_default.post_delay;
12739 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12740 titlemessage_first_default.auto_delay_unit =
12741 title_first_default.auto_delay_unit;
12743 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12744 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12745 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12746 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12747 titlescreen_initial_default.auto_delay_unit =
12748 title_initial_default.auto_delay_unit;
12749 titlescreen_default.fade_mode = title_default.fade_mode;
12750 titlescreen_default.fade_delay = title_default.fade_delay;
12751 titlescreen_default.post_delay = title_default.post_delay;
12752 titlescreen_default.auto_delay = title_default.auto_delay;
12753 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12754 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12755 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12756 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12757 titlemessage_initial_default.auto_delay_unit =
12758 title_initial_default.auto_delay_unit;
12759 titlemessage_default.fade_mode = title_default.fade_mode;
12760 titlemessage_default.fade_delay = title_default.fade_delay;
12761 titlemessage_default.post_delay = title_default.post_delay;
12762 titlemessage_default.auto_delay = title_default.auto_delay;
12763 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12765 // special case: initialize "ARG_DEFAULT" values in static default config
12766 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12767 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12769 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12770 titlescreen_first[i] = titlescreen_first_default;
12771 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12772 titlemessage_first[i] = titlemessage_first_default;
12774 titlescreen_initial[i] = titlescreen_initial_default;
12775 titlescreen[i] = titlescreen_default;
12776 titlemessage_initial[i] = titlemessage_initial_default;
12777 titlemessage[i] = titlemessage_default;
12780 // special case: initialize "ARG_DEFAULT" values in static default config
12781 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12782 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12784 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12787 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12788 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12789 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12792 // special case: initialize "ARG_DEFAULT" values in static default config
12793 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12794 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12796 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12797 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12798 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12800 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12803 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12807 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12811 struct XY *dst, *src;
12813 game_buttons_xy[] =
12815 { &game.button.save, &game.button.stop },
12816 { &game.button.pause2, &game.button.pause },
12817 { &game.button.load, &game.button.play },
12818 { &game.button.undo, &game.button.stop },
12819 { &game.button.redo, &game.button.play },
12825 // special case: initialize later added SETUP list size from LEVELS value
12826 if (menu.list_size[GAME_MODE_SETUP] == -1)
12827 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12829 // set default position for snapshot buttons to stop/pause/play buttons
12830 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12831 if ((*game_buttons_xy[i].dst).x == -1 &&
12832 (*game_buttons_xy[i].dst).y == -1)
12833 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12835 // --------------------------------------------------------------------------
12836 // dynamic viewports (including playfield margins, borders and alignments)
12837 // --------------------------------------------------------------------------
12839 // dynamic viewports currently only supported for landscape mode
12840 int display_width = MAX(video.display_width, video.display_height);
12841 int display_height = MIN(video.display_width, video.display_height);
12843 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12845 struct RectWithBorder *vp_window = &viewport.window[i];
12846 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12847 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12848 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12849 boolean dynamic_window_width = (vp_window->min_width != -1);
12850 boolean dynamic_window_height = (vp_window->min_height != -1);
12851 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12852 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12854 // adjust window size if min/max width/height is specified
12856 if (vp_window->min_width != -1)
12858 int window_width = display_width;
12860 // when using static window height, use aspect ratio of display
12861 if (vp_window->min_height == -1)
12862 window_width = vp_window->height * display_width / display_height;
12864 vp_window->width = MAX(vp_window->min_width, window_width);
12867 if (vp_window->min_height != -1)
12869 int window_height = display_height;
12871 // when using static window width, use aspect ratio of display
12872 if (vp_window->min_width == -1)
12873 window_height = vp_window->width * display_height / display_width;
12875 vp_window->height = MAX(vp_window->min_height, window_height);
12878 if (vp_window->max_width != -1)
12879 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12881 if (vp_window->max_height != -1)
12882 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12884 int playfield_width = vp_window->width;
12885 int playfield_height = vp_window->height;
12887 // adjust playfield size and position according to specified margins
12889 playfield_width -= vp_playfield->margin_left;
12890 playfield_width -= vp_playfield->margin_right;
12892 playfield_height -= vp_playfield->margin_top;
12893 playfield_height -= vp_playfield->margin_bottom;
12895 // adjust playfield size if min/max width/height is specified
12897 if (vp_playfield->min_width != -1)
12898 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12900 if (vp_playfield->min_height != -1)
12901 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12903 if (vp_playfield->max_width != -1)
12904 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12906 if (vp_playfield->max_height != -1)
12907 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12909 // adjust playfield position according to specified alignment
12911 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12912 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12913 else if (vp_playfield->align == ALIGN_CENTER)
12914 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12915 else if (vp_playfield->align == ALIGN_RIGHT)
12916 vp_playfield->x += playfield_width - vp_playfield->width;
12918 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12919 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12920 else if (vp_playfield->valign == VALIGN_MIDDLE)
12921 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12922 else if (vp_playfield->valign == VALIGN_BOTTOM)
12923 vp_playfield->y += playfield_height - vp_playfield->height;
12925 vp_playfield->x += vp_playfield->margin_left;
12926 vp_playfield->y += vp_playfield->margin_top;
12928 // adjust individual playfield borders if only default border is specified
12930 if (vp_playfield->border_left == -1)
12931 vp_playfield->border_left = vp_playfield->border_size;
12932 if (vp_playfield->border_right == -1)
12933 vp_playfield->border_right = vp_playfield->border_size;
12934 if (vp_playfield->border_top == -1)
12935 vp_playfield->border_top = vp_playfield->border_size;
12936 if (vp_playfield->border_bottom == -1)
12937 vp_playfield->border_bottom = vp_playfield->border_size;
12939 // set dynamic playfield borders if borders are specified as undefined
12940 // (but only if window size was dynamic and playfield size was static)
12942 if (dynamic_window_width && !dynamic_playfield_width)
12944 if (vp_playfield->border_left == -1)
12946 vp_playfield->border_left = (vp_playfield->x -
12947 vp_playfield->margin_left);
12948 vp_playfield->x -= vp_playfield->border_left;
12949 vp_playfield->width += vp_playfield->border_left;
12952 if (vp_playfield->border_right == -1)
12954 vp_playfield->border_right = (vp_window->width -
12956 vp_playfield->width -
12957 vp_playfield->margin_right);
12958 vp_playfield->width += vp_playfield->border_right;
12962 if (dynamic_window_height && !dynamic_playfield_height)
12964 if (vp_playfield->border_top == -1)
12966 vp_playfield->border_top = (vp_playfield->y -
12967 vp_playfield->margin_top);
12968 vp_playfield->y -= vp_playfield->border_top;
12969 vp_playfield->height += vp_playfield->border_top;
12972 if (vp_playfield->border_bottom == -1)
12974 vp_playfield->border_bottom = (vp_window->height -
12976 vp_playfield->height -
12977 vp_playfield->margin_bottom);
12978 vp_playfield->height += vp_playfield->border_bottom;
12982 // adjust playfield size to be a multiple of a defined alignment tile size
12984 int align_size = vp_playfield->align_size;
12985 int playfield_xtiles = vp_playfield->width / align_size;
12986 int playfield_ytiles = vp_playfield->height / align_size;
12987 int playfield_width_corrected = playfield_xtiles * align_size;
12988 int playfield_height_corrected = playfield_ytiles * align_size;
12989 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12990 i == GFX_SPECIAL_ARG_EDITOR);
12992 if (is_playfield_mode &&
12993 dynamic_playfield_width &&
12994 vp_playfield->width != playfield_width_corrected)
12996 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12998 vp_playfield->width = playfield_width_corrected;
13000 if (vp_playfield->align == ALIGN_LEFT)
13002 vp_playfield->border_left += playfield_xdiff;
13004 else if (vp_playfield->align == ALIGN_RIGHT)
13006 vp_playfield->border_right += playfield_xdiff;
13008 else if (vp_playfield->align == ALIGN_CENTER)
13010 int border_left_diff = playfield_xdiff / 2;
13011 int border_right_diff = playfield_xdiff - border_left_diff;
13013 vp_playfield->border_left += border_left_diff;
13014 vp_playfield->border_right += border_right_diff;
13018 if (is_playfield_mode &&
13019 dynamic_playfield_height &&
13020 vp_playfield->height != playfield_height_corrected)
13022 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13024 vp_playfield->height = playfield_height_corrected;
13026 if (vp_playfield->valign == VALIGN_TOP)
13028 vp_playfield->border_top += playfield_ydiff;
13030 else if (vp_playfield->align == VALIGN_BOTTOM)
13032 vp_playfield->border_right += playfield_ydiff;
13034 else if (vp_playfield->align == VALIGN_MIDDLE)
13036 int border_top_diff = playfield_ydiff / 2;
13037 int border_bottom_diff = playfield_ydiff - border_top_diff;
13039 vp_playfield->border_top += border_top_diff;
13040 vp_playfield->border_bottom += border_bottom_diff;
13044 // adjust door positions according to specified alignment
13046 for (j = 0; j < 2; j++)
13048 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13050 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13051 vp_door->x = ALIGNED_VP_XPOS(vp_door);
13052 else if (vp_door->align == ALIGN_CENTER)
13053 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13054 else if (vp_door->align == ALIGN_RIGHT)
13055 vp_door->x += vp_window->width - vp_door->width;
13057 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13058 vp_door->y = ALIGNED_VP_YPOS(vp_door);
13059 else if (vp_door->valign == VALIGN_MIDDLE)
13060 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13061 else if (vp_door->valign == VALIGN_BOTTOM)
13062 vp_door->y += vp_window->height - vp_door->height;
13067 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13071 struct XYTileSize *dst, *src;
13074 editor_buttons_xy[] =
13077 &editor.button.element_left, &editor.palette.element_left,
13078 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13081 &editor.button.element_middle, &editor.palette.element_middle,
13082 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13085 &editor.button.element_right, &editor.palette.element_right,
13086 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13093 // set default position for element buttons to element graphics
13094 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13096 if ((*editor_buttons_xy[i].dst).x == -1 &&
13097 (*editor_buttons_xy[i].dst).y == -1)
13099 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13101 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13103 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13107 // adjust editor palette rows and columns if specified to be dynamic
13109 if (editor.palette.cols == -1)
13111 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13112 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13113 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13115 editor.palette.cols = (vp_width - sc_width) / bt_width;
13117 if (editor.palette.x == -1)
13119 int palette_width = editor.palette.cols * bt_width + sc_width;
13121 editor.palette.x = (vp_width - palette_width) / 2;
13125 if (editor.palette.rows == -1)
13127 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13128 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13129 int tx_height = getFontHeight(FONT_TEXT_2);
13131 editor.palette.rows = (vp_height - tx_height) / bt_height;
13133 if (editor.palette.y == -1)
13135 int palette_height = editor.palette.rows * bt_height + tx_height;
13137 editor.palette.y = (vp_height - palette_height) / 2;
13142 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13143 boolean initialize)
13145 // special case: check if network and preview player positions are redefined,
13146 // to compare this later against the main menu level preview being redefined
13147 struct TokenIntPtrInfo menu_config_players[] =
13149 { "main.network_players.x", &menu.main.network_players.redefined },
13150 { "main.network_players.y", &menu.main.network_players.redefined },
13151 { "main.preview_players.x", &menu.main.preview_players.redefined },
13152 { "main.preview_players.y", &menu.main.preview_players.redefined },
13153 { "preview.x", &preview.redefined },
13154 { "preview.y", &preview.redefined }
13160 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13161 *menu_config_players[i].value = FALSE;
13165 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13166 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13167 *menu_config_players[i].value = TRUE;
13171 static void InitMenuDesignSettings_PreviewPlayers(void)
13173 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13176 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13178 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13181 static void LoadMenuDesignSettingsFromFilename(char *filename)
13183 static struct TitleFadingInfo tfi;
13184 static struct TitleMessageInfo tmi;
13185 static struct TokenInfo title_tokens[] =
13187 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
13188 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
13189 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
13190 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
13191 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
13195 static struct TokenInfo titlemessage_tokens[] =
13197 { TYPE_INTEGER, &tmi.x, ".x" },
13198 { TYPE_INTEGER, &tmi.y, ".y" },
13199 { TYPE_INTEGER, &tmi.width, ".width" },
13200 { TYPE_INTEGER, &tmi.height, ".height" },
13201 { TYPE_INTEGER, &tmi.chars, ".chars" },
13202 { TYPE_INTEGER, &tmi.lines, ".lines" },
13203 { TYPE_INTEGER, &tmi.align, ".align" },
13204 { TYPE_INTEGER, &tmi.valign, ".valign" },
13205 { TYPE_INTEGER, &tmi.font, ".font" },
13206 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
13207 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
13208 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
13209 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
13210 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
13211 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
13212 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
13213 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
13214 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
13220 struct TitleFadingInfo *info;
13225 // initialize first titles from "enter screen" definitions, if defined
13226 { &title_initial_first_default, "menu.enter_screen.TITLE" },
13227 { &title_first_default, "menu.enter_screen.TITLE" },
13229 // initialize title screens from "next screen" definitions, if defined
13230 { &title_initial_default, "menu.next_screen.TITLE" },
13231 { &title_default, "menu.next_screen.TITLE" },
13237 struct TitleMessageInfo *array;
13240 titlemessage_arrays[] =
13242 // initialize first titles from "enter screen" definitions, if defined
13243 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
13244 { titlescreen_first, "menu.enter_screen.TITLE" },
13245 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
13246 { titlemessage_first, "menu.enter_screen.TITLE" },
13248 // initialize titles from "next screen" definitions, if defined
13249 { titlescreen_initial, "menu.next_screen.TITLE" },
13250 { titlescreen, "menu.next_screen.TITLE" },
13251 { titlemessage_initial, "menu.next_screen.TITLE" },
13252 { titlemessage, "menu.next_screen.TITLE" },
13254 // overwrite titles with title definitions, if defined
13255 { titlescreen_initial_first, "[title_initial]" },
13256 { titlescreen_first, "[title]" },
13257 { titlemessage_initial_first, "[title_initial]" },
13258 { titlemessage_first, "[title]" },
13260 { titlescreen_initial, "[title_initial]" },
13261 { titlescreen, "[title]" },
13262 { titlemessage_initial, "[title_initial]" },
13263 { titlemessage, "[title]" },
13265 // overwrite titles with title screen/message definitions, if defined
13266 { titlescreen_initial_first, "[titlescreen_initial]" },
13267 { titlescreen_first, "[titlescreen]" },
13268 { titlemessage_initial_first, "[titlemessage_initial]" },
13269 { titlemessage_first, "[titlemessage]" },
13271 { titlescreen_initial, "[titlescreen_initial]" },
13272 { titlescreen, "[titlescreen]" },
13273 { titlemessage_initial, "[titlemessage_initial]" },
13274 { titlemessage, "[titlemessage]" },
13278 SetupFileHash *setup_file_hash;
13281 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13284 // the following initializes hierarchical values from dynamic configuration
13286 // special case: initialize with default values that may be overwritten
13287 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13288 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13290 struct TokenIntPtrInfo menu_config[] =
13292 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
13293 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
13294 { "menu.list_size", &menu.list_size[i] }
13297 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13299 char *token = menu_config[j].token;
13300 char *value = getHashEntry(setup_file_hash, token);
13303 *menu_config[j].value = get_integer_from_string(value);
13307 // special case: initialize with default values that may be overwritten
13308 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13309 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13311 struct TokenIntPtrInfo menu_config[] =
13313 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
13314 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
13315 { "menu.list_size.INFO", &menu.list_size_info[i] },
13316 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
13317 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
13320 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13322 char *token = menu_config[j].token;
13323 char *value = getHashEntry(setup_file_hash, token);
13326 *menu_config[j].value = get_integer_from_string(value);
13330 // special case: initialize with default values that may be overwritten
13331 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13332 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13334 struct TokenIntPtrInfo menu_config[] =
13336 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
13337 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13340 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13342 char *token = menu_config[j].token;
13343 char *value = getHashEntry(setup_file_hash, token);
13346 *menu_config[j].value = get_integer_from_string(value);
13350 // special case: initialize with default values that may be overwritten
13351 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13352 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13354 struct TokenIntPtrInfo menu_config[] =
13356 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13357 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13358 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13359 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13360 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13361 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13362 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13363 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13364 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13365 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13368 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13370 char *token = menu_config[j].token;
13371 char *value = getHashEntry(setup_file_hash, token);
13374 *menu_config[j].value = get_integer_from_string(value);
13378 // special case: initialize with default values that may be overwritten
13379 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13380 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13382 struct TokenIntPtrInfo menu_config[] =
13384 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13385 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13386 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13387 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13388 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13389 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13390 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13391 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13392 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13395 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13397 char *token = menu_config[j].token;
13398 char *value = getHashEntry(setup_file_hash, token);
13401 *menu_config[j].value = get_token_parameter_value(token, value);
13405 // special case: initialize with default values that may be overwritten
13406 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13407 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13411 char *token_prefix;
13412 struct RectWithBorder *struct_ptr;
13416 { "viewport.window", &viewport.window[i] },
13417 { "viewport.playfield", &viewport.playfield[i] },
13418 { "viewport.door_1", &viewport.door_1[i] },
13419 { "viewport.door_2", &viewport.door_2[i] }
13422 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13424 struct TokenIntPtrInfo vp_config[] =
13426 { ".x", &vp_struct[j].struct_ptr->x },
13427 { ".y", &vp_struct[j].struct_ptr->y },
13428 { ".width", &vp_struct[j].struct_ptr->width },
13429 { ".height", &vp_struct[j].struct_ptr->height },
13430 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13431 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13432 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13433 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13434 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13435 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13436 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13437 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13438 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13439 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13440 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13441 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13442 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13443 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13444 { ".align", &vp_struct[j].struct_ptr->align },
13445 { ".valign", &vp_struct[j].struct_ptr->valign }
13448 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13450 char *token = getStringCat2(vp_struct[j].token_prefix,
13451 vp_config[k].token);
13452 char *value = getHashEntry(setup_file_hash, token);
13455 *vp_config[k].value = get_token_parameter_value(token, value);
13462 // special case: initialize with default values that may be overwritten
13463 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13464 for (i = 0; title_info[i].info != NULL; i++)
13466 struct TitleFadingInfo *info = title_info[i].info;
13467 char *base_token = title_info[i].text;
13469 for (j = 0; title_tokens[j].type != -1; j++)
13471 char *token = getStringCat2(base_token, title_tokens[j].text);
13472 char *value = getHashEntry(setup_file_hash, token);
13476 int parameter_value = get_token_parameter_value(token, value);
13480 *(int *)title_tokens[j].value = (int)parameter_value;
13489 // special case: initialize with default values that may be overwritten
13490 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13491 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13493 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13494 char *base_token = titlemessage_arrays[i].text;
13496 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13498 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13499 char *value = getHashEntry(setup_file_hash, token);
13503 int parameter_value = get_token_parameter_value(token, value);
13505 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13509 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13510 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13512 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13522 // read (and overwrite with) values that may be specified in config file
13523 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13525 // special case: check if network and preview player positions are redefined
13526 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13528 freeSetupFileHash(setup_file_hash);
13531 void LoadMenuDesignSettings(void)
13533 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13535 InitMenuDesignSettings_Static();
13536 InitMenuDesignSettings_SpecialPreProcessing();
13537 InitMenuDesignSettings_PreviewPlayers();
13539 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13541 // first look for special settings configured in level series config
13542 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13544 if (fileExists(filename_base))
13545 LoadMenuDesignSettingsFromFilename(filename_base);
13548 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13550 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13551 LoadMenuDesignSettingsFromFilename(filename_local);
13553 InitMenuDesignSettings_SpecialPostProcessing();
13556 void LoadMenuDesignSettings_AfterGraphics(void)
13558 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13561 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13562 boolean ignore_defaults)
13566 for (i = 0; sound_config_vars[i].token != NULL; i++)
13568 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13570 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13571 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13575 *sound_config_vars[i].value =
13576 get_token_parameter_value(sound_config_vars[i].token, value);
13580 void InitSoundSettings_Static(void)
13582 // always start with reliable default values from static default config
13583 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13586 static void LoadSoundSettingsFromFilename(char *filename)
13588 SetupFileHash *setup_file_hash;
13590 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13593 // read (and overwrite with) values that may be specified in config file
13594 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13596 freeSetupFileHash(setup_file_hash);
13599 void LoadSoundSettings(void)
13601 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13603 InitSoundSettings_Static();
13605 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13607 // first look for special settings configured in level series config
13608 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13610 if (fileExists(filename_base))
13611 LoadSoundSettingsFromFilename(filename_base);
13614 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13616 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13617 LoadSoundSettingsFromFilename(filename_local);
13620 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13622 char *filename = getEditorSetupFilename();
13623 SetupFileList *setup_file_list, *list;
13624 SetupFileHash *element_hash;
13625 int num_unknown_tokens = 0;
13628 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13631 element_hash = newSetupFileHash();
13633 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13634 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13636 // determined size may be larger than needed (due to unknown elements)
13638 for (list = setup_file_list; list != NULL; list = list->next)
13641 // add space for up to 3 more elements for padding that may be needed
13642 *num_elements += 3;
13644 // free memory for old list of elements, if needed
13645 checked_free(*elements);
13647 // allocate memory for new list of elements
13648 *elements = checked_malloc(*num_elements * sizeof(int));
13651 for (list = setup_file_list; list != NULL; list = list->next)
13653 char *value = getHashEntry(element_hash, list->token);
13655 if (value == NULL) // try to find obsolete token mapping
13657 char *mapped_token = get_mapped_token(list->token);
13659 if (mapped_token != NULL)
13661 value = getHashEntry(element_hash, mapped_token);
13663 free(mapped_token);
13669 (*elements)[(*num_elements)++] = atoi(value);
13673 if (num_unknown_tokens == 0)
13676 Warn("unknown token(s) found in config file:");
13677 Warn("- config file: '%s'", filename);
13679 num_unknown_tokens++;
13682 Warn("- token: '%s'", list->token);
13686 if (num_unknown_tokens > 0)
13689 while (*num_elements % 4) // pad with empty elements, if needed
13690 (*elements)[(*num_elements)++] = EL_EMPTY;
13692 freeSetupFileList(setup_file_list);
13693 freeSetupFileHash(element_hash);
13696 for (i = 0; i < *num_elements; i++)
13697 Debug("editor", "element '%s' [%d]\n",
13698 element_info[(*elements)[i]].token_name, (*elements)[i]);
13702 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13705 SetupFileHash *setup_file_hash = NULL;
13706 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13707 char *filename_music, *filename_prefix, *filename_info;
13713 token_to_value_ptr[] =
13715 { "title_header", &tmp_music_file_info.title_header },
13716 { "artist_header", &tmp_music_file_info.artist_header },
13717 { "album_header", &tmp_music_file_info.album_header },
13718 { "year_header", &tmp_music_file_info.year_header },
13719 { "played_header", &tmp_music_file_info.played_header },
13721 { "title", &tmp_music_file_info.title },
13722 { "artist", &tmp_music_file_info.artist },
13723 { "album", &tmp_music_file_info.album },
13724 { "year", &tmp_music_file_info.year },
13725 { "played", &tmp_music_file_info.played },
13731 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13732 getCustomMusicFilename(basename));
13734 if (filename_music == NULL)
13737 // ---------- try to replace file extension ----------
13739 filename_prefix = getStringCopy(filename_music);
13740 if (strrchr(filename_prefix, '.') != NULL)
13741 *strrchr(filename_prefix, '.') = '\0';
13742 filename_info = getStringCat2(filename_prefix, ".txt");
13744 if (fileExists(filename_info))
13745 setup_file_hash = loadSetupFileHash(filename_info);
13747 free(filename_prefix);
13748 free(filename_info);
13750 if (setup_file_hash == NULL)
13752 // ---------- try to add file extension ----------
13754 filename_prefix = getStringCopy(filename_music);
13755 filename_info = getStringCat2(filename_prefix, ".txt");
13757 if (fileExists(filename_info))
13758 setup_file_hash = loadSetupFileHash(filename_info);
13760 free(filename_prefix);
13761 free(filename_info);
13764 if (setup_file_hash == NULL)
13767 // ---------- music file info found ----------
13769 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13771 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13773 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13775 *token_to_value_ptr[i].value_ptr =
13776 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13779 tmp_music_file_info.basename = getStringCopy(basename);
13780 tmp_music_file_info.music = music;
13781 tmp_music_file_info.is_sound = is_sound;
13783 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13784 *new_music_file_info = tmp_music_file_info;
13786 return new_music_file_info;
13789 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13791 return get_music_file_info_ext(basename, music, FALSE);
13794 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13796 return get_music_file_info_ext(basename, sound, TRUE);
13799 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13800 char *basename, boolean is_sound)
13802 for (; list != NULL; list = list->next)
13803 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13809 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13811 return music_info_listed_ext(list, basename, FALSE);
13814 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13816 return music_info_listed_ext(list, basename, TRUE);
13819 void LoadMusicInfo(void)
13821 int num_music_noconf = getMusicListSize_NoConf();
13822 int num_music = getMusicListSize();
13823 int num_sounds = getSoundListSize();
13824 struct FileInfo *music, *sound;
13825 struct MusicFileInfo *next, **new;
13829 while (music_file_info != NULL)
13831 next = music_file_info->next;
13833 checked_free(music_file_info->basename);
13835 checked_free(music_file_info->title_header);
13836 checked_free(music_file_info->artist_header);
13837 checked_free(music_file_info->album_header);
13838 checked_free(music_file_info->year_header);
13839 checked_free(music_file_info->played_header);
13841 checked_free(music_file_info->title);
13842 checked_free(music_file_info->artist);
13843 checked_free(music_file_info->album);
13844 checked_free(music_file_info->year);
13845 checked_free(music_file_info->played);
13847 free(music_file_info);
13849 music_file_info = next;
13852 new = &music_file_info;
13854 // get (configured or unconfigured) music file info for all levels
13855 for (i = leveldir_current->first_level;
13856 i <= leveldir_current->last_level; i++)
13860 if (levelset.music[i] != MUS_UNDEFINED)
13862 // get music file info for configured level music
13863 music_nr = levelset.music[i];
13865 else if (num_music_noconf > 0)
13867 // get music file info for unconfigured level music
13868 int level_pos = i - leveldir_current->first_level;
13870 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13877 char *basename = getMusicInfoEntryFilename(music_nr);
13879 if (basename == NULL)
13882 if (!music_info_listed(music_file_info, basename))
13884 *new = get_music_file_info(basename, music_nr);
13887 new = &(*new)->next;
13891 // get music file info for all remaining configured music files
13892 for (i = 0; i < num_music; i++)
13894 music = getMusicListEntry(i);
13896 if (music->filename == NULL)
13899 if (strEqual(music->filename, UNDEFINED_FILENAME))
13902 // a configured file may be not recognized as music
13903 if (!FileIsMusic(music->filename))
13906 if (!music_info_listed(music_file_info, music->filename))
13908 *new = get_music_file_info(music->filename, i);
13911 new = &(*new)->next;
13915 // get sound file info for all configured sound files
13916 for (i = 0; i < num_sounds; i++)
13918 sound = getSoundListEntry(i);
13920 if (sound->filename == NULL)
13923 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13926 // a configured file may be not recognized as sound
13927 if (!FileIsSound(sound->filename))
13930 if (!sound_info_listed(music_file_info, sound->filename))
13932 *new = get_sound_file_info(sound->filename, i);
13934 new = &(*new)->next;
13938 // add pointers to previous list nodes
13940 struct MusicFileInfo *node = music_file_info;
13942 while (node != NULL)
13945 node->next->prev = node;
13951 static void add_helpanim_entry(int element, int action, int direction,
13952 int delay, int *num_list_entries)
13954 struct HelpAnimInfo *new_list_entry;
13955 (*num_list_entries)++;
13958 checked_realloc(helpanim_info,
13959 *num_list_entries * sizeof(struct HelpAnimInfo));
13960 new_list_entry = &helpanim_info[*num_list_entries - 1];
13962 new_list_entry->element = element;
13963 new_list_entry->action = action;
13964 new_list_entry->direction = direction;
13965 new_list_entry->delay = delay;
13968 static void print_unknown_token(char *filename, char *token, int token_nr)
13973 Warn("unknown token(s) found in config file:");
13974 Warn("- config file: '%s'", filename);
13977 Warn("- token: '%s'", token);
13980 static void print_unknown_token_end(int token_nr)
13986 void LoadHelpAnimInfo(void)
13988 char *filename = getHelpAnimFilename();
13989 SetupFileList *setup_file_list = NULL, *list;
13990 SetupFileHash *element_hash, *action_hash, *direction_hash;
13991 int num_list_entries = 0;
13992 int num_unknown_tokens = 0;
13995 if (fileExists(filename))
13996 setup_file_list = loadSetupFileList(filename);
13998 if (setup_file_list == NULL)
14000 // use reliable default values from static configuration
14001 SetupFileList *insert_ptr;
14003 insert_ptr = setup_file_list =
14004 newSetupFileList(helpanim_config[0].token,
14005 helpanim_config[0].value);
14007 for (i = 1; helpanim_config[i].token; i++)
14008 insert_ptr = addListEntry(insert_ptr,
14009 helpanim_config[i].token,
14010 helpanim_config[i].value);
14013 element_hash = newSetupFileHash();
14014 action_hash = newSetupFileHash();
14015 direction_hash = newSetupFileHash();
14017 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14018 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14020 for (i = 0; i < NUM_ACTIONS; i++)
14021 setHashEntry(action_hash, element_action_info[i].suffix,
14022 i_to_a(element_action_info[i].value));
14024 // do not store direction index (bit) here, but direction value!
14025 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14026 setHashEntry(direction_hash, element_direction_info[i].suffix,
14027 i_to_a(1 << element_direction_info[i].value));
14029 for (list = setup_file_list; list != NULL; list = list->next)
14031 char *element_token, *action_token, *direction_token;
14032 char *element_value, *action_value, *direction_value;
14033 int delay = atoi(list->value);
14035 if (strEqual(list->token, "end"))
14037 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14042 /* first try to break element into element/action/direction parts;
14043 if this does not work, also accept combined "element[.act][.dir]"
14044 elements (like "dynamite.active"), which are unique elements */
14046 if (strchr(list->token, '.') == NULL) // token contains no '.'
14048 element_value = getHashEntry(element_hash, list->token);
14049 if (element_value != NULL) // element found
14050 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14051 &num_list_entries);
14054 // no further suffixes found -- this is not an element
14055 print_unknown_token(filename, list->token, num_unknown_tokens++);
14061 // token has format "<prefix>.<something>"
14063 action_token = strchr(list->token, '.'); // suffix may be action ...
14064 direction_token = action_token; // ... or direction
14066 element_token = getStringCopy(list->token);
14067 *strchr(element_token, '.') = '\0';
14069 element_value = getHashEntry(element_hash, element_token);
14071 if (element_value == NULL) // this is no element
14073 element_value = getHashEntry(element_hash, list->token);
14074 if (element_value != NULL) // combined element found
14075 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14076 &num_list_entries);
14078 print_unknown_token(filename, list->token, num_unknown_tokens++);
14080 free(element_token);
14085 action_value = getHashEntry(action_hash, action_token);
14087 if (action_value != NULL) // action found
14089 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14090 &num_list_entries);
14092 free(element_token);
14097 direction_value = getHashEntry(direction_hash, direction_token);
14099 if (direction_value != NULL) // direction found
14101 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14102 &num_list_entries);
14104 free(element_token);
14109 if (strchr(action_token + 1, '.') == NULL)
14111 // no further suffixes found -- this is not an action nor direction
14113 element_value = getHashEntry(element_hash, list->token);
14114 if (element_value != NULL) // combined element found
14115 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14116 &num_list_entries);
14118 print_unknown_token(filename, list->token, num_unknown_tokens++);
14120 free(element_token);
14125 // token has format "<prefix>.<suffix>.<something>"
14127 direction_token = strchr(action_token + 1, '.');
14129 action_token = getStringCopy(action_token);
14130 *strchr(action_token + 1, '.') = '\0';
14132 action_value = getHashEntry(action_hash, action_token);
14134 if (action_value == NULL) // this is no action
14136 element_value = getHashEntry(element_hash, list->token);
14137 if (element_value != NULL) // combined element found
14138 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14139 &num_list_entries);
14141 print_unknown_token(filename, list->token, num_unknown_tokens++);
14143 free(element_token);
14144 free(action_token);
14149 direction_value = getHashEntry(direction_hash, direction_token);
14151 if (direction_value != NULL) // direction found
14153 add_helpanim_entry(atoi(element_value), atoi(action_value),
14154 atoi(direction_value), delay, &num_list_entries);
14156 free(element_token);
14157 free(action_token);
14162 // this is no direction
14164 element_value = getHashEntry(element_hash, list->token);
14165 if (element_value != NULL) // combined element found
14166 add_helpanim_entry(atoi(element_value), -1, -1, delay,
14167 &num_list_entries);
14169 print_unknown_token(filename, list->token, num_unknown_tokens++);
14171 free(element_token);
14172 free(action_token);
14175 print_unknown_token_end(num_unknown_tokens);
14177 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14178 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
14180 freeSetupFileList(setup_file_list);
14181 freeSetupFileHash(element_hash);
14182 freeSetupFileHash(action_hash);
14183 freeSetupFileHash(direction_hash);
14186 for (i = 0; i < num_list_entries; i++)
14187 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14188 EL_NAME(helpanim_info[i].element),
14189 helpanim_info[i].element,
14190 helpanim_info[i].action,
14191 helpanim_info[i].direction,
14192 helpanim_info[i].delay);
14196 void LoadHelpTextInfo(void)
14198 char *filename = getHelpTextFilename();
14201 if (helptext_info != NULL)
14203 freeSetupFileHash(helptext_info);
14204 helptext_info = NULL;
14207 if (fileExists(filename))
14208 helptext_info = loadSetupFileHash(filename);
14210 if (helptext_info == NULL)
14212 // use reliable default values from static configuration
14213 helptext_info = newSetupFileHash();
14215 for (i = 0; helptext_config[i].token; i++)
14216 setHashEntry(helptext_info,
14217 helptext_config[i].token,
14218 helptext_config[i].value);
14222 BEGIN_HASH_ITERATION(helptext_info, itr)
14224 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14225 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14227 END_HASH_ITERATION(hash, itr)
14232 // ----------------------------------------------------------------------------
14234 // ----------------------------------------------------------------------------
14236 #define MAX_NUM_CONVERT_LEVELS 1000
14238 void ConvertLevels(void)
14240 static LevelDirTree *convert_leveldir = NULL;
14241 static int convert_level_nr = -1;
14242 static int num_levels_handled = 0;
14243 static int num_levels_converted = 0;
14244 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14247 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14248 global.convert_leveldir);
14250 if (convert_leveldir == NULL)
14251 Fail("no such level identifier: '%s'", global.convert_leveldir);
14253 leveldir_current = convert_leveldir;
14255 if (global.convert_level_nr != -1)
14257 convert_leveldir->first_level = global.convert_level_nr;
14258 convert_leveldir->last_level = global.convert_level_nr;
14261 convert_level_nr = convert_leveldir->first_level;
14263 PrintLine("=", 79);
14264 Print("Converting levels\n");
14265 PrintLine("-", 79);
14266 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14267 Print("Level series name: '%s'\n", convert_leveldir->name);
14268 Print("Level series author: '%s'\n", convert_leveldir->author);
14269 Print("Number of levels: %d\n", convert_leveldir->levels);
14270 PrintLine("=", 79);
14273 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14274 levels_failed[i] = FALSE;
14276 while (convert_level_nr <= convert_leveldir->last_level)
14278 char *level_filename;
14281 level_nr = convert_level_nr++;
14283 Print("Level %03d: ", level_nr);
14285 LoadLevel(level_nr);
14286 if (level.no_level_file || level.no_valid_file)
14288 Print("(no level)\n");
14292 Print("converting level ... ");
14295 // special case: conversion of some EMC levels as requested by ACME
14296 level.game_engine_type = GAME_ENGINE_TYPE_RND;
14299 level_filename = getDefaultLevelFilename(level_nr);
14300 new_level = !fileExists(level_filename);
14304 SaveLevel(level_nr);
14306 num_levels_converted++;
14308 Print("converted.\n");
14312 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14313 levels_failed[level_nr] = TRUE;
14315 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14318 num_levels_handled++;
14322 PrintLine("=", 79);
14323 Print("Number of levels handled: %d\n", num_levels_handled);
14324 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14325 (num_levels_handled ?
14326 num_levels_converted * 100 / num_levels_handled : 0));
14327 PrintLine("-", 79);
14328 Print("Summary (for automatic parsing by scripts):\n");
14329 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14330 convert_leveldir->identifier, num_levels_converted,
14331 num_levels_handled,
14332 (num_levels_handled ?
14333 num_levels_converted * 100 / num_levels_handled : 0));
14335 if (num_levels_handled != num_levels_converted)
14337 Print(", FAILED:");
14338 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14339 if (levels_failed[i])
14344 PrintLine("=", 79);
14346 CloseAllAndExit(0);
14350 // ----------------------------------------------------------------------------
14351 // create and save images for use in level sketches (raw BMP format)
14352 // ----------------------------------------------------------------------------
14354 void CreateLevelSketchImages(void)
14360 InitElementPropertiesGfxElement();
14362 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14363 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14365 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14367 int element = getMappedElement(i);
14368 char basename1[16];
14369 char basename2[16];
14373 sprintf(basename1, "%04d.bmp", i);
14374 sprintf(basename2, "%04ds.bmp", i);
14376 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14377 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14379 DrawSizedElement(0, 0, element, TILESIZE);
14380 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14382 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14383 Fail("cannot save level sketch image file '%s'", filename1);
14385 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14386 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14388 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14389 Fail("cannot save level sketch image file '%s'", filename2);
14394 // create corresponding SQL statements (for normal and small images)
14397 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14398 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14401 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14402 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14404 // optional: create content for forum level sketch demonstration post
14406 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14409 FreeBitmap(bitmap1);
14410 FreeBitmap(bitmap2);
14413 fprintf(stderr, "\n");
14415 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14417 CloseAllAndExit(0);
14421 // ----------------------------------------------------------------------------
14422 // create and save images for element collecting animations (raw BMP format)
14423 // ----------------------------------------------------------------------------
14425 static boolean createCollectImage(int element)
14427 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14430 void CreateCollectElementImages(void)
14434 int anim_frames = num_steps - 1;
14435 int tile_size = TILESIZE;
14436 int anim_width = tile_size * anim_frames;
14437 int anim_height = tile_size;
14438 int num_collect_images = 0;
14439 int pos_collect_images = 0;
14441 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14442 if (createCollectImage(i))
14443 num_collect_images++;
14445 Info("Creating %d element collecting animation images ...",
14446 num_collect_images);
14448 int dst_width = anim_width * 2;
14449 int dst_height = anim_height * num_collect_images / 2;
14450 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14451 char *basename_bmp = "RocksCollect.bmp";
14452 char *basename_png = "RocksCollect.png";
14453 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14454 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14455 int len_filename_bmp = strlen(filename_bmp);
14456 int len_filename_png = strlen(filename_png);
14457 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14458 char cmd_convert[max_command_len];
14460 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14464 // force using RGBA surface for destination bitmap
14465 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14466 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14468 dst_bitmap->surface =
14469 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14471 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14473 if (!createCollectImage(i))
14476 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14477 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14478 int graphic = el2img(i);
14479 char *token_name = element_info[i].token_name;
14480 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14481 Bitmap *src_bitmap;
14484 Info("- creating collecting image for '%s' ...", token_name);
14486 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14488 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14489 tile_size, tile_size, 0, 0);
14491 // force using RGBA surface for temporary bitmap (using transparent black)
14492 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14493 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14495 tmp_bitmap->surface =
14496 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14498 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14500 for (j = 0; j < anim_frames; j++)
14502 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14503 int frame_size = frame_size_final * num_steps;
14504 int offset = (tile_size - frame_size_final) / 2;
14505 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14507 while (frame_size > frame_size_final)
14511 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14513 FreeBitmap(frame_bitmap);
14515 frame_bitmap = half_bitmap;
14518 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14519 frame_size_final, frame_size_final,
14520 dst_x + j * tile_size + offset, dst_y + offset);
14522 FreeBitmap(frame_bitmap);
14525 tmp_bitmap->surface_masked = NULL;
14527 FreeBitmap(tmp_bitmap);
14529 pos_collect_images++;
14532 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14533 Fail("cannot save element collecting image file '%s'", filename_bmp);
14535 FreeBitmap(dst_bitmap);
14537 Info("Converting image file from BMP to PNG ...");
14539 if (system(cmd_convert) != 0)
14540 Fail("converting image file failed");
14542 unlink(filename_bmp);
14546 CloseAllAndExit(0);
14550 // ----------------------------------------------------------------------------
14551 // create and save images for custom and group elements (raw BMP format)
14552 // ----------------------------------------------------------------------------
14554 void CreateCustomElementImages(char *directory)
14556 char *src_basename = "RocksCE-template.ilbm";
14557 char *dst_basename = "RocksCE.bmp";
14558 char *src_filename = getPath2(directory, src_basename);
14559 char *dst_filename = getPath2(directory, dst_basename);
14560 Bitmap *src_bitmap;
14562 int yoffset_ce = 0;
14563 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14566 InitVideoDefaults();
14568 ReCreateBitmap(&backbuffer, video.width, video.height);
14570 src_bitmap = LoadImage(src_filename);
14572 bitmap = CreateBitmap(TILEX * 16 * 2,
14573 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14576 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14583 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14584 TILEX * x, TILEY * y + yoffset_ce);
14586 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14588 TILEX * x + TILEX * 16,
14589 TILEY * y + yoffset_ce);
14591 for (j = 2; j >= 0; j--)
14595 BlitBitmap(src_bitmap, bitmap,
14596 TILEX + c * 7, 0, 6, 10,
14597 TILEX * x + 6 + j * 7,
14598 TILEY * y + 11 + yoffset_ce);
14600 BlitBitmap(src_bitmap, bitmap,
14601 TILEX + c * 8, TILEY, 6, 10,
14602 TILEX * 16 + TILEX * x + 6 + j * 8,
14603 TILEY * y + 10 + yoffset_ce);
14609 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14616 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14617 TILEX * x, TILEY * y + yoffset_ge);
14619 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14621 TILEX * x + TILEX * 16,
14622 TILEY * y + yoffset_ge);
14624 for (j = 1; j >= 0; j--)
14628 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14629 TILEX * x + 6 + j * 10,
14630 TILEY * y + 11 + yoffset_ge);
14632 BlitBitmap(src_bitmap, bitmap,
14633 TILEX + c * 8, TILEY + 12, 6, 10,
14634 TILEX * 16 + TILEX * x + 10 + j * 8,
14635 TILEY * y + 10 + yoffset_ge);
14641 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14642 Fail("cannot save CE graphics file '%s'", dst_filename);
14644 FreeBitmap(bitmap);
14646 CloseAllAndExit(0);