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
170 -1, SAVE_CONF_ALWAYS,
171 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
172 &li.fieldx, STD_LEV_FIELDX
175 -1, SAVE_CONF_ALWAYS,
176 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
177 &li.fieldy, STD_LEV_FIELDY
181 -1, SAVE_CONF_ALWAYS,
182 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
187 -1, SAVE_CONF_ALWAYS,
188 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
194 TYPE_INTEGER, CONF_VALUE_32_BIT(2),
200 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
201 &li.use_step_counter, FALSE
206 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
207 &li.wind_direction_initial, MV_NONE
212 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
213 &li.em_slippery_gems, FALSE
218 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
219 &li.use_custom_template, FALSE
224 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
225 &li.can_move_into_acid_bits, ~0 // default: everything can
230 TYPE_BITFIELD, CONF_VALUE_8_BIT(7),
231 &li.dont_collide_with_bits, ~0 // default: always deadly
236 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
237 &li.em_explodes_by_fire, FALSE
242 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
243 &li.score[SC_TIME_BONUS], 1
248 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
249 &li.auto_exit_sokoban, FALSE
254 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
255 &li.auto_count_gems, FALSE
260 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
261 &li.solved_by_one_player, FALSE
266 TYPE_INTEGER, CONF_VALUE_8_BIT(12),
267 &li.time_score_base, 1
272 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
273 &li.rate_time_over_score, FALSE
278 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
279 &li.bd_intermission, FALSE
284 TYPE_INTEGER, CONF_VALUE_8_BIT(15),
285 &li.bd_scheduling_type, GD_SCHEDULING_MILLISECONDS
290 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
291 &li.bd_pal_timing, FALSE
296 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
297 &li.bd_cycle_delay_ms, 200
302 TYPE_INTEGER, CONF_VALUE_8_BIT(17),
303 &li.bd_cycle_delay_c64, 0
308 TYPE_INTEGER, CONF_VALUE_8_BIT(18),
309 &li.bd_hatching_delay_cycles, 21
314 TYPE_INTEGER, CONF_VALUE_8_BIT(19),
315 &li.bd_hatching_delay_seconds, 2
320 TYPE_BOOLEAN, CONF_VALUE_8_BIT(20),
321 &li.bd_line_shifting_borders, FALSE
326 TYPE_BOOLEAN, CONF_VALUE_8_BIT(21),
327 &li.bd_wraparound_objects, FALSE
332 TYPE_BOOLEAN, CONF_VALUE_8_BIT(22),
333 &li.bd_scan_first_and_last_row, TRUE
338 TYPE_BOOLEAN, CONF_VALUE_8_BIT(23),
339 &li.bd_short_explosions, TRUE
349 static struct LevelFileConfigInfo chunk_config_ELEM[] =
351 // (these values are the same for each player)
354 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
355 &li.block_last_field, FALSE // default case for EM levels
359 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
360 &li.sp_block_last_field, TRUE // default case for SP levels
364 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
365 &li.instant_relocation, FALSE
369 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
370 &li.can_pass_to_walkable, FALSE
374 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
375 &li.block_snap_field, TRUE
379 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
380 &li.continuous_snapping, TRUE
384 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
385 &li.shifted_relocation, FALSE
389 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
390 &li.lazy_relocation, FALSE
394 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
395 &li.finish_dig_collect, TRUE
399 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
400 &li.keep_walkable_ce, FALSE
403 // (these values are different for each player)
406 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
407 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
411 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
412 &li.initial_player_gravity[0], FALSE
416 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
417 &li.use_start_element[0], FALSE
421 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
422 &li.start_element[0], EL_PLAYER_1
426 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
427 &li.use_artwork_element[0], FALSE
431 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
432 &li.artwork_element[0], EL_PLAYER_1
436 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
437 &li.use_explosion_element[0], FALSE
441 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
442 &li.explosion_element[0], EL_PLAYER_1
446 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
447 &li.use_initial_inventory[0], FALSE
451 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
452 &li.initial_inventory_size[0], 1
456 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
457 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
458 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
463 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
464 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
468 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
469 &li.initial_player_gravity[1], FALSE
473 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
474 &li.use_start_element[1], FALSE
478 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
479 &li.start_element[1], EL_PLAYER_2
483 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
484 &li.use_artwork_element[1], FALSE
488 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
489 &li.artwork_element[1], EL_PLAYER_2
493 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
494 &li.use_explosion_element[1], FALSE
498 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
499 &li.explosion_element[1], EL_PLAYER_2
503 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
504 &li.use_initial_inventory[1], FALSE
508 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
509 &li.initial_inventory_size[1], 1
513 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
514 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
515 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
520 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
521 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
525 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
526 &li.initial_player_gravity[2], FALSE
530 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
531 &li.use_start_element[2], FALSE
535 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
536 &li.start_element[2], EL_PLAYER_3
540 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
541 &li.use_artwork_element[2], FALSE
545 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
546 &li.artwork_element[2], EL_PLAYER_3
550 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
551 &li.use_explosion_element[2], FALSE
555 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
556 &li.explosion_element[2], EL_PLAYER_3
560 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
561 &li.use_initial_inventory[2], FALSE
565 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
566 &li.initial_inventory_size[2], 1
570 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
571 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
572 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
577 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
578 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
582 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
583 &li.initial_player_gravity[3], FALSE
587 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
588 &li.use_start_element[3], FALSE
592 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
593 &li.start_element[3], EL_PLAYER_4
597 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
598 &li.use_artwork_element[3], FALSE
602 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
603 &li.artwork_element[3], EL_PLAYER_4
607 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
608 &li.use_explosion_element[3], FALSE
612 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
613 &li.explosion_element[3], EL_PLAYER_4
617 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
618 &li.use_initial_inventory[3], FALSE
622 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
623 &li.initial_inventory_size[3], 1
627 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
628 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
629 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
632 // (these values are only valid for BD style levels)
635 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
636 &li.bd_diagonal_movements, FALSE
641 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
642 &li.score[SC_DIAMOND_EXTRA], 20
645 // (the following values are related to various game elements)
649 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
650 &li.score[SC_EMERALD], 10
655 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
656 &li.score[SC_DIAMOND], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
662 &li.score[SC_BUG], 10
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
668 &li.score[SC_SPACESHIP], 10
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.score[SC_PACMAN], 10
679 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
680 &li.score[SC_NUT], 10
685 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
686 &li.score[SC_DYNAMITE], 10
691 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
692 &li.score[SC_KEY], 10
697 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
698 &li.score[SC_PEARL], 10
703 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
704 &li.score[SC_CRYSTAL], 10
709 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
710 &li.amoeba_content, EL_DIAMOND
714 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
719 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
720 &li.grow_into_diggable, TRUE
725 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
726 &li.yamyam_content, EL_ROCK, NULL,
727 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
731 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
732 &li.score[SC_YAMYAM], 10
737 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
738 &li.score[SC_ROBOT], 10
742 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
748 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
754 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
755 &li.time_magic_wall, 10
760 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
761 &li.game_of_life[0], 2
765 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
766 &li.game_of_life[1], 3
770 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
771 &li.game_of_life[2], 3
775 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
776 &li.game_of_life[3], 3
780 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
781 &li.use_life_bugs, FALSE
786 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
791 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
796 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
801 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
806 EL_TIMEGATE_SWITCH, -1,
807 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
808 &li.time_timegate, 10
812 EL_LIGHT_SWITCH_ACTIVE, -1,
813 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
818 EL_SHIELD_NORMAL, -1,
819 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
820 &li.shield_normal_time, 10
823 EL_SHIELD_NORMAL, -1,
824 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
825 &li.score[SC_SHIELD], 10
829 EL_SHIELD_DEADLY, -1,
830 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
831 &li.shield_deadly_time, 10
834 EL_SHIELD_DEADLY, -1,
835 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
836 &li.score[SC_SHIELD], 10
841 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
846 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
847 &li.extra_time_score, 10
851 EL_TIME_ORB_FULL, -1,
852 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
853 &li.time_orb_time, 10
856 EL_TIME_ORB_FULL, -1,
857 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
858 &li.use_time_orb_bug, FALSE
863 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
864 &li.use_spring_bug, FALSE
869 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
870 &li.android_move_time, 10
874 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
875 &li.android_clone_time, 10
878 EL_EMC_ANDROID, SAVE_CONF_NEVER,
879 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
880 &li.android_clone_element[0], EL_EMPTY, NULL,
881 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
885 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
886 &li.android_clone_element[0], EL_EMPTY, NULL,
887 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
892 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
897 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
902 EL_EMC_MAGNIFIER, -1,
903 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
904 &li.magnify_score, 10
907 EL_EMC_MAGNIFIER, -1,
908 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
913 EL_EMC_MAGIC_BALL, -1,
914 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
918 EL_EMC_MAGIC_BALL, -1,
919 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
920 &li.ball_random, FALSE
923 EL_EMC_MAGIC_BALL, -1,
924 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
925 &li.ball_active_initial, FALSE
928 EL_EMC_MAGIC_BALL, -1,
929 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
930 &li.ball_content, EL_EMPTY, NULL,
931 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
935 EL_SOKOBAN_FIELD_EMPTY, -1,
936 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
937 &li.sb_fields_needed, TRUE
941 EL_SOKOBAN_OBJECT, -1,
942 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
943 &li.sb_objects_needed, TRUE
948 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
949 &li.mm_laser_red, FALSE
953 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
954 &li.mm_laser_green, FALSE
958 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
959 &li.mm_laser_blue, TRUE
964 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
965 &li.df_laser_red, TRUE
969 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
970 &li.df_laser_green, TRUE
974 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
975 &li.df_laser_blue, FALSE
979 EL_MM_FUSE_ACTIVE, -1,
980 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
985 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
991 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
996 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
997 &li.mm_ball_choice_mode, ANIM_RANDOM
1000 EL_MM_GRAY_BALL, -1,
1001 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1002 &li.mm_ball_content, EL_EMPTY, NULL,
1003 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1006 EL_MM_GRAY_BALL, -1,
1007 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1008 &li.rotate_mm_ball_content, TRUE
1011 EL_MM_GRAY_BALL, -1,
1012 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1013 &li.explode_mm_ball, FALSE
1017 EL_MM_STEEL_BLOCK, -1,
1018 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1019 &li.mm_time_block, 75
1022 EL_MM_LIGHTBALL, -1,
1023 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1024 &li.score[SC_ELEM_BONUS], 10
1034 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1038 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1039 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1043 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1044 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1049 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1050 &xx_envelope.autowrap, FALSE
1054 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1055 &xx_envelope.centered, FALSE
1060 TYPE_STRING, CONF_VALUE_BYTES(1),
1061 &xx_envelope.text, -1, NULL,
1062 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1063 &xx_default_string_empty[0]
1073 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1077 TYPE_STRING, CONF_VALUE_BYTES(1),
1078 &xx_ei.description[0], -1,
1079 &yy_ei.description[0],
1080 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1081 &xx_default_description[0]
1086 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1087 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1088 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1090 #if ENABLE_RESERVED_CODE
1091 // (reserved for later use)
1094 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1095 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1096 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1102 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1103 &xx_ei.use_gfx_element, FALSE,
1104 &yy_ei.use_gfx_element
1108 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1109 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1110 &yy_ei.gfx_element_initial
1115 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1116 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1117 &yy_ei.access_direction
1122 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1123 &xx_ei.collect_score_initial, 10,
1124 &yy_ei.collect_score_initial
1128 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1129 &xx_ei.collect_count_initial, 1,
1130 &yy_ei.collect_count_initial
1135 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1136 &xx_ei.ce_value_fixed_initial, 0,
1137 &yy_ei.ce_value_fixed_initial
1141 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1142 &xx_ei.ce_value_random_initial, 0,
1143 &yy_ei.ce_value_random_initial
1147 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1148 &xx_ei.use_last_ce_value, FALSE,
1149 &yy_ei.use_last_ce_value
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1155 &xx_ei.push_delay_fixed, 8,
1156 &yy_ei.push_delay_fixed
1160 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1161 &xx_ei.push_delay_random, 8,
1162 &yy_ei.push_delay_random
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1167 &xx_ei.drop_delay_fixed, 0,
1168 &yy_ei.drop_delay_fixed
1172 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1173 &xx_ei.drop_delay_random, 0,
1174 &yy_ei.drop_delay_random
1178 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1179 &xx_ei.move_delay_fixed, 0,
1180 &yy_ei.move_delay_fixed
1184 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1185 &xx_ei.move_delay_random, 0,
1186 &yy_ei.move_delay_random
1190 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1191 &xx_ei.step_delay_fixed, 0,
1192 &yy_ei.step_delay_fixed
1196 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1197 &xx_ei.step_delay_random, 0,
1198 &yy_ei.step_delay_random
1203 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1204 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1209 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1210 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1211 &yy_ei.move_direction_initial
1215 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1216 &xx_ei.move_stepsize, TILEX / 8,
1217 &yy_ei.move_stepsize
1222 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1223 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1224 &yy_ei.move_enter_element
1228 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1229 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1230 &yy_ei.move_leave_element
1234 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1235 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1236 &yy_ei.move_leave_type
1241 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1242 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1243 &yy_ei.slippery_type
1248 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1249 &xx_ei.explosion_type, EXPLODES_3X3,
1250 &yy_ei.explosion_type
1254 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1255 &xx_ei.explosion_delay, 16,
1256 &yy_ei.explosion_delay
1260 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1261 &xx_ei.ignition_delay, 8,
1262 &yy_ei.ignition_delay
1267 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1268 &xx_ei.content, EL_EMPTY_SPACE,
1270 &xx_num_contents, 1, 1
1273 // ---------- "num_change_pages" must be the last entry ---------------------
1276 -1, SAVE_CONF_ALWAYS,
1277 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1278 &xx_ei.num_change_pages, 1,
1279 &yy_ei.num_change_pages
1290 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1292 // ---------- "current_change_page" must be the first entry -----------------
1295 -1, SAVE_CONF_ALWAYS,
1296 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1297 &xx_current_change_page, -1
1300 // ---------- (the remaining entries can be in any order) -------------------
1304 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1305 &xx_change.can_change, FALSE
1310 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1311 &xx_event_bits[0], 0
1315 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1316 &xx_event_bits[1], 0
1321 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1322 &xx_change.trigger_player, CH_PLAYER_ANY
1326 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1327 &xx_change.trigger_side, CH_SIDE_ANY
1331 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1332 &xx_change.trigger_page, CH_PAGE_ANY
1337 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1338 &xx_change.target_element, EL_EMPTY_SPACE
1343 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1344 &xx_change.delay_fixed, 0
1348 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1349 &xx_change.delay_random, 0
1353 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1354 &xx_change.delay_frames, FRAMES_PER_SECOND
1359 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1360 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1365 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1366 &xx_change.explode, FALSE
1370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1371 &xx_change.use_target_content, FALSE
1375 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1376 &xx_change.only_if_complete, FALSE
1380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1381 &xx_change.use_random_replace, FALSE
1385 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1386 &xx_change.random_percentage, 100
1390 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1391 &xx_change.replace_when, CP_WHEN_EMPTY
1396 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1397 &xx_change.has_action, FALSE
1401 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1402 &xx_change.action_type, CA_NO_ACTION
1406 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1407 &xx_change.action_mode, CA_MODE_UNDEFINED
1411 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1412 &xx_change.action_arg, CA_ARG_UNDEFINED
1417 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1418 &xx_change.action_element, EL_EMPTY_SPACE
1423 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1424 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1425 &xx_num_contents, 1, 1
1435 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1439 TYPE_STRING, CONF_VALUE_BYTES(1),
1440 &xx_ei.description[0], -1, NULL,
1441 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1442 &xx_default_description[0]
1447 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1448 &xx_ei.use_gfx_element, FALSE
1452 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1453 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1458 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1459 &xx_group.choice_mode, ANIM_RANDOM
1464 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1465 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1466 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1476 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1480 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1481 &xx_ei.use_gfx_element, FALSE
1485 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1486 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1496 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1500 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1501 &li.block_snap_field, TRUE
1505 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1506 &li.continuous_snapping, TRUE
1510 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1511 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1515 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1516 &li.use_start_element[0], FALSE
1520 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1521 &li.start_element[0], EL_PLAYER_1
1525 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1526 &li.use_artwork_element[0], FALSE
1530 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1531 &li.artwork_element[0], EL_PLAYER_1
1535 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1536 &li.use_explosion_element[0], FALSE
1540 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1541 &li.explosion_element[0], EL_PLAYER_1
1556 filetype_id_list[] =
1558 { LEVEL_FILE_TYPE_RND, "RND" },
1559 { LEVEL_FILE_TYPE_BD, "BD" },
1560 { LEVEL_FILE_TYPE_EM, "EM" },
1561 { LEVEL_FILE_TYPE_SP, "SP" },
1562 { LEVEL_FILE_TYPE_DX, "DX" },
1563 { LEVEL_FILE_TYPE_SB, "SB" },
1564 { LEVEL_FILE_TYPE_DC, "DC" },
1565 { LEVEL_FILE_TYPE_MM, "MM" },
1566 { LEVEL_FILE_TYPE_MM, "DF" },
1571 // ============================================================================
1572 // level file functions
1573 // ============================================================================
1575 static boolean check_special_flags(char *flag)
1577 if (strEqual(options.special_flags, flag) ||
1578 strEqual(leveldir_current->special_flags, flag))
1584 static struct DateInfo getCurrentDate(void)
1586 time_t epoch_seconds = time(NULL);
1587 struct tm *now = localtime(&epoch_seconds);
1588 struct DateInfo date;
1590 date.year = now->tm_year + 1900;
1591 date.month = now->tm_mon + 1;
1592 date.day = now->tm_mday;
1594 date.src = DATE_SRC_CLOCK;
1599 static void resetEventFlags(struct ElementChangeInfo *change)
1603 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1604 change->has_event[i] = FALSE;
1607 static void resetEventBits(void)
1611 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1612 xx_event_bits[i] = 0;
1615 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1619 /* important: only change event flag if corresponding event bit is set
1620 (this is because all xx_event_bits[] values are loaded separately,
1621 and all xx_event_bits[] values are set back to zero before loading
1622 another value xx_event_bits[x] (each value representing 32 flags)) */
1624 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1625 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1626 change->has_event[i] = TRUE;
1629 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1633 /* in contrast to the above function setEventFlagsFromEventBits(), it
1634 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1635 depending on the corresponding change->has_event[i] values here, as
1636 all xx_event_bits[] values are reset in resetEventBits() before */
1638 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1639 if (change->has_event[i])
1640 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1643 static char *getDefaultElementDescription(struct ElementInfo *ei)
1645 static char description[MAX_ELEMENT_NAME_LEN + 1];
1646 char *default_description = (ei->custom_description != NULL ?
1647 ei->custom_description :
1648 ei->editor_description);
1651 // always start with reliable default values
1652 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1653 description[i] = '\0';
1655 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1656 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1658 return &description[0];
1661 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1663 char *default_description = getDefaultElementDescription(ei);
1666 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1667 ei->description[i] = default_description[i];
1670 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1674 for (i = 0; conf[i].data_type != -1; i++)
1676 int default_value = conf[i].default_value;
1677 int data_type = conf[i].data_type;
1678 int conf_type = conf[i].conf_type;
1679 int byte_mask = conf_type & CONF_MASK_BYTES;
1681 if (byte_mask == CONF_MASK_MULTI_BYTES)
1683 int default_num_entities = conf[i].default_num_entities;
1684 int max_num_entities = conf[i].max_num_entities;
1686 *(int *)(conf[i].num_entities) = default_num_entities;
1688 if (data_type == TYPE_STRING)
1690 char *default_string = conf[i].default_string;
1691 char *string = (char *)(conf[i].value);
1693 strncpy(string, default_string, max_num_entities);
1695 else if (data_type == TYPE_ELEMENT_LIST)
1697 int *element_array = (int *)(conf[i].value);
1700 for (j = 0; j < max_num_entities; j++)
1701 element_array[j] = default_value;
1703 else if (data_type == TYPE_CONTENT_LIST)
1705 struct Content *content = (struct Content *)(conf[i].value);
1708 for (c = 0; c < max_num_entities; c++)
1709 for (y = 0; y < 3; y++)
1710 for (x = 0; x < 3; x++)
1711 content[c].e[x][y] = default_value;
1714 else // constant size configuration data (1, 2 or 4 bytes)
1716 if (data_type == TYPE_BOOLEAN)
1717 *(boolean *)(conf[i].value) = default_value;
1719 *(int *) (conf[i].value) = default_value;
1724 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1728 for (i = 0; conf[i].data_type != -1; i++)
1730 int data_type = conf[i].data_type;
1731 int conf_type = conf[i].conf_type;
1732 int byte_mask = conf_type & CONF_MASK_BYTES;
1734 if (byte_mask == CONF_MASK_MULTI_BYTES)
1736 int max_num_entities = conf[i].max_num_entities;
1738 if (data_type == TYPE_STRING)
1740 char *string = (char *)(conf[i].value);
1741 char *string_copy = (char *)(conf[i].value_copy);
1743 strncpy(string_copy, string, max_num_entities);
1745 else if (data_type == TYPE_ELEMENT_LIST)
1747 int *element_array = (int *)(conf[i].value);
1748 int *element_array_copy = (int *)(conf[i].value_copy);
1751 for (j = 0; j < max_num_entities; j++)
1752 element_array_copy[j] = element_array[j];
1754 else if (data_type == TYPE_CONTENT_LIST)
1756 struct Content *content = (struct Content *)(conf[i].value);
1757 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1760 for (c = 0; c < max_num_entities; c++)
1761 for (y = 0; y < 3; y++)
1762 for (x = 0; x < 3; x++)
1763 content_copy[c].e[x][y] = content[c].e[x][y];
1766 else // constant size configuration data (1, 2 or 4 bytes)
1768 if (data_type == TYPE_BOOLEAN)
1769 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1771 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1776 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1780 xx_ei = *ei_from; // copy element data into temporary buffer
1781 yy_ei = *ei_to; // copy element data into temporary buffer
1783 copyConfigFromConfigList(chunk_config_CUSX_base);
1788 // ---------- reinitialize and copy change pages ----------
1790 ei_to->num_change_pages = ei_from->num_change_pages;
1791 ei_to->current_change_page = ei_from->current_change_page;
1793 setElementChangePages(ei_to, ei_to->num_change_pages);
1795 for (i = 0; i < ei_to->num_change_pages; i++)
1796 ei_to->change_page[i] = ei_from->change_page[i];
1798 // ---------- copy group element info ----------
1799 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1800 *ei_to->group = *ei_from->group;
1802 // mark this custom element as modified
1803 ei_to->modified_settings = TRUE;
1806 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1808 int change_page_size = sizeof(struct ElementChangeInfo);
1810 ei->num_change_pages = MAX(1, change_pages);
1813 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1815 if (ei->current_change_page >= ei->num_change_pages)
1816 ei->current_change_page = ei->num_change_pages - 1;
1818 ei->change = &ei->change_page[ei->current_change_page];
1821 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1823 xx_change = *change; // copy change data into temporary buffer
1825 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1827 *change = xx_change;
1829 resetEventFlags(change);
1831 change->direct_action = 0;
1832 change->other_action = 0;
1834 change->pre_change_function = NULL;
1835 change->change_function = NULL;
1836 change->post_change_function = NULL;
1839 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1843 li = *level; // copy level data into temporary buffer
1844 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1845 *level = li; // copy temporary buffer back to level data
1847 setLevelInfoToDefaults_BD();
1848 setLevelInfoToDefaults_EM();
1849 setLevelInfoToDefaults_SP();
1850 setLevelInfoToDefaults_MM();
1852 level->native_bd_level = &native_bd_level;
1853 level->native_em_level = &native_em_level;
1854 level->native_sp_level = &native_sp_level;
1855 level->native_mm_level = &native_mm_level;
1857 level->file_version = FILE_VERSION_ACTUAL;
1858 level->game_version = GAME_VERSION_ACTUAL;
1860 level->creation_date = getCurrentDate();
1862 level->encoding_16bit_field = TRUE;
1863 level->encoding_16bit_yamyam = TRUE;
1864 level->encoding_16bit_amoeba = TRUE;
1866 // clear level name and level author string buffers
1867 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1868 level->name[i] = '\0';
1869 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1870 level->author[i] = '\0';
1872 // set level name and level author to default values
1873 strcpy(level->name, NAMELESS_LEVEL_NAME);
1874 strcpy(level->author, ANONYMOUS_NAME);
1876 // set level playfield to playable default level with player and exit
1877 for (x = 0; x < MAX_LEV_FIELDX; x++)
1878 for (y = 0; y < MAX_LEV_FIELDY; y++)
1879 level->field[x][y] = EL_SAND;
1881 level->field[0][0] = EL_PLAYER_1;
1882 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1884 BorderElement = EL_STEELWALL;
1886 // detect custom elements when loading them
1887 level->file_has_custom_elements = FALSE;
1889 // set all bug compatibility flags to "false" => do not emulate this bug
1890 level->use_action_after_change_bug = FALSE;
1892 if (leveldir_current)
1894 // try to determine better author name than 'anonymous'
1895 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1897 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1898 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1902 switch (LEVELCLASS(leveldir_current))
1904 case LEVELCLASS_TUTORIAL:
1905 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1908 case LEVELCLASS_CONTRIB:
1909 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1910 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1913 case LEVELCLASS_PRIVATE:
1914 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1915 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1919 // keep default value
1926 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1928 static boolean clipboard_elements_initialized = FALSE;
1931 InitElementPropertiesStatic();
1933 li = *level; // copy level data into temporary buffer
1934 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1935 *level = li; // copy temporary buffer back to level data
1937 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1940 struct ElementInfo *ei = &element_info[element];
1942 if (element == EL_MM_GRAY_BALL)
1944 struct LevelInfo_MM *level_mm = level->native_mm_level;
1947 for (j = 0; j < level->num_mm_ball_contents; j++)
1948 level->mm_ball_content[j] =
1949 map_element_MM_to_RND(level_mm->ball_content[j]);
1952 // never initialize clipboard elements after the very first time
1953 // (to be able to use clipboard elements between several levels)
1954 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1957 if (IS_ENVELOPE(element))
1959 int envelope_nr = element - EL_ENVELOPE_1;
1961 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1963 level->envelope[envelope_nr] = xx_envelope;
1966 if (IS_CUSTOM_ELEMENT(element) ||
1967 IS_GROUP_ELEMENT(element) ||
1968 IS_INTERNAL_ELEMENT(element))
1970 xx_ei = *ei; // copy element data into temporary buffer
1972 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1977 setElementChangePages(ei, 1);
1978 setElementChangeInfoToDefaults(ei->change);
1980 if (IS_CUSTOM_ELEMENT(element) ||
1981 IS_GROUP_ELEMENT(element))
1983 setElementDescriptionToDefault(ei);
1985 ei->modified_settings = FALSE;
1988 if (IS_CUSTOM_ELEMENT(element) ||
1989 IS_INTERNAL_ELEMENT(element))
1991 // internal values used in level editor
1993 ei->access_type = 0;
1994 ei->access_layer = 0;
1995 ei->access_protected = 0;
1996 ei->walk_to_action = 0;
1997 ei->smash_targets = 0;
2000 ei->can_explode_by_fire = FALSE;
2001 ei->can_explode_smashed = FALSE;
2002 ei->can_explode_impact = FALSE;
2004 ei->current_change_page = 0;
2007 if (IS_GROUP_ELEMENT(element) ||
2008 IS_INTERNAL_ELEMENT(element))
2010 struct ElementGroupInfo *group;
2012 // initialize memory for list of elements in group
2013 if (ei->group == NULL)
2014 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2018 xx_group = *group; // copy group data into temporary buffer
2020 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2025 if (IS_EMPTY_ELEMENT(element) ||
2026 IS_INTERNAL_ELEMENT(element))
2028 xx_ei = *ei; // copy element data into temporary buffer
2030 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2036 clipboard_elements_initialized = TRUE;
2039 static void setLevelInfoToDefaults(struct LevelInfo *level,
2040 boolean level_info_only,
2041 boolean reset_file_status)
2043 setLevelInfoToDefaults_Level(level);
2045 if (!level_info_only)
2046 setLevelInfoToDefaults_Elements(level);
2048 if (reset_file_status)
2050 level->no_valid_file = FALSE;
2051 level->no_level_file = FALSE;
2054 level->changed = FALSE;
2057 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2059 level_file_info->nr = 0;
2060 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2061 level_file_info->packed = FALSE;
2063 setString(&level_file_info->basename, NULL);
2064 setString(&level_file_info->filename, NULL);
2067 int getMappedElement_SB(int, boolean);
2069 static void ActivateLevelTemplate(void)
2073 if (check_special_flags("load_xsb_to_ces"))
2075 // fill smaller playfields with padding "beyond border wall" elements
2076 if (level.fieldx < level_template.fieldx ||
2077 level.fieldy < level_template.fieldy)
2079 short field[level.fieldx][level.fieldy];
2080 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2081 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2082 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2083 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2085 // copy old playfield (which is smaller than the visible area)
2086 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2087 field[x][y] = level.field[x][y];
2089 // fill new, larger playfield with "beyond border wall" elements
2090 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2091 level.field[x][y] = getMappedElement_SB('_', TRUE);
2093 // copy the old playfield to the middle of the new playfield
2094 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2095 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2097 level.fieldx = new_fieldx;
2098 level.fieldy = new_fieldy;
2102 // Currently there is no special action needed to activate the template
2103 // data, because 'element_info' property settings overwrite the original
2104 // level data, while all other variables do not change.
2106 // Exception: 'from_level_template' elements in the original level playfield
2107 // are overwritten with the corresponding elements at the same position in
2108 // playfield from the level template.
2110 for (x = 0; x < level.fieldx; x++)
2111 for (y = 0; y < level.fieldy; y++)
2112 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2113 level.field[x][y] = level_template.field[x][y];
2115 if (check_special_flags("load_xsb_to_ces"))
2117 struct LevelInfo level_backup = level;
2119 // overwrite all individual level settings from template level settings
2120 level = level_template;
2122 // restore level file info
2123 level.file_info = level_backup.file_info;
2125 // restore playfield size
2126 level.fieldx = level_backup.fieldx;
2127 level.fieldy = level_backup.fieldy;
2129 // restore playfield content
2130 for (x = 0; x < level.fieldx; x++)
2131 for (y = 0; y < level.fieldy; y++)
2132 level.field[x][y] = level_backup.field[x][y];
2134 // restore name and author from individual level
2135 strcpy(level.name, level_backup.name);
2136 strcpy(level.author, level_backup.author);
2138 // restore flag "use_custom_template"
2139 level.use_custom_template = level_backup.use_custom_template;
2143 static boolean checkForPackageFromBasename_BD(char *basename)
2145 // check for native BD level file extensions
2146 if (!strSuffixLower(basename, ".bd") &&
2147 !strSuffixLower(basename, ".bdr") &&
2148 !strSuffixLower(basename, ".brc") &&
2149 !strSuffixLower(basename, ".gds"))
2152 // check for standard single-level BD files (like "001.bd")
2153 if (strSuffixLower(basename, ".bd") &&
2154 strlen(basename) == 6 &&
2155 basename[0] >= '0' && basename[0] <= '9' &&
2156 basename[1] >= '0' && basename[1] <= '9' &&
2157 basename[2] >= '0' && basename[2] <= '9')
2160 // this is a level package in native BD file format
2164 static char *getLevelFilenameFromBasename(char *basename)
2166 static char *filename = NULL;
2168 checked_free(filename);
2170 filename = getPath2(getCurrentLevelDir(), basename);
2175 static int getFileTypeFromBasename(char *basename)
2177 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2179 static char *filename = NULL;
2180 struct stat file_status;
2182 // ---------- try to determine file type from filename ----------
2184 // check for typical filename of a Supaplex level package file
2185 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2186 return LEVEL_FILE_TYPE_SP;
2188 // check for typical filename of a Diamond Caves II level package file
2189 if (strSuffixLower(basename, ".dc") ||
2190 strSuffixLower(basename, ".dc2"))
2191 return LEVEL_FILE_TYPE_DC;
2193 // check for typical filename of a Sokoban level package file
2194 if (strSuffixLower(basename, ".xsb") &&
2195 strchr(basename, '%') == NULL)
2196 return LEVEL_FILE_TYPE_SB;
2198 // check for typical filename of a Boulder Dash (GDash) level package file
2199 if (checkForPackageFromBasename_BD(basename))
2200 return LEVEL_FILE_TYPE_BD;
2202 // ---------- try to determine file type from filesize ----------
2204 checked_free(filename);
2205 filename = getPath2(getCurrentLevelDir(), basename);
2207 if (stat(filename, &file_status) == 0)
2209 // check for typical filesize of a Supaplex level package file
2210 if (file_status.st_size == 170496)
2211 return LEVEL_FILE_TYPE_SP;
2214 return LEVEL_FILE_TYPE_UNKNOWN;
2217 static int getFileTypeFromMagicBytes(char *filename, int type)
2221 if ((file = openFile(filename, MODE_READ)))
2223 char chunk_name[CHUNK_ID_LEN + 1];
2225 getFileChunkBE(file, chunk_name, NULL);
2227 if (strEqual(chunk_name, "MMII") ||
2228 strEqual(chunk_name, "MIRR"))
2229 type = LEVEL_FILE_TYPE_MM;
2237 static boolean checkForPackageFromBasename(char *basename)
2239 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2240 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2242 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2245 static char *getSingleLevelBasenameExt(int nr, char *extension)
2247 static char basename[MAX_FILENAME_LEN];
2250 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2252 sprintf(basename, "%03d.%s", nr, extension);
2257 static char *getSingleLevelBasename(int nr)
2259 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2262 static char *getPackedLevelBasename(int type)
2264 static char basename[MAX_FILENAME_LEN];
2265 char *directory = getCurrentLevelDir();
2267 DirectoryEntry *dir_entry;
2269 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2271 if ((dir = openDirectory(directory)) == NULL)
2273 Warn("cannot read current level directory '%s'", directory);
2278 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2280 char *entry_basename = dir_entry->basename;
2281 int entry_type = getFileTypeFromBasename(entry_basename);
2283 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2285 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2288 strcpy(basename, entry_basename);
2295 closeDirectory(dir);
2300 static char *getSingleLevelFilename(int nr)
2302 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2305 #if ENABLE_UNUSED_CODE
2306 static char *getPackedLevelFilename(int type)
2308 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2312 char *getDefaultLevelFilename(int nr)
2314 return getSingleLevelFilename(nr);
2317 #if ENABLE_UNUSED_CODE
2318 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2322 lfi->packed = FALSE;
2324 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2325 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2329 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2330 int type, char *format, ...)
2332 static char basename[MAX_FILENAME_LEN];
2335 va_start(ap, format);
2336 vsprintf(basename, format, ap);
2340 lfi->packed = FALSE;
2342 setString(&lfi->basename, basename);
2343 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2346 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2352 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2353 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2356 static int getFiletypeFromID(char *filetype_id)
2358 char *filetype_id_lower;
2359 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2362 if (filetype_id == NULL)
2363 return LEVEL_FILE_TYPE_UNKNOWN;
2365 filetype_id_lower = getStringToLower(filetype_id);
2367 for (i = 0; filetype_id_list[i].id != NULL; i++)
2369 char *id_lower = getStringToLower(filetype_id_list[i].id);
2371 if (strEqual(filetype_id_lower, id_lower))
2372 filetype = filetype_id_list[i].filetype;
2376 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2380 free(filetype_id_lower);
2385 char *getLocalLevelTemplateFilename(void)
2387 return getDefaultLevelFilename(-1);
2390 char *getGlobalLevelTemplateFilename(void)
2392 // global variable "leveldir_current" must be modified in the loop below
2393 LevelDirTree *leveldir_current_last = leveldir_current;
2394 char *filename = NULL;
2396 // check for template level in path from current to topmost tree node
2398 while (leveldir_current != NULL)
2400 filename = getDefaultLevelFilename(-1);
2402 if (fileExists(filename))
2405 leveldir_current = leveldir_current->node_parent;
2408 // restore global variable "leveldir_current" modified in above loop
2409 leveldir_current = leveldir_current_last;
2414 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2418 // special case: level number is negative => check for level template file
2421 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2422 getSingleLevelBasename(-1));
2424 // replace local level template filename with global template filename
2425 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2427 // no fallback if template file not existing
2431 // special case: check for file name/pattern specified in "levelinfo.conf"
2432 if (leveldir_current->level_filename != NULL)
2434 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2436 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2437 leveldir_current->level_filename, nr);
2439 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2441 if (fileExists(lfi->filename))
2444 else if (leveldir_current->level_filetype != NULL)
2446 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2448 // check for specified native level file with standard file name
2449 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2450 "%03d.%s", nr, LEVELFILE_EXTENSION);
2451 if (fileExists(lfi->filename))
2455 // check for native Rocks'n'Diamonds level file
2456 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2457 "%03d.%s", nr, LEVELFILE_EXTENSION);
2458 if (fileExists(lfi->filename))
2461 // check for native Boulder Dash level file
2462 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2463 if (fileExists(lfi->filename))
2466 // check for Emerald Mine level file (V1)
2467 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2468 'a' + (nr / 10) % 26, '0' + nr % 10);
2469 if (fileExists(lfi->filename))
2471 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2472 'A' + (nr / 10) % 26, '0' + nr % 10);
2473 if (fileExists(lfi->filename))
2476 // check for Emerald Mine level file (V2 to V5)
2477 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2478 if (fileExists(lfi->filename))
2481 // check for Emerald Mine level file (V6 / single mode)
2482 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2483 if (fileExists(lfi->filename))
2485 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2486 if (fileExists(lfi->filename))
2489 // check for Emerald Mine level file (V6 / teamwork mode)
2490 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2491 if (fileExists(lfi->filename))
2493 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2494 if (fileExists(lfi->filename))
2497 // check for various packed level file formats
2498 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2499 if (fileExists(lfi->filename))
2502 // no known level file found -- use default values (and fail later)
2503 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2504 "%03d.%s", nr, LEVELFILE_EXTENSION);
2507 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2509 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2510 lfi->type = getFileTypeFromBasename(lfi->basename);
2512 if (lfi->type == LEVEL_FILE_TYPE_RND)
2513 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2516 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2518 // always start with reliable default values
2519 setFileInfoToDefaults(level_file_info);
2521 level_file_info->nr = nr; // set requested level number
2523 determineLevelFileInfo_Filename(level_file_info);
2524 determineLevelFileInfo_Filetype(level_file_info);
2527 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2528 struct LevelFileInfo *lfi_to)
2530 lfi_to->nr = lfi_from->nr;
2531 lfi_to->type = lfi_from->type;
2532 lfi_to->packed = lfi_from->packed;
2534 setString(&lfi_to->basename, lfi_from->basename);
2535 setString(&lfi_to->filename, lfi_from->filename);
2538 // ----------------------------------------------------------------------------
2539 // functions for loading R'n'D level
2540 // ----------------------------------------------------------------------------
2542 int getMappedElement(int element)
2544 // remap some (historic, now obsolete) elements
2548 case EL_PLAYER_OBSOLETE:
2549 element = EL_PLAYER_1;
2552 case EL_KEY_OBSOLETE:
2556 case EL_EM_KEY_1_FILE_OBSOLETE:
2557 element = EL_EM_KEY_1;
2560 case EL_EM_KEY_2_FILE_OBSOLETE:
2561 element = EL_EM_KEY_2;
2564 case EL_EM_KEY_3_FILE_OBSOLETE:
2565 element = EL_EM_KEY_3;
2568 case EL_EM_KEY_4_FILE_OBSOLETE:
2569 element = EL_EM_KEY_4;
2572 case EL_ENVELOPE_OBSOLETE:
2573 element = EL_ENVELOPE_1;
2581 if (element >= NUM_FILE_ELEMENTS)
2583 Warn("invalid level element %d", element);
2585 element = EL_UNKNOWN;
2593 static int getMappedElementByVersion(int element, int game_version)
2595 // remap some elements due to certain game version
2597 if (game_version <= VERSION_IDENT(2,2,0,0))
2599 // map game font elements
2600 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2601 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2602 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2603 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2606 if (game_version < VERSION_IDENT(3,0,0,0))
2608 // map Supaplex gravity tube elements
2609 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2610 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2611 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2612 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2619 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2621 level->file_version = getFileVersion(file);
2622 level->game_version = getFileVersion(file);
2627 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2629 level->creation_date.year = getFile16BitBE(file);
2630 level->creation_date.month = getFile8Bit(file);
2631 level->creation_date.day = getFile8Bit(file);
2633 level->creation_date.src = DATE_SRC_LEVELFILE;
2638 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2640 int initial_player_stepsize;
2641 int initial_player_gravity;
2644 level->fieldx = getFile8Bit(file);
2645 level->fieldy = getFile8Bit(file);
2647 level->time = getFile16BitBE(file);
2648 level->gems_needed = getFile16BitBE(file);
2650 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2651 level->name[i] = getFile8Bit(file);
2652 level->name[MAX_LEVEL_NAME_LEN] = 0;
2654 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2655 level->score[i] = getFile8Bit(file);
2657 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2658 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2659 for (y = 0; y < 3; y++)
2660 for (x = 0; x < 3; x++)
2661 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2663 level->amoeba_speed = getFile8Bit(file);
2664 level->time_magic_wall = getFile8Bit(file);
2665 level->time_wheel = getFile8Bit(file);
2666 level->amoeba_content = getMappedElement(getFile8Bit(file));
2668 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2671 for (i = 0; i < MAX_PLAYERS; i++)
2672 level->initial_player_stepsize[i] = initial_player_stepsize;
2674 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2676 for (i = 0; i < MAX_PLAYERS; i++)
2677 level->initial_player_gravity[i] = initial_player_gravity;
2679 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2680 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2682 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2684 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2685 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2686 level->can_move_into_acid_bits = getFile32BitBE(file);
2687 level->dont_collide_with_bits = getFile8Bit(file);
2689 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2690 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2692 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2693 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2694 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2696 level->game_engine_type = getFile8Bit(file);
2698 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2703 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2707 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2708 level->name[i] = getFile8Bit(file);
2709 level->name[MAX_LEVEL_NAME_LEN] = 0;
2714 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2718 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2719 level->author[i] = getFile8Bit(file);
2720 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2725 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2728 int chunk_size_expected = level->fieldx * level->fieldy;
2730 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2731 stored with 16-bit encoding (and should be twice as big then).
2732 Even worse, playfield data was stored 16-bit when only yamyam content
2733 contained 16-bit elements and vice versa. */
2735 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2736 chunk_size_expected *= 2;
2738 if (chunk_size_expected != chunk_size)
2740 ReadUnusedBytesFromFile(file, chunk_size);
2741 return chunk_size_expected;
2744 for (y = 0; y < level->fieldy; y++)
2745 for (x = 0; x < level->fieldx; x++)
2746 level->field[x][y] =
2747 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2752 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2755 int header_size = 4;
2756 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2757 int chunk_size_expected = header_size + content_size;
2759 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2760 stored with 16-bit encoding (and should be twice as big then).
2761 Even worse, playfield data was stored 16-bit when only yamyam content
2762 contained 16-bit elements and vice versa. */
2764 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2765 chunk_size_expected += content_size;
2767 if (chunk_size_expected != chunk_size)
2769 ReadUnusedBytesFromFile(file, chunk_size);
2770 return chunk_size_expected;
2774 level->num_yamyam_contents = getFile8Bit(file);
2778 // correct invalid number of content fields -- should never happen
2779 if (level->num_yamyam_contents < 1 ||
2780 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2781 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2783 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2784 for (y = 0; y < 3; y++)
2785 for (x = 0; x < 3; x++)
2786 level->yamyam_content[i].e[x][y] =
2787 getMappedElement(level->encoding_16bit_field ?
2788 getFile16BitBE(file) : getFile8Bit(file));
2792 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2797 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2799 element = getMappedElement(getFile16BitBE(file));
2800 num_contents = getFile8Bit(file);
2802 getFile8Bit(file); // content x size (unused)
2803 getFile8Bit(file); // content y size (unused)
2805 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2807 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2808 for (y = 0; y < 3; y++)
2809 for (x = 0; x < 3; x++)
2810 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2812 // correct invalid number of content fields -- should never happen
2813 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2814 num_contents = STD_ELEMENT_CONTENTS;
2816 if (element == EL_YAMYAM)
2818 level->num_yamyam_contents = num_contents;
2820 for (i = 0; i < num_contents; i++)
2821 for (y = 0; y < 3; y++)
2822 for (x = 0; x < 3; x++)
2823 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2825 else if (element == EL_BD_AMOEBA)
2827 level->amoeba_content = content_array[0][0][0];
2831 Warn("cannot load content for element '%d'", element);
2837 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2843 int chunk_size_expected;
2845 element = getMappedElement(getFile16BitBE(file));
2846 if (!IS_ENVELOPE(element))
2847 element = EL_ENVELOPE_1;
2849 envelope_nr = element - EL_ENVELOPE_1;
2851 envelope_len = getFile16BitBE(file);
2853 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2854 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2856 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2858 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2859 if (chunk_size_expected != chunk_size)
2861 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2862 return chunk_size_expected;
2865 for (i = 0; i < envelope_len; i++)
2866 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2871 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2873 int num_changed_custom_elements = getFile16BitBE(file);
2874 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2877 if (chunk_size_expected != chunk_size)
2879 ReadUnusedBytesFromFile(file, chunk_size - 2);
2880 return chunk_size_expected;
2883 for (i = 0; i < num_changed_custom_elements; i++)
2885 int element = getMappedElement(getFile16BitBE(file));
2886 int properties = getFile32BitBE(file);
2888 if (IS_CUSTOM_ELEMENT(element))
2889 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2891 Warn("invalid custom element number %d", element);
2893 // older game versions that wrote level files with CUS1 chunks used
2894 // different default push delay values (not yet stored in level file)
2895 element_info[element].push_delay_fixed = 2;
2896 element_info[element].push_delay_random = 8;
2899 level->file_has_custom_elements = TRUE;
2904 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2906 int num_changed_custom_elements = getFile16BitBE(file);
2907 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2910 if (chunk_size_expected != chunk_size)
2912 ReadUnusedBytesFromFile(file, chunk_size - 2);
2913 return chunk_size_expected;
2916 for (i = 0; i < num_changed_custom_elements; i++)
2918 int element = getMappedElement(getFile16BitBE(file));
2919 int custom_target_element = getMappedElement(getFile16BitBE(file));
2921 if (IS_CUSTOM_ELEMENT(element))
2922 element_info[element].change->target_element = custom_target_element;
2924 Warn("invalid custom element number %d", element);
2927 level->file_has_custom_elements = TRUE;
2932 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2934 int num_changed_custom_elements = getFile16BitBE(file);
2935 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2938 if (chunk_size_expected != chunk_size)
2940 ReadUnusedBytesFromFile(file, chunk_size - 2);
2941 return chunk_size_expected;
2944 for (i = 0; i < num_changed_custom_elements; i++)
2946 int element = getMappedElement(getFile16BitBE(file));
2947 struct ElementInfo *ei = &element_info[element];
2948 unsigned int event_bits;
2950 if (!IS_CUSTOM_ELEMENT(element))
2952 Warn("invalid custom element number %d", element);
2954 element = EL_INTERNAL_DUMMY;
2957 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2958 ei->description[j] = getFile8Bit(file);
2959 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2961 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2963 // some free bytes for future properties and padding
2964 ReadUnusedBytesFromFile(file, 7);
2966 ei->use_gfx_element = getFile8Bit(file);
2967 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2969 ei->collect_score_initial = getFile8Bit(file);
2970 ei->collect_count_initial = getFile8Bit(file);
2972 ei->push_delay_fixed = getFile16BitBE(file);
2973 ei->push_delay_random = getFile16BitBE(file);
2974 ei->move_delay_fixed = getFile16BitBE(file);
2975 ei->move_delay_random = getFile16BitBE(file);
2977 ei->move_pattern = getFile16BitBE(file);
2978 ei->move_direction_initial = getFile8Bit(file);
2979 ei->move_stepsize = getFile8Bit(file);
2981 for (y = 0; y < 3; y++)
2982 for (x = 0; x < 3; x++)
2983 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2985 // bits 0 - 31 of "has_event[]"
2986 event_bits = getFile32BitBE(file);
2987 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2988 if (event_bits & (1u << j))
2989 ei->change->has_event[j] = TRUE;
2991 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2993 ei->change->delay_fixed = getFile16BitBE(file);
2994 ei->change->delay_random = getFile16BitBE(file);
2995 ei->change->delay_frames = getFile16BitBE(file);
2997 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2999 ei->change->explode = getFile8Bit(file);
3000 ei->change->use_target_content = getFile8Bit(file);
3001 ei->change->only_if_complete = getFile8Bit(file);
3002 ei->change->use_random_replace = getFile8Bit(file);
3004 ei->change->random_percentage = getFile8Bit(file);
3005 ei->change->replace_when = getFile8Bit(file);
3007 for (y = 0; y < 3; y++)
3008 for (x = 0; x < 3; x++)
3009 ei->change->target_content.e[x][y] =
3010 getMappedElement(getFile16BitBE(file));
3012 ei->slippery_type = getFile8Bit(file);
3014 // some free bytes for future properties and padding
3015 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3017 // mark that this custom element has been modified
3018 ei->modified_settings = TRUE;
3021 level->file_has_custom_elements = TRUE;
3026 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3028 struct ElementInfo *ei;
3029 int chunk_size_expected;
3033 // ---------- custom element base property values (96 bytes) ----------------
3035 element = getMappedElement(getFile16BitBE(file));
3037 if (!IS_CUSTOM_ELEMENT(element))
3039 Warn("invalid custom element number %d", element);
3041 ReadUnusedBytesFromFile(file, chunk_size - 2);
3046 ei = &element_info[element];
3048 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3049 ei->description[i] = getFile8Bit(file);
3050 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3052 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3054 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3056 ei->num_change_pages = getFile8Bit(file);
3058 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3059 if (chunk_size_expected != chunk_size)
3061 ReadUnusedBytesFromFile(file, chunk_size - 43);
3062 return chunk_size_expected;
3065 ei->ce_value_fixed_initial = getFile16BitBE(file);
3066 ei->ce_value_random_initial = getFile16BitBE(file);
3067 ei->use_last_ce_value = getFile8Bit(file);
3069 ei->use_gfx_element = getFile8Bit(file);
3070 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3072 ei->collect_score_initial = getFile8Bit(file);
3073 ei->collect_count_initial = getFile8Bit(file);
3075 ei->drop_delay_fixed = getFile8Bit(file);
3076 ei->push_delay_fixed = getFile8Bit(file);
3077 ei->drop_delay_random = getFile8Bit(file);
3078 ei->push_delay_random = getFile8Bit(file);
3079 ei->move_delay_fixed = getFile16BitBE(file);
3080 ei->move_delay_random = getFile16BitBE(file);
3082 // bits 0 - 15 of "move_pattern" ...
3083 ei->move_pattern = getFile16BitBE(file);
3084 ei->move_direction_initial = getFile8Bit(file);
3085 ei->move_stepsize = getFile8Bit(file);
3087 ei->slippery_type = getFile8Bit(file);
3089 for (y = 0; y < 3; y++)
3090 for (x = 0; x < 3; x++)
3091 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3093 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3094 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3095 ei->move_leave_type = getFile8Bit(file);
3097 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3098 ei->move_pattern |= (getFile16BitBE(file) << 16);
3100 ei->access_direction = getFile8Bit(file);
3102 ei->explosion_delay = getFile8Bit(file);
3103 ei->ignition_delay = getFile8Bit(file);
3104 ei->explosion_type = getFile8Bit(file);
3106 // some free bytes for future custom property values and padding
3107 ReadUnusedBytesFromFile(file, 1);
3109 // ---------- change page property values (48 bytes) ------------------------
3111 setElementChangePages(ei, ei->num_change_pages);
3113 for (i = 0; i < ei->num_change_pages; i++)
3115 struct ElementChangeInfo *change = &ei->change_page[i];
3116 unsigned int event_bits;
3118 // always start with reliable default values
3119 setElementChangeInfoToDefaults(change);
3121 // bits 0 - 31 of "has_event[]" ...
3122 event_bits = getFile32BitBE(file);
3123 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3124 if (event_bits & (1u << j))
3125 change->has_event[j] = TRUE;
3127 change->target_element = getMappedElement(getFile16BitBE(file));
3129 change->delay_fixed = getFile16BitBE(file);
3130 change->delay_random = getFile16BitBE(file);
3131 change->delay_frames = getFile16BitBE(file);
3133 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3135 change->explode = getFile8Bit(file);
3136 change->use_target_content = getFile8Bit(file);
3137 change->only_if_complete = getFile8Bit(file);
3138 change->use_random_replace = getFile8Bit(file);
3140 change->random_percentage = getFile8Bit(file);
3141 change->replace_when = getFile8Bit(file);
3143 for (y = 0; y < 3; y++)
3144 for (x = 0; x < 3; x++)
3145 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3147 change->can_change = getFile8Bit(file);
3149 change->trigger_side = getFile8Bit(file);
3151 change->trigger_player = getFile8Bit(file);
3152 change->trigger_page = getFile8Bit(file);
3154 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3155 CH_PAGE_ANY : (1 << change->trigger_page));
3157 change->has_action = getFile8Bit(file);
3158 change->action_type = getFile8Bit(file);
3159 change->action_mode = getFile8Bit(file);
3160 change->action_arg = getFile16BitBE(file);
3162 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3163 event_bits = getFile8Bit(file);
3164 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3165 if (event_bits & (1u << (j - 32)))
3166 change->has_event[j] = TRUE;
3169 // mark this custom element as modified
3170 ei->modified_settings = TRUE;
3172 level->file_has_custom_elements = TRUE;
3177 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3179 struct ElementInfo *ei;
3180 struct ElementGroupInfo *group;
3184 element = getMappedElement(getFile16BitBE(file));
3186 if (!IS_GROUP_ELEMENT(element))
3188 Warn("invalid group element number %d", element);
3190 ReadUnusedBytesFromFile(file, chunk_size - 2);
3195 ei = &element_info[element];
3197 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3198 ei->description[i] = getFile8Bit(file);
3199 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3201 group = element_info[element].group;
3203 group->num_elements = getFile8Bit(file);
3205 ei->use_gfx_element = getFile8Bit(file);
3206 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3208 group->choice_mode = getFile8Bit(file);
3210 // some free bytes for future values and padding
3211 ReadUnusedBytesFromFile(file, 3);
3213 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3214 group->element[i] = getMappedElement(getFile16BitBE(file));
3216 // mark this group element as modified
3217 element_info[element].modified_settings = TRUE;
3219 level->file_has_custom_elements = TRUE;
3224 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3225 int element, int real_element)
3227 int micro_chunk_size = 0;
3228 int conf_type = getFile8Bit(file);
3229 int byte_mask = conf_type & CONF_MASK_BYTES;
3230 boolean element_found = FALSE;
3233 micro_chunk_size += 1;
3235 if (byte_mask == CONF_MASK_MULTI_BYTES)
3237 int num_bytes = getFile16BitBE(file);
3238 byte *buffer = checked_malloc(num_bytes);
3240 ReadBytesFromFile(file, buffer, num_bytes);
3242 for (i = 0; conf[i].data_type != -1; i++)
3244 if (conf[i].element == element &&
3245 conf[i].conf_type == conf_type)
3247 int data_type = conf[i].data_type;
3248 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3249 int max_num_entities = conf[i].max_num_entities;
3251 if (num_entities > max_num_entities)
3253 Warn("truncating number of entities for element %d from %d to %d",
3254 element, num_entities, max_num_entities);
3256 num_entities = max_num_entities;
3259 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3260 data_type == TYPE_CONTENT_LIST))
3262 // for element and content lists, zero entities are not allowed
3263 Warn("found empty list of entities for element %d", element);
3265 // do not set "num_entities" here to prevent reading behind buffer
3267 *(int *)(conf[i].num_entities) = 1; // at least one is required
3271 *(int *)(conf[i].num_entities) = num_entities;
3274 element_found = TRUE;
3276 if (data_type == TYPE_STRING)
3278 char *string = (char *)(conf[i].value);
3281 for (j = 0; j < max_num_entities; j++)
3282 string[j] = (j < num_entities ? buffer[j] : '\0');
3284 else if (data_type == TYPE_ELEMENT_LIST)
3286 int *element_array = (int *)(conf[i].value);
3289 for (j = 0; j < num_entities; j++)
3291 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3293 else if (data_type == TYPE_CONTENT_LIST)
3295 struct Content *content= (struct Content *)(conf[i].value);
3298 for (c = 0; c < num_entities; c++)
3299 for (y = 0; y < 3; y++)
3300 for (x = 0; x < 3; x++)
3301 content[c].e[x][y] =
3302 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3305 element_found = FALSE;
3311 checked_free(buffer);
3313 micro_chunk_size += 2 + num_bytes;
3315 else // constant size configuration data (1, 2 or 4 bytes)
3317 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3318 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3319 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3321 for (i = 0; conf[i].data_type != -1; i++)
3323 if (conf[i].element == element &&
3324 conf[i].conf_type == conf_type)
3326 int data_type = conf[i].data_type;
3328 if (data_type == TYPE_ELEMENT)
3329 value = getMappedElement(value);
3331 if (data_type == TYPE_BOOLEAN)
3332 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3334 *(int *) (conf[i].value) = value;
3336 element_found = TRUE;
3342 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3347 char *error_conf_chunk_bytes =
3348 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3349 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3350 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3351 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3352 int error_element = real_element;
3354 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3355 error_conf_chunk_bytes, error_conf_chunk_token,
3356 error_element, EL_NAME(error_element));
3359 return micro_chunk_size;
3362 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3364 int real_chunk_size = 0;
3366 li = *level; // copy level data into temporary buffer
3368 while (!checkEndOfFile(file))
3370 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3372 if (real_chunk_size >= chunk_size)
3376 *level = li; // copy temporary buffer back to level data
3378 return real_chunk_size;
3381 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3383 int real_chunk_size = 0;
3385 li = *level; // copy level data into temporary buffer
3387 while (!checkEndOfFile(file))
3389 int element = getMappedElement(getFile16BitBE(file));
3391 real_chunk_size += 2;
3392 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3394 if (real_chunk_size >= chunk_size)
3398 *level = li; // copy temporary buffer back to level data
3400 return real_chunk_size;
3403 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3405 int real_chunk_size = 0;
3407 li = *level; // copy level data into temporary buffer
3409 while (!checkEndOfFile(file))
3411 int element = getMappedElement(getFile16BitBE(file));
3413 real_chunk_size += 2;
3414 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3416 if (real_chunk_size >= chunk_size)
3420 *level = li; // copy temporary buffer back to level data
3422 return real_chunk_size;
3425 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3427 int element = getMappedElement(getFile16BitBE(file));
3428 int envelope_nr = element - EL_ENVELOPE_1;
3429 int real_chunk_size = 2;
3431 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3433 while (!checkEndOfFile(file))
3435 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3438 if (real_chunk_size >= chunk_size)
3442 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3444 return real_chunk_size;
3447 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3449 int element = getMappedElement(getFile16BitBE(file));
3450 int real_chunk_size = 2;
3451 struct ElementInfo *ei = &element_info[element];
3454 xx_ei = *ei; // copy element data into temporary buffer
3456 xx_ei.num_change_pages = -1;
3458 while (!checkEndOfFile(file))
3460 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3462 if (xx_ei.num_change_pages != -1)
3465 if (real_chunk_size >= chunk_size)
3471 if (ei->num_change_pages == -1)
3473 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3476 ei->num_change_pages = 1;
3478 setElementChangePages(ei, 1);
3479 setElementChangeInfoToDefaults(ei->change);
3481 return real_chunk_size;
3484 // initialize number of change pages stored for this custom element
3485 setElementChangePages(ei, ei->num_change_pages);
3486 for (i = 0; i < ei->num_change_pages; i++)
3487 setElementChangeInfoToDefaults(&ei->change_page[i]);
3489 // start with reading properties for the first change page
3490 xx_current_change_page = 0;
3492 while (!checkEndOfFile(file))
3494 // level file might contain invalid change page number
3495 if (xx_current_change_page >= ei->num_change_pages)
3498 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3500 xx_change = *change; // copy change data into temporary buffer
3502 resetEventBits(); // reset bits; change page might have changed
3504 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3507 *change = xx_change;
3509 setEventFlagsFromEventBits(change);
3511 if (real_chunk_size >= chunk_size)
3515 level->file_has_custom_elements = TRUE;
3517 return real_chunk_size;
3520 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3522 int element = getMappedElement(getFile16BitBE(file));
3523 int real_chunk_size = 2;
3524 struct ElementInfo *ei = &element_info[element];
3525 struct ElementGroupInfo *group = ei->group;
3530 xx_ei = *ei; // copy element data into temporary buffer
3531 xx_group = *group; // copy group data into temporary buffer
3533 while (!checkEndOfFile(file))
3535 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3538 if (real_chunk_size >= chunk_size)
3545 level->file_has_custom_elements = TRUE;
3547 return real_chunk_size;
3550 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3552 int element = getMappedElement(getFile16BitBE(file));
3553 int real_chunk_size = 2;
3554 struct ElementInfo *ei = &element_info[element];
3556 xx_ei = *ei; // copy element data into temporary buffer
3558 while (!checkEndOfFile(file))
3560 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3563 if (real_chunk_size >= chunk_size)
3569 level->file_has_custom_elements = TRUE;
3571 return real_chunk_size;
3574 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3575 struct LevelFileInfo *level_file_info,
3576 boolean level_info_only)
3578 char *filename = level_file_info->filename;
3579 char cookie[MAX_LINE_LEN];
3580 char chunk_name[CHUNK_ID_LEN + 1];
3584 if (!(file = openFile(filename, MODE_READ)))
3586 level->no_valid_file = TRUE;
3587 level->no_level_file = TRUE;
3589 if (level_info_only)
3592 Warn("cannot read level '%s' -- using empty level", filename);
3594 if (!setup.editor.use_template_for_new_levels)
3597 // if level file not found, try to initialize level data from template
3598 filename = getGlobalLevelTemplateFilename();
3600 if (!(file = openFile(filename, MODE_READ)))
3603 // default: for empty levels, use level template for custom elements
3604 level->use_custom_template = TRUE;
3606 level->no_valid_file = FALSE;
3609 getFileChunkBE(file, chunk_name, NULL);
3610 if (strEqual(chunk_name, "RND1"))
3612 getFile32BitBE(file); // not used
3614 getFileChunkBE(file, chunk_name, NULL);
3615 if (!strEqual(chunk_name, "CAVE"))
3617 level->no_valid_file = TRUE;
3619 Warn("unknown format of level file '%s'", filename);
3626 else // check for pre-2.0 file format with cookie string
3628 strcpy(cookie, chunk_name);
3629 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3631 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3632 cookie[strlen(cookie) - 1] = '\0';
3634 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3636 level->no_valid_file = TRUE;
3638 Warn("unknown format of level file '%s'", filename);
3645 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3647 level->no_valid_file = TRUE;
3649 Warn("unsupported version of level file '%s'", filename);
3656 // pre-2.0 level files have no game version, so use file version here
3657 level->game_version = level->file_version;
3660 if (level->file_version < FILE_VERSION_1_2)
3662 // level files from versions before 1.2.0 without chunk structure
3663 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3664 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3672 int (*loader)(File *, int, struct LevelInfo *);
3676 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3677 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3678 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3679 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3680 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3681 { "INFO", -1, LoadLevel_INFO },
3682 { "BODY", -1, LoadLevel_BODY },
3683 { "CONT", -1, LoadLevel_CONT },
3684 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3685 { "CNT3", -1, LoadLevel_CNT3 },
3686 { "CUS1", -1, LoadLevel_CUS1 },
3687 { "CUS2", -1, LoadLevel_CUS2 },
3688 { "CUS3", -1, LoadLevel_CUS3 },
3689 { "CUS4", -1, LoadLevel_CUS4 },
3690 { "GRP1", -1, LoadLevel_GRP1 },
3691 { "CONF", -1, LoadLevel_CONF },
3692 { "ELEM", -1, LoadLevel_ELEM },
3693 { "NOTE", -1, LoadLevel_NOTE },
3694 { "CUSX", -1, LoadLevel_CUSX },
3695 { "GRPX", -1, LoadLevel_GRPX },
3696 { "EMPX", -1, LoadLevel_EMPX },
3701 while (getFileChunkBE(file, chunk_name, &chunk_size))
3705 while (chunk_info[i].name != NULL &&
3706 !strEqual(chunk_name, chunk_info[i].name))
3709 if (chunk_info[i].name == NULL)
3711 Warn("unknown chunk '%s' in level file '%s'",
3712 chunk_name, filename);
3714 ReadUnusedBytesFromFile(file, chunk_size);
3716 else if (chunk_info[i].size != -1 &&
3717 chunk_info[i].size != chunk_size)
3719 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3720 chunk_size, chunk_name, filename);
3722 ReadUnusedBytesFromFile(file, chunk_size);
3726 // call function to load this level chunk
3727 int chunk_size_expected =
3728 (chunk_info[i].loader)(file, chunk_size, level);
3730 if (chunk_size_expected < 0)
3732 Warn("error reading chunk '%s' in level file '%s'",
3733 chunk_name, filename);
3738 // the size of some chunks cannot be checked before reading other
3739 // chunks first (like "HEAD" and "BODY") that contain some header
3740 // information, so check them here
3741 if (chunk_size_expected != chunk_size)
3743 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3744 chunk_size, chunk_name, filename);
3756 // ----------------------------------------------------------------------------
3757 // functions for loading BD level
3758 // ----------------------------------------------------------------------------
3760 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3762 struct LevelInfo_BD *level_bd = level->native_bd_level;
3763 GdCave *cave = NULL; // will be changed below
3764 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3765 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3768 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3770 // cave and map newly allocated when set to defaults above
3771 cave = level_bd->cave;
3773 for (i = 0; i < 5; i++)
3775 cave->level_time[i] = level->time;
3776 cave->level_diamonds[i] = level->gems_needed;
3777 cave->level_magic_wall_time[i] = level->time_magic_wall;
3779 cave->level_speed[i] = level->bd_cycle_delay_ms;
3780 cave->level_ckdelay[i] = level->bd_cycle_delay_c64;
3781 cave->level_hatching_delay_frame[i] = level->bd_hatching_delay_cycles;
3782 cave->level_hatching_delay_time[i] = level->bd_hatching_delay_seconds;
3784 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3787 cave->diamond_value = level->score[SC_EMERALD];
3788 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3790 cave->scheduling = level->bd_scheduling_type;
3791 cave->pal_timing = level->bd_pal_timing;
3792 cave->intermission = level->bd_intermission;
3793 cave->diagonal_movements = level->bd_diagonal_movements;
3795 cave->lineshift = level->bd_line_shifting_borders;
3796 cave->wraparound_objects = level->bd_wraparound_objects;
3797 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3798 cave->short_explosions = level->bd_short_explosions;
3800 strncpy(cave->name, level->name, sizeof(GdString));
3801 cave->name[sizeof(GdString) - 1] = '\0';
3803 for (x = 0; x < cave->w; x++)
3804 for (y = 0; y < cave->h; y++)
3805 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3808 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3810 struct LevelInfo_BD *level_bd = level->native_bd_level;
3811 GdCave *cave = level_bd->cave;
3812 int bd_level_nr = level_bd->level_nr;
3815 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3816 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3818 level->time = cave->level_time[bd_level_nr];
3819 level->gems_needed = cave->level_diamonds[bd_level_nr];
3820 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3822 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3823 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3824 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3825 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3827 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3828 level->score[SC_EMERALD] = cave->diamond_value;
3829 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3831 level->bd_scheduling_type = cave->scheduling;
3832 level->bd_pal_timing = cave->pal_timing;
3833 level->bd_intermission = cave->intermission;
3834 level->bd_diagonal_movements = cave->diagonal_movements;
3836 level->bd_line_shifting_borders = cave->lineshift;
3837 level->bd_wraparound_objects = cave->wraparound_objects;
3838 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
3839 level->bd_short_explosions = cave->short_explosions;
3841 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3843 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3844 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3846 for (x = 0; x < level->fieldx; x++)
3847 for (y = 0; y < level->fieldy; y++)
3848 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3850 checked_free(cave_name);
3853 static void setTapeInfoToDefaults(void);
3855 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3857 struct LevelInfo_BD *level_bd = level->native_bd_level;
3858 GdCave *cave = level_bd->cave;
3859 GdReplay *replay = level_bd->replay;
3865 // always start with reliable default values
3866 setTapeInfoToDefaults();
3868 tape.level_nr = level_nr; // (currently not used)
3869 tape.random_seed = replay->seed;
3871 TapeSetDateFromIsoDateString(replay->date);
3874 tape.pos[tape.counter].delay = 0;
3876 tape.bd_replay = TRUE;
3878 // all time calculations only used to display approximate tape time
3879 int cave_speed = cave->speed;
3880 int milliseconds_game = 0;
3881 int milliseconds_elapsed = 20;
3883 for (i = 0; i < replay->movements->len; i++)
3885 int replay_action = replay->movements->data[i];
3886 int tape_action = map_action_BD_to_RND(replay_action);
3887 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3888 boolean success = 0;
3892 success = TapeAddAction(action);
3894 milliseconds_game += milliseconds_elapsed;
3896 if (milliseconds_game >= cave_speed)
3898 milliseconds_game -= cave_speed;
3905 tape.pos[tape.counter].delay = 0;
3906 tape.pos[tape.counter].action[0] = 0;
3910 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3916 TapeHaltRecording();
3920 // ----------------------------------------------------------------------------
3921 // functions for loading EM level
3922 // ----------------------------------------------------------------------------
3924 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3926 static int ball_xy[8][2] =
3937 struct LevelInfo_EM *level_em = level->native_em_level;
3938 struct CAVE *cav = level_em->cav;
3941 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3942 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3944 cav->time_seconds = level->time;
3945 cav->gems_needed = level->gems_needed;
3947 cav->emerald_score = level->score[SC_EMERALD];
3948 cav->diamond_score = level->score[SC_DIAMOND];
3949 cav->alien_score = level->score[SC_ROBOT];
3950 cav->tank_score = level->score[SC_SPACESHIP];
3951 cav->bug_score = level->score[SC_BUG];
3952 cav->eater_score = level->score[SC_YAMYAM];
3953 cav->nut_score = level->score[SC_NUT];
3954 cav->dynamite_score = level->score[SC_DYNAMITE];
3955 cav->key_score = level->score[SC_KEY];
3956 cav->exit_score = level->score[SC_TIME_BONUS];
3958 cav->num_eater_arrays = level->num_yamyam_contents;
3960 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3961 for (y = 0; y < 3; y++)
3962 for (x = 0; x < 3; x++)
3963 cav->eater_array[i][y * 3 + x] =
3964 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3966 cav->amoeba_time = level->amoeba_speed;
3967 cav->wonderwall_time = level->time_magic_wall;
3968 cav->wheel_time = level->time_wheel;
3970 cav->android_move_time = level->android_move_time;
3971 cav->android_clone_time = level->android_clone_time;
3972 cav->ball_random = level->ball_random;
3973 cav->ball_active = level->ball_active_initial;
3974 cav->ball_time = level->ball_time;
3975 cav->num_ball_arrays = level->num_ball_contents;
3977 cav->lenses_score = level->lenses_score;
3978 cav->magnify_score = level->magnify_score;
3979 cav->slurp_score = level->slurp_score;
3981 cav->lenses_time = level->lenses_time;
3982 cav->magnify_time = level->magnify_time;
3984 cav->wind_time = 9999;
3985 cav->wind_direction =
3986 map_direction_RND_to_EM(level->wind_direction_initial);
3988 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3989 for (j = 0; j < 8; j++)
3990 cav->ball_array[i][j] =
3991 map_element_RND_to_EM_cave(level->ball_content[i].
3992 e[ball_xy[j][0]][ball_xy[j][1]]);
3994 map_android_clone_elements_RND_to_EM(level);
3996 // first fill the complete playfield with the empty space element
3997 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3998 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3999 cav->cave[x][y] = Cblank;
4001 // then copy the real level contents from level file into the playfield
4002 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4004 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4006 if (level->field[x][y] == EL_AMOEBA_DEAD)
4007 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4009 cav->cave[x][y] = new_element;
4012 for (i = 0; i < MAX_PLAYERS; i++)
4014 cav->player_x[i] = -1;
4015 cav->player_y[i] = -1;
4018 // initialize player positions and delete players from the playfield
4019 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4021 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4023 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4025 cav->player_x[player_nr] = x;
4026 cav->player_y[player_nr] = y;
4028 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4033 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4035 static int ball_xy[8][2] =
4046 struct LevelInfo_EM *level_em = level->native_em_level;
4047 struct CAVE *cav = level_em->cav;
4050 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4051 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4053 level->time = cav->time_seconds;
4054 level->gems_needed = cav->gems_needed;
4056 sprintf(level->name, "Level %d", level->file_info.nr);
4058 level->score[SC_EMERALD] = cav->emerald_score;
4059 level->score[SC_DIAMOND] = cav->diamond_score;
4060 level->score[SC_ROBOT] = cav->alien_score;
4061 level->score[SC_SPACESHIP] = cav->tank_score;
4062 level->score[SC_BUG] = cav->bug_score;
4063 level->score[SC_YAMYAM] = cav->eater_score;
4064 level->score[SC_NUT] = cav->nut_score;
4065 level->score[SC_DYNAMITE] = cav->dynamite_score;
4066 level->score[SC_KEY] = cav->key_score;
4067 level->score[SC_TIME_BONUS] = cav->exit_score;
4069 level->num_yamyam_contents = cav->num_eater_arrays;
4071 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4072 for (y = 0; y < 3; y++)
4073 for (x = 0; x < 3; x++)
4074 level->yamyam_content[i].e[x][y] =
4075 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4077 level->amoeba_speed = cav->amoeba_time;
4078 level->time_magic_wall = cav->wonderwall_time;
4079 level->time_wheel = cav->wheel_time;
4081 level->android_move_time = cav->android_move_time;
4082 level->android_clone_time = cav->android_clone_time;
4083 level->ball_random = cav->ball_random;
4084 level->ball_active_initial = cav->ball_active;
4085 level->ball_time = cav->ball_time;
4086 level->num_ball_contents = cav->num_ball_arrays;
4088 level->lenses_score = cav->lenses_score;
4089 level->magnify_score = cav->magnify_score;
4090 level->slurp_score = cav->slurp_score;
4092 level->lenses_time = cav->lenses_time;
4093 level->magnify_time = cav->magnify_time;
4095 level->wind_direction_initial =
4096 map_direction_EM_to_RND(cav->wind_direction);
4098 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4099 for (j = 0; j < 8; j++)
4100 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4101 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4103 map_android_clone_elements_EM_to_RND(level);
4105 // convert the playfield (some elements need special treatment)
4106 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4108 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4110 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4111 new_element = EL_AMOEBA_DEAD;
4113 level->field[x][y] = new_element;
4116 for (i = 0; i < MAX_PLAYERS; i++)
4118 // in case of all players set to the same field, use the first player
4119 int nr = MAX_PLAYERS - i - 1;
4120 int jx = cav->player_x[nr];
4121 int jy = cav->player_y[nr];
4123 if (jx != -1 && jy != -1)
4124 level->field[jx][jy] = EL_PLAYER_1 + nr;
4127 // time score is counted for each 10 seconds left in Emerald Mine levels
4128 level->time_score_base = 10;
4132 // ----------------------------------------------------------------------------
4133 // functions for loading SP level
4134 // ----------------------------------------------------------------------------
4136 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4138 struct LevelInfo_SP *level_sp = level->native_sp_level;
4139 LevelInfoType *header = &level_sp->header;
4142 level_sp->width = level->fieldx;
4143 level_sp->height = level->fieldy;
4145 for (x = 0; x < level->fieldx; x++)
4146 for (y = 0; y < level->fieldy; y++)
4147 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4149 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4151 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4152 header->LevelTitle[i] = level->name[i];
4153 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4155 header->InfotronsNeeded = level->gems_needed;
4157 header->SpecialPortCount = 0;
4159 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4161 boolean gravity_port_found = FALSE;
4162 boolean gravity_port_valid = FALSE;
4163 int gravity_port_flag;
4164 int gravity_port_base_element;
4165 int element = level->field[x][y];
4167 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4168 element <= EL_SP_GRAVITY_ON_PORT_UP)
4170 gravity_port_found = TRUE;
4171 gravity_port_valid = TRUE;
4172 gravity_port_flag = 1;
4173 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4175 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4176 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4178 gravity_port_found = TRUE;
4179 gravity_port_valid = TRUE;
4180 gravity_port_flag = 0;
4181 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4183 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4184 element <= EL_SP_GRAVITY_PORT_UP)
4186 // change R'n'D style gravity inverting special port to normal port
4187 // (there are no gravity inverting ports in native Supaplex engine)
4189 gravity_port_found = TRUE;
4190 gravity_port_valid = FALSE;
4191 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4194 if (gravity_port_found)
4196 if (gravity_port_valid &&
4197 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4199 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4201 port->PortLocation = (y * level->fieldx + x) * 2;
4202 port->Gravity = gravity_port_flag;
4204 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4206 header->SpecialPortCount++;
4210 // change special gravity port to normal port
4212 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4215 level_sp->playfield[x][y] = element - EL_SP_START;
4220 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4222 struct LevelInfo_SP *level_sp = level->native_sp_level;
4223 LevelInfoType *header = &level_sp->header;
4224 boolean num_invalid_elements = 0;
4227 level->fieldx = level_sp->width;
4228 level->fieldy = level_sp->height;
4230 for (x = 0; x < level->fieldx; x++)
4232 for (y = 0; y < level->fieldy; y++)
4234 int element_old = level_sp->playfield[x][y];
4235 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4237 if (element_new == EL_UNKNOWN)
4239 num_invalid_elements++;
4241 Debug("level:native:SP", "invalid element %d at position %d, %d",
4245 level->field[x][y] = element_new;
4249 if (num_invalid_elements > 0)
4250 Warn("found %d invalid elements%s", num_invalid_elements,
4251 (!options.debug ? " (use '--debug' for more details)" : ""));
4253 for (i = 0; i < MAX_PLAYERS; i++)
4254 level->initial_player_gravity[i] =
4255 (header->InitialGravity == 1 ? TRUE : FALSE);
4257 // skip leading spaces
4258 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4259 if (header->LevelTitle[i] != ' ')
4263 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4264 level->name[j] = header->LevelTitle[i];
4265 level->name[j] = '\0';
4267 // cut trailing spaces
4269 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4270 level->name[j - 1] = '\0';
4272 level->gems_needed = header->InfotronsNeeded;
4274 for (i = 0; i < header->SpecialPortCount; i++)
4276 SpecialPortType *port = &header->SpecialPort[i];
4277 int port_location = port->PortLocation;
4278 int gravity = port->Gravity;
4279 int port_x, port_y, port_element;
4281 port_x = (port_location / 2) % level->fieldx;
4282 port_y = (port_location / 2) / level->fieldx;
4284 if (port_x < 0 || port_x >= level->fieldx ||
4285 port_y < 0 || port_y >= level->fieldy)
4287 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4292 port_element = level->field[port_x][port_y];
4294 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4295 port_element > EL_SP_GRAVITY_PORT_UP)
4297 Warn("no special port at position (%d, %d)", port_x, port_y);
4302 // change previous (wrong) gravity inverting special port to either
4303 // gravity enabling special port or gravity disabling special port
4304 level->field[port_x][port_y] +=
4305 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4306 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4309 // change special gravity ports without database entries to normal ports
4310 for (x = 0; x < level->fieldx; x++)
4311 for (y = 0; y < level->fieldy; y++)
4312 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4313 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4314 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4316 level->time = 0; // no time limit
4317 level->amoeba_speed = 0;
4318 level->time_magic_wall = 0;
4319 level->time_wheel = 0;
4320 level->amoeba_content = EL_EMPTY;
4322 // original Supaplex does not use score values -- rate by playing time
4323 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4324 level->score[i] = 0;
4326 level->rate_time_over_score = TRUE;
4328 // there are no yamyams in supaplex levels
4329 for (i = 0; i < level->num_yamyam_contents; i++)
4330 for (x = 0; x < 3; x++)
4331 for (y = 0; y < 3; y++)
4332 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4335 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4337 struct LevelInfo_SP *level_sp = level->native_sp_level;
4338 struct DemoInfo_SP *demo = &level_sp->demo;
4341 // always start with reliable default values
4342 demo->is_available = FALSE;
4345 if (TAPE_IS_EMPTY(tape))
4348 demo->level_nr = tape.level_nr; // (currently not used)
4350 level_sp->header.DemoRandomSeed = tape.random_seed;
4354 for (i = 0; i < tape.length; i++)
4356 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4357 int demo_repeat = tape.pos[i].delay;
4358 int demo_entries = (demo_repeat + 15) / 16;
4360 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4362 Warn("tape truncated: size exceeds maximum SP demo size %d",
4368 for (j = 0; j < demo_repeat / 16; j++)
4369 demo->data[demo->length++] = 0xf0 | demo_action;
4371 if (demo_repeat % 16)
4372 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4375 demo->is_available = TRUE;
4378 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4380 struct LevelInfo_SP *level_sp = level->native_sp_level;
4381 struct DemoInfo_SP *demo = &level_sp->demo;
4382 char *filename = level->file_info.filename;
4385 // always start with reliable default values
4386 setTapeInfoToDefaults();
4388 if (!demo->is_available)
4391 tape.level_nr = demo->level_nr; // (currently not used)
4392 tape.random_seed = level_sp->header.DemoRandomSeed;
4394 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4397 tape.pos[tape.counter].delay = 0;
4399 for (i = 0; i < demo->length; i++)
4401 int demo_action = demo->data[i] & 0x0f;
4402 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4403 int tape_action = map_key_SP_to_RND(demo_action);
4404 int tape_repeat = demo_repeat + 1;
4405 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4406 boolean success = 0;
4409 for (j = 0; j < tape_repeat; j++)
4410 success = TapeAddAction(action);
4414 Warn("SP demo truncated: size exceeds maximum tape size %d",
4421 TapeHaltRecording();
4425 // ----------------------------------------------------------------------------
4426 // functions for loading MM level
4427 // ----------------------------------------------------------------------------
4429 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4431 struct LevelInfo_MM *level_mm = level->native_mm_level;
4434 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4435 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4437 level_mm->time = level->time;
4438 level_mm->kettles_needed = level->gems_needed;
4439 level_mm->auto_count_kettles = level->auto_count_gems;
4441 level_mm->mm_laser_red = level->mm_laser_red;
4442 level_mm->mm_laser_green = level->mm_laser_green;
4443 level_mm->mm_laser_blue = level->mm_laser_blue;
4445 level_mm->df_laser_red = level->df_laser_red;
4446 level_mm->df_laser_green = level->df_laser_green;
4447 level_mm->df_laser_blue = level->df_laser_blue;
4449 strcpy(level_mm->name, level->name);
4450 strcpy(level_mm->author, level->author);
4452 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4453 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4454 level_mm->score[SC_KEY] = level->score[SC_KEY];
4455 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4456 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4458 level_mm->amoeba_speed = level->amoeba_speed;
4459 level_mm->time_fuse = level->mm_time_fuse;
4460 level_mm->time_bomb = level->mm_time_bomb;
4461 level_mm->time_ball = level->mm_time_ball;
4462 level_mm->time_block = level->mm_time_block;
4464 level_mm->num_ball_contents = level->num_mm_ball_contents;
4465 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4466 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4467 level_mm->explode_ball = level->explode_mm_ball;
4469 for (i = 0; i < level->num_mm_ball_contents; i++)
4470 level_mm->ball_content[i] =
4471 map_element_RND_to_MM(level->mm_ball_content[i]);
4473 for (x = 0; x < level->fieldx; x++)
4474 for (y = 0; y < level->fieldy; y++)
4476 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4479 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4481 struct LevelInfo_MM *level_mm = level->native_mm_level;
4484 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4485 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4487 level->time = level_mm->time;
4488 level->gems_needed = level_mm->kettles_needed;
4489 level->auto_count_gems = level_mm->auto_count_kettles;
4491 level->mm_laser_red = level_mm->mm_laser_red;
4492 level->mm_laser_green = level_mm->mm_laser_green;
4493 level->mm_laser_blue = level_mm->mm_laser_blue;
4495 level->df_laser_red = level_mm->df_laser_red;
4496 level->df_laser_green = level_mm->df_laser_green;
4497 level->df_laser_blue = level_mm->df_laser_blue;
4499 strcpy(level->name, level_mm->name);
4501 // only overwrite author from 'levelinfo.conf' if author defined in level
4502 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4503 strcpy(level->author, level_mm->author);
4505 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4506 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4507 level->score[SC_KEY] = level_mm->score[SC_KEY];
4508 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4509 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4511 level->amoeba_speed = level_mm->amoeba_speed;
4512 level->mm_time_fuse = level_mm->time_fuse;
4513 level->mm_time_bomb = level_mm->time_bomb;
4514 level->mm_time_ball = level_mm->time_ball;
4515 level->mm_time_block = level_mm->time_block;
4517 level->num_mm_ball_contents = level_mm->num_ball_contents;
4518 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4519 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4520 level->explode_mm_ball = level_mm->explode_ball;
4522 for (i = 0; i < level->num_mm_ball_contents; i++)
4523 level->mm_ball_content[i] =
4524 map_element_MM_to_RND(level_mm->ball_content[i]);
4526 for (x = 0; x < level->fieldx; x++)
4527 for (y = 0; y < level->fieldy; y++)
4528 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4532 // ----------------------------------------------------------------------------
4533 // functions for loading DC level
4534 // ----------------------------------------------------------------------------
4536 #define DC_LEVEL_HEADER_SIZE 344
4538 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4541 static int last_data_encoded;
4545 int diff_hi, diff_lo;
4546 int data_hi, data_lo;
4547 unsigned short data_decoded;
4551 last_data_encoded = 0;
4558 diff = data_encoded - last_data_encoded;
4559 diff_hi = diff & ~0xff;
4560 diff_lo = diff & 0xff;
4564 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4565 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4566 data_hi = data_hi & 0xff00;
4568 data_decoded = data_hi | data_lo;
4570 last_data_encoded = data_encoded;
4572 offset1 = (offset1 + 1) % 31;
4573 offset2 = offset2 & 0xff;
4575 return data_decoded;
4578 static int getMappedElement_DC(int element)
4586 // 0x0117 - 0x036e: (?)
4589 // 0x042d - 0x0684: (?)
4605 element = EL_CRYSTAL;
4608 case 0x0e77: // quicksand (boulder)
4609 element = EL_QUICKSAND_FAST_FULL;
4612 case 0x0e99: // slow quicksand (boulder)
4613 element = EL_QUICKSAND_FULL;
4617 element = EL_EM_EXIT_OPEN;
4621 element = EL_EM_EXIT_CLOSED;
4625 element = EL_EM_STEEL_EXIT_OPEN;
4629 element = EL_EM_STEEL_EXIT_CLOSED;
4632 case 0x0f4f: // dynamite (lit 1)
4633 element = EL_EM_DYNAMITE_ACTIVE;
4636 case 0x0f57: // dynamite (lit 2)
4637 element = EL_EM_DYNAMITE_ACTIVE;
4640 case 0x0f5f: // dynamite (lit 3)
4641 element = EL_EM_DYNAMITE_ACTIVE;
4644 case 0x0f67: // dynamite (lit 4)
4645 element = EL_EM_DYNAMITE_ACTIVE;
4652 element = EL_AMOEBA_WET;
4656 element = EL_AMOEBA_DROP;
4660 element = EL_DC_MAGIC_WALL;
4664 element = EL_SPACESHIP_UP;
4668 element = EL_SPACESHIP_DOWN;
4672 element = EL_SPACESHIP_LEFT;
4676 element = EL_SPACESHIP_RIGHT;
4680 element = EL_BUG_UP;
4684 element = EL_BUG_DOWN;
4688 element = EL_BUG_LEFT;
4692 element = EL_BUG_RIGHT;
4696 element = EL_MOLE_UP;
4700 element = EL_MOLE_DOWN;
4704 element = EL_MOLE_LEFT;
4708 element = EL_MOLE_RIGHT;
4716 element = EL_YAMYAM_UP;
4720 element = EL_SWITCHGATE_OPEN;
4724 element = EL_SWITCHGATE_CLOSED;
4728 element = EL_DC_SWITCHGATE_SWITCH_UP;
4732 element = EL_TIMEGATE_CLOSED;
4735 case 0x144c: // conveyor belt switch (green)
4736 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4739 case 0x144f: // conveyor belt switch (red)
4740 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4743 case 0x1452: // conveyor belt switch (blue)
4744 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4748 element = EL_CONVEYOR_BELT_3_MIDDLE;
4752 element = EL_CONVEYOR_BELT_3_LEFT;
4756 element = EL_CONVEYOR_BELT_3_RIGHT;
4760 element = EL_CONVEYOR_BELT_1_MIDDLE;
4764 element = EL_CONVEYOR_BELT_1_LEFT;
4768 element = EL_CONVEYOR_BELT_1_RIGHT;
4772 element = EL_CONVEYOR_BELT_4_MIDDLE;
4776 element = EL_CONVEYOR_BELT_4_LEFT;
4780 element = EL_CONVEYOR_BELT_4_RIGHT;
4784 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4788 element = EL_EXPANDABLE_WALL_VERTICAL;
4792 element = EL_EXPANDABLE_WALL_ANY;
4795 case 0x14ce: // growing steel wall (left/right)
4796 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4799 case 0x14df: // growing steel wall (up/down)
4800 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4803 case 0x14e8: // growing steel wall (up/down/left/right)
4804 element = EL_EXPANDABLE_STEELWALL_ANY;
4808 element = EL_SHIELD_DEADLY;
4812 element = EL_EXTRA_TIME;
4820 element = EL_EMPTY_SPACE;
4823 case 0x1578: // quicksand (empty)
4824 element = EL_QUICKSAND_FAST_EMPTY;
4827 case 0x1579: // slow quicksand (empty)
4828 element = EL_QUICKSAND_EMPTY;
4838 element = EL_EM_DYNAMITE;
4841 case 0x15a1: // key (red)
4842 element = EL_EM_KEY_1;
4845 case 0x15a2: // key (yellow)
4846 element = EL_EM_KEY_2;
4849 case 0x15a3: // key (blue)
4850 element = EL_EM_KEY_4;
4853 case 0x15a4: // key (green)
4854 element = EL_EM_KEY_3;
4857 case 0x15a5: // key (white)
4858 element = EL_DC_KEY_WHITE;
4862 element = EL_WALL_SLIPPERY;
4869 case 0x15a8: // wall (not round)
4873 case 0x15a9: // (blue)
4874 element = EL_CHAR_A;
4877 case 0x15aa: // (blue)
4878 element = EL_CHAR_B;
4881 case 0x15ab: // (blue)
4882 element = EL_CHAR_C;
4885 case 0x15ac: // (blue)
4886 element = EL_CHAR_D;
4889 case 0x15ad: // (blue)
4890 element = EL_CHAR_E;
4893 case 0x15ae: // (blue)
4894 element = EL_CHAR_F;
4897 case 0x15af: // (blue)
4898 element = EL_CHAR_G;
4901 case 0x15b0: // (blue)
4902 element = EL_CHAR_H;
4905 case 0x15b1: // (blue)
4906 element = EL_CHAR_I;
4909 case 0x15b2: // (blue)
4910 element = EL_CHAR_J;
4913 case 0x15b3: // (blue)
4914 element = EL_CHAR_K;
4917 case 0x15b4: // (blue)
4918 element = EL_CHAR_L;
4921 case 0x15b5: // (blue)
4922 element = EL_CHAR_M;
4925 case 0x15b6: // (blue)
4926 element = EL_CHAR_N;
4929 case 0x15b7: // (blue)
4930 element = EL_CHAR_O;
4933 case 0x15b8: // (blue)
4934 element = EL_CHAR_P;
4937 case 0x15b9: // (blue)
4938 element = EL_CHAR_Q;
4941 case 0x15ba: // (blue)
4942 element = EL_CHAR_R;
4945 case 0x15bb: // (blue)
4946 element = EL_CHAR_S;
4949 case 0x15bc: // (blue)
4950 element = EL_CHAR_T;
4953 case 0x15bd: // (blue)
4954 element = EL_CHAR_U;
4957 case 0x15be: // (blue)
4958 element = EL_CHAR_V;
4961 case 0x15bf: // (blue)
4962 element = EL_CHAR_W;
4965 case 0x15c0: // (blue)
4966 element = EL_CHAR_X;
4969 case 0x15c1: // (blue)
4970 element = EL_CHAR_Y;
4973 case 0x15c2: // (blue)
4974 element = EL_CHAR_Z;
4977 case 0x15c3: // (blue)
4978 element = EL_CHAR_AUMLAUT;
4981 case 0x15c4: // (blue)
4982 element = EL_CHAR_OUMLAUT;
4985 case 0x15c5: // (blue)
4986 element = EL_CHAR_UUMLAUT;
4989 case 0x15c6: // (blue)
4990 element = EL_CHAR_0;
4993 case 0x15c7: // (blue)
4994 element = EL_CHAR_1;
4997 case 0x15c8: // (blue)
4998 element = EL_CHAR_2;
5001 case 0x15c9: // (blue)
5002 element = EL_CHAR_3;
5005 case 0x15ca: // (blue)
5006 element = EL_CHAR_4;
5009 case 0x15cb: // (blue)
5010 element = EL_CHAR_5;
5013 case 0x15cc: // (blue)
5014 element = EL_CHAR_6;
5017 case 0x15cd: // (blue)
5018 element = EL_CHAR_7;
5021 case 0x15ce: // (blue)
5022 element = EL_CHAR_8;
5025 case 0x15cf: // (blue)
5026 element = EL_CHAR_9;
5029 case 0x15d0: // (blue)
5030 element = EL_CHAR_PERIOD;
5033 case 0x15d1: // (blue)
5034 element = EL_CHAR_EXCLAM;
5037 case 0x15d2: // (blue)
5038 element = EL_CHAR_COLON;
5041 case 0x15d3: // (blue)
5042 element = EL_CHAR_LESS;
5045 case 0x15d4: // (blue)
5046 element = EL_CHAR_GREATER;
5049 case 0x15d5: // (blue)
5050 element = EL_CHAR_QUESTION;
5053 case 0x15d6: // (blue)
5054 element = EL_CHAR_COPYRIGHT;
5057 case 0x15d7: // (blue)
5058 element = EL_CHAR_UP;
5061 case 0x15d8: // (blue)
5062 element = EL_CHAR_DOWN;
5065 case 0x15d9: // (blue)
5066 element = EL_CHAR_BUTTON;
5069 case 0x15da: // (blue)
5070 element = EL_CHAR_PLUS;
5073 case 0x15db: // (blue)
5074 element = EL_CHAR_MINUS;
5077 case 0x15dc: // (blue)
5078 element = EL_CHAR_APOSTROPHE;
5081 case 0x15dd: // (blue)
5082 element = EL_CHAR_PARENLEFT;
5085 case 0x15de: // (blue)
5086 element = EL_CHAR_PARENRIGHT;
5089 case 0x15df: // (green)
5090 element = EL_CHAR_A;
5093 case 0x15e0: // (green)
5094 element = EL_CHAR_B;
5097 case 0x15e1: // (green)
5098 element = EL_CHAR_C;
5101 case 0x15e2: // (green)
5102 element = EL_CHAR_D;
5105 case 0x15e3: // (green)
5106 element = EL_CHAR_E;
5109 case 0x15e4: // (green)
5110 element = EL_CHAR_F;
5113 case 0x15e5: // (green)
5114 element = EL_CHAR_G;
5117 case 0x15e6: // (green)
5118 element = EL_CHAR_H;
5121 case 0x15e7: // (green)
5122 element = EL_CHAR_I;
5125 case 0x15e8: // (green)
5126 element = EL_CHAR_J;
5129 case 0x15e9: // (green)
5130 element = EL_CHAR_K;
5133 case 0x15ea: // (green)
5134 element = EL_CHAR_L;
5137 case 0x15eb: // (green)
5138 element = EL_CHAR_M;
5141 case 0x15ec: // (green)
5142 element = EL_CHAR_N;
5145 case 0x15ed: // (green)
5146 element = EL_CHAR_O;
5149 case 0x15ee: // (green)
5150 element = EL_CHAR_P;
5153 case 0x15ef: // (green)
5154 element = EL_CHAR_Q;
5157 case 0x15f0: // (green)
5158 element = EL_CHAR_R;
5161 case 0x15f1: // (green)
5162 element = EL_CHAR_S;
5165 case 0x15f2: // (green)
5166 element = EL_CHAR_T;
5169 case 0x15f3: // (green)
5170 element = EL_CHAR_U;
5173 case 0x15f4: // (green)
5174 element = EL_CHAR_V;
5177 case 0x15f5: // (green)
5178 element = EL_CHAR_W;
5181 case 0x15f6: // (green)
5182 element = EL_CHAR_X;
5185 case 0x15f7: // (green)
5186 element = EL_CHAR_Y;
5189 case 0x15f8: // (green)
5190 element = EL_CHAR_Z;
5193 case 0x15f9: // (green)
5194 element = EL_CHAR_AUMLAUT;
5197 case 0x15fa: // (green)
5198 element = EL_CHAR_OUMLAUT;
5201 case 0x15fb: // (green)
5202 element = EL_CHAR_UUMLAUT;
5205 case 0x15fc: // (green)
5206 element = EL_CHAR_0;
5209 case 0x15fd: // (green)
5210 element = EL_CHAR_1;
5213 case 0x15fe: // (green)
5214 element = EL_CHAR_2;
5217 case 0x15ff: // (green)
5218 element = EL_CHAR_3;
5221 case 0x1600: // (green)
5222 element = EL_CHAR_4;
5225 case 0x1601: // (green)
5226 element = EL_CHAR_5;
5229 case 0x1602: // (green)
5230 element = EL_CHAR_6;
5233 case 0x1603: // (green)
5234 element = EL_CHAR_7;
5237 case 0x1604: // (green)
5238 element = EL_CHAR_8;
5241 case 0x1605: // (green)
5242 element = EL_CHAR_9;
5245 case 0x1606: // (green)
5246 element = EL_CHAR_PERIOD;
5249 case 0x1607: // (green)
5250 element = EL_CHAR_EXCLAM;
5253 case 0x1608: // (green)
5254 element = EL_CHAR_COLON;
5257 case 0x1609: // (green)
5258 element = EL_CHAR_LESS;
5261 case 0x160a: // (green)
5262 element = EL_CHAR_GREATER;
5265 case 0x160b: // (green)
5266 element = EL_CHAR_QUESTION;
5269 case 0x160c: // (green)
5270 element = EL_CHAR_COPYRIGHT;
5273 case 0x160d: // (green)
5274 element = EL_CHAR_UP;
5277 case 0x160e: // (green)
5278 element = EL_CHAR_DOWN;
5281 case 0x160f: // (green)
5282 element = EL_CHAR_BUTTON;
5285 case 0x1610: // (green)
5286 element = EL_CHAR_PLUS;
5289 case 0x1611: // (green)
5290 element = EL_CHAR_MINUS;
5293 case 0x1612: // (green)
5294 element = EL_CHAR_APOSTROPHE;
5297 case 0x1613: // (green)
5298 element = EL_CHAR_PARENLEFT;
5301 case 0x1614: // (green)
5302 element = EL_CHAR_PARENRIGHT;
5305 case 0x1615: // (blue steel)
5306 element = EL_STEEL_CHAR_A;
5309 case 0x1616: // (blue steel)
5310 element = EL_STEEL_CHAR_B;
5313 case 0x1617: // (blue steel)
5314 element = EL_STEEL_CHAR_C;
5317 case 0x1618: // (blue steel)
5318 element = EL_STEEL_CHAR_D;
5321 case 0x1619: // (blue steel)
5322 element = EL_STEEL_CHAR_E;
5325 case 0x161a: // (blue steel)
5326 element = EL_STEEL_CHAR_F;
5329 case 0x161b: // (blue steel)
5330 element = EL_STEEL_CHAR_G;
5333 case 0x161c: // (blue steel)
5334 element = EL_STEEL_CHAR_H;
5337 case 0x161d: // (blue steel)
5338 element = EL_STEEL_CHAR_I;
5341 case 0x161e: // (blue steel)
5342 element = EL_STEEL_CHAR_J;
5345 case 0x161f: // (blue steel)
5346 element = EL_STEEL_CHAR_K;
5349 case 0x1620: // (blue steel)
5350 element = EL_STEEL_CHAR_L;
5353 case 0x1621: // (blue steel)
5354 element = EL_STEEL_CHAR_M;
5357 case 0x1622: // (blue steel)
5358 element = EL_STEEL_CHAR_N;
5361 case 0x1623: // (blue steel)
5362 element = EL_STEEL_CHAR_O;
5365 case 0x1624: // (blue steel)
5366 element = EL_STEEL_CHAR_P;
5369 case 0x1625: // (blue steel)
5370 element = EL_STEEL_CHAR_Q;
5373 case 0x1626: // (blue steel)
5374 element = EL_STEEL_CHAR_R;
5377 case 0x1627: // (blue steel)
5378 element = EL_STEEL_CHAR_S;
5381 case 0x1628: // (blue steel)
5382 element = EL_STEEL_CHAR_T;
5385 case 0x1629: // (blue steel)
5386 element = EL_STEEL_CHAR_U;
5389 case 0x162a: // (blue steel)
5390 element = EL_STEEL_CHAR_V;
5393 case 0x162b: // (blue steel)
5394 element = EL_STEEL_CHAR_W;
5397 case 0x162c: // (blue steel)
5398 element = EL_STEEL_CHAR_X;
5401 case 0x162d: // (blue steel)
5402 element = EL_STEEL_CHAR_Y;
5405 case 0x162e: // (blue steel)
5406 element = EL_STEEL_CHAR_Z;
5409 case 0x162f: // (blue steel)
5410 element = EL_STEEL_CHAR_AUMLAUT;
5413 case 0x1630: // (blue steel)
5414 element = EL_STEEL_CHAR_OUMLAUT;
5417 case 0x1631: // (blue steel)
5418 element = EL_STEEL_CHAR_UUMLAUT;
5421 case 0x1632: // (blue steel)
5422 element = EL_STEEL_CHAR_0;
5425 case 0x1633: // (blue steel)
5426 element = EL_STEEL_CHAR_1;
5429 case 0x1634: // (blue steel)
5430 element = EL_STEEL_CHAR_2;
5433 case 0x1635: // (blue steel)
5434 element = EL_STEEL_CHAR_3;
5437 case 0x1636: // (blue steel)
5438 element = EL_STEEL_CHAR_4;
5441 case 0x1637: // (blue steel)
5442 element = EL_STEEL_CHAR_5;
5445 case 0x1638: // (blue steel)
5446 element = EL_STEEL_CHAR_6;
5449 case 0x1639: // (blue steel)
5450 element = EL_STEEL_CHAR_7;
5453 case 0x163a: // (blue steel)
5454 element = EL_STEEL_CHAR_8;
5457 case 0x163b: // (blue steel)
5458 element = EL_STEEL_CHAR_9;
5461 case 0x163c: // (blue steel)
5462 element = EL_STEEL_CHAR_PERIOD;
5465 case 0x163d: // (blue steel)
5466 element = EL_STEEL_CHAR_EXCLAM;
5469 case 0x163e: // (blue steel)
5470 element = EL_STEEL_CHAR_COLON;
5473 case 0x163f: // (blue steel)
5474 element = EL_STEEL_CHAR_LESS;
5477 case 0x1640: // (blue steel)
5478 element = EL_STEEL_CHAR_GREATER;
5481 case 0x1641: // (blue steel)
5482 element = EL_STEEL_CHAR_QUESTION;
5485 case 0x1642: // (blue steel)
5486 element = EL_STEEL_CHAR_COPYRIGHT;
5489 case 0x1643: // (blue steel)
5490 element = EL_STEEL_CHAR_UP;
5493 case 0x1644: // (blue steel)
5494 element = EL_STEEL_CHAR_DOWN;
5497 case 0x1645: // (blue steel)
5498 element = EL_STEEL_CHAR_BUTTON;
5501 case 0x1646: // (blue steel)
5502 element = EL_STEEL_CHAR_PLUS;
5505 case 0x1647: // (blue steel)
5506 element = EL_STEEL_CHAR_MINUS;
5509 case 0x1648: // (blue steel)
5510 element = EL_STEEL_CHAR_APOSTROPHE;
5513 case 0x1649: // (blue steel)
5514 element = EL_STEEL_CHAR_PARENLEFT;
5517 case 0x164a: // (blue steel)
5518 element = EL_STEEL_CHAR_PARENRIGHT;
5521 case 0x164b: // (green steel)
5522 element = EL_STEEL_CHAR_A;
5525 case 0x164c: // (green steel)
5526 element = EL_STEEL_CHAR_B;
5529 case 0x164d: // (green steel)
5530 element = EL_STEEL_CHAR_C;
5533 case 0x164e: // (green steel)
5534 element = EL_STEEL_CHAR_D;
5537 case 0x164f: // (green steel)
5538 element = EL_STEEL_CHAR_E;
5541 case 0x1650: // (green steel)
5542 element = EL_STEEL_CHAR_F;
5545 case 0x1651: // (green steel)
5546 element = EL_STEEL_CHAR_G;
5549 case 0x1652: // (green steel)
5550 element = EL_STEEL_CHAR_H;
5553 case 0x1653: // (green steel)
5554 element = EL_STEEL_CHAR_I;
5557 case 0x1654: // (green steel)
5558 element = EL_STEEL_CHAR_J;
5561 case 0x1655: // (green steel)
5562 element = EL_STEEL_CHAR_K;
5565 case 0x1656: // (green steel)
5566 element = EL_STEEL_CHAR_L;
5569 case 0x1657: // (green steel)
5570 element = EL_STEEL_CHAR_M;
5573 case 0x1658: // (green steel)
5574 element = EL_STEEL_CHAR_N;
5577 case 0x1659: // (green steel)
5578 element = EL_STEEL_CHAR_O;
5581 case 0x165a: // (green steel)
5582 element = EL_STEEL_CHAR_P;
5585 case 0x165b: // (green steel)
5586 element = EL_STEEL_CHAR_Q;
5589 case 0x165c: // (green steel)
5590 element = EL_STEEL_CHAR_R;
5593 case 0x165d: // (green steel)
5594 element = EL_STEEL_CHAR_S;
5597 case 0x165e: // (green steel)
5598 element = EL_STEEL_CHAR_T;
5601 case 0x165f: // (green steel)
5602 element = EL_STEEL_CHAR_U;
5605 case 0x1660: // (green steel)
5606 element = EL_STEEL_CHAR_V;
5609 case 0x1661: // (green steel)
5610 element = EL_STEEL_CHAR_W;
5613 case 0x1662: // (green steel)
5614 element = EL_STEEL_CHAR_X;
5617 case 0x1663: // (green steel)
5618 element = EL_STEEL_CHAR_Y;
5621 case 0x1664: // (green steel)
5622 element = EL_STEEL_CHAR_Z;
5625 case 0x1665: // (green steel)
5626 element = EL_STEEL_CHAR_AUMLAUT;
5629 case 0x1666: // (green steel)
5630 element = EL_STEEL_CHAR_OUMLAUT;
5633 case 0x1667: // (green steel)
5634 element = EL_STEEL_CHAR_UUMLAUT;
5637 case 0x1668: // (green steel)
5638 element = EL_STEEL_CHAR_0;
5641 case 0x1669: // (green steel)
5642 element = EL_STEEL_CHAR_1;
5645 case 0x166a: // (green steel)
5646 element = EL_STEEL_CHAR_2;
5649 case 0x166b: // (green steel)
5650 element = EL_STEEL_CHAR_3;
5653 case 0x166c: // (green steel)
5654 element = EL_STEEL_CHAR_4;
5657 case 0x166d: // (green steel)
5658 element = EL_STEEL_CHAR_5;
5661 case 0x166e: // (green steel)
5662 element = EL_STEEL_CHAR_6;
5665 case 0x166f: // (green steel)
5666 element = EL_STEEL_CHAR_7;
5669 case 0x1670: // (green steel)
5670 element = EL_STEEL_CHAR_8;
5673 case 0x1671: // (green steel)
5674 element = EL_STEEL_CHAR_9;
5677 case 0x1672: // (green steel)
5678 element = EL_STEEL_CHAR_PERIOD;
5681 case 0x1673: // (green steel)
5682 element = EL_STEEL_CHAR_EXCLAM;
5685 case 0x1674: // (green steel)
5686 element = EL_STEEL_CHAR_COLON;
5689 case 0x1675: // (green steel)
5690 element = EL_STEEL_CHAR_LESS;
5693 case 0x1676: // (green steel)
5694 element = EL_STEEL_CHAR_GREATER;
5697 case 0x1677: // (green steel)
5698 element = EL_STEEL_CHAR_QUESTION;
5701 case 0x1678: // (green steel)
5702 element = EL_STEEL_CHAR_COPYRIGHT;
5705 case 0x1679: // (green steel)
5706 element = EL_STEEL_CHAR_UP;
5709 case 0x167a: // (green steel)
5710 element = EL_STEEL_CHAR_DOWN;
5713 case 0x167b: // (green steel)
5714 element = EL_STEEL_CHAR_BUTTON;
5717 case 0x167c: // (green steel)
5718 element = EL_STEEL_CHAR_PLUS;
5721 case 0x167d: // (green steel)
5722 element = EL_STEEL_CHAR_MINUS;
5725 case 0x167e: // (green steel)
5726 element = EL_STEEL_CHAR_APOSTROPHE;
5729 case 0x167f: // (green steel)
5730 element = EL_STEEL_CHAR_PARENLEFT;
5733 case 0x1680: // (green steel)
5734 element = EL_STEEL_CHAR_PARENRIGHT;
5737 case 0x1681: // gate (red)
5738 element = EL_EM_GATE_1;
5741 case 0x1682: // secret gate (red)
5742 element = EL_EM_GATE_1_GRAY;
5745 case 0x1683: // gate (yellow)
5746 element = EL_EM_GATE_2;
5749 case 0x1684: // secret gate (yellow)
5750 element = EL_EM_GATE_2_GRAY;
5753 case 0x1685: // gate (blue)
5754 element = EL_EM_GATE_4;
5757 case 0x1686: // secret gate (blue)
5758 element = EL_EM_GATE_4_GRAY;
5761 case 0x1687: // gate (green)
5762 element = EL_EM_GATE_3;
5765 case 0x1688: // secret gate (green)
5766 element = EL_EM_GATE_3_GRAY;
5769 case 0x1689: // gate (white)
5770 element = EL_DC_GATE_WHITE;
5773 case 0x168a: // secret gate (white)
5774 element = EL_DC_GATE_WHITE_GRAY;
5777 case 0x168b: // secret gate (no key)
5778 element = EL_DC_GATE_FAKE_GRAY;
5782 element = EL_ROBOT_WHEEL;
5786 element = EL_DC_TIMEGATE_SWITCH;
5790 element = EL_ACID_POOL_BOTTOM;
5794 element = EL_ACID_POOL_TOPLEFT;
5798 element = EL_ACID_POOL_TOPRIGHT;
5802 element = EL_ACID_POOL_BOTTOMLEFT;
5806 element = EL_ACID_POOL_BOTTOMRIGHT;
5810 element = EL_STEELWALL;
5814 element = EL_STEELWALL_SLIPPERY;
5817 case 0x1695: // steel wall (not round)
5818 element = EL_STEELWALL;
5821 case 0x1696: // steel wall (left)
5822 element = EL_DC_STEELWALL_1_LEFT;
5825 case 0x1697: // steel wall (bottom)
5826 element = EL_DC_STEELWALL_1_BOTTOM;
5829 case 0x1698: // steel wall (right)
5830 element = EL_DC_STEELWALL_1_RIGHT;
5833 case 0x1699: // steel wall (top)
5834 element = EL_DC_STEELWALL_1_TOP;
5837 case 0x169a: // steel wall (left/bottom)
5838 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5841 case 0x169b: // steel wall (right/bottom)
5842 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5845 case 0x169c: // steel wall (right/top)
5846 element = EL_DC_STEELWALL_1_TOPRIGHT;
5849 case 0x169d: // steel wall (left/top)
5850 element = EL_DC_STEELWALL_1_TOPLEFT;
5853 case 0x169e: // steel wall (right/bottom small)
5854 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5857 case 0x169f: // steel wall (left/bottom small)
5858 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5861 case 0x16a0: // steel wall (right/top small)
5862 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5865 case 0x16a1: // steel wall (left/top small)
5866 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5869 case 0x16a2: // steel wall (left/right)
5870 element = EL_DC_STEELWALL_1_VERTICAL;
5873 case 0x16a3: // steel wall (top/bottom)
5874 element = EL_DC_STEELWALL_1_HORIZONTAL;
5877 case 0x16a4: // steel wall 2 (left end)
5878 element = EL_DC_STEELWALL_2_LEFT;
5881 case 0x16a5: // steel wall 2 (right end)
5882 element = EL_DC_STEELWALL_2_RIGHT;
5885 case 0x16a6: // steel wall 2 (top end)
5886 element = EL_DC_STEELWALL_2_TOP;
5889 case 0x16a7: // steel wall 2 (bottom end)
5890 element = EL_DC_STEELWALL_2_BOTTOM;
5893 case 0x16a8: // steel wall 2 (left/right)
5894 element = EL_DC_STEELWALL_2_HORIZONTAL;
5897 case 0x16a9: // steel wall 2 (up/down)
5898 element = EL_DC_STEELWALL_2_VERTICAL;
5901 case 0x16aa: // steel wall 2 (mid)
5902 element = EL_DC_STEELWALL_2_MIDDLE;
5906 element = EL_SIGN_EXCLAMATION;
5910 element = EL_SIGN_RADIOACTIVITY;
5914 element = EL_SIGN_STOP;
5918 element = EL_SIGN_WHEELCHAIR;
5922 element = EL_SIGN_PARKING;
5926 element = EL_SIGN_NO_ENTRY;
5930 element = EL_SIGN_HEART;
5934 element = EL_SIGN_GIVE_WAY;
5938 element = EL_SIGN_ENTRY_FORBIDDEN;
5942 element = EL_SIGN_EMERGENCY_EXIT;
5946 element = EL_SIGN_YIN_YANG;
5950 element = EL_WALL_EMERALD;
5954 element = EL_WALL_DIAMOND;
5958 element = EL_WALL_PEARL;
5962 element = EL_WALL_CRYSTAL;
5966 element = EL_INVISIBLE_WALL;
5970 element = EL_INVISIBLE_STEELWALL;
5974 // EL_INVISIBLE_SAND
5977 element = EL_LIGHT_SWITCH;
5981 element = EL_ENVELOPE_1;
5985 if (element >= 0x0117 && element <= 0x036e) // (?)
5986 element = EL_DIAMOND;
5987 else if (element >= 0x042d && element <= 0x0684) // (?)
5988 element = EL_EMERALD;
5989 else if (element >= 0x157c && element <= 0x158b)
5991 else if (element >= 0x1590 && element <= 0x159f)
5992 element = EL_DC_LANDMINE;
5993 else if (element >= 0x16bc && element <= 0x16cb)
5994 element = EL_INVISIBLE_SAND;
5997 Warn("unknown Diamond Caves element 0x%04x", element);
5999 element = EL_UNKNOWN;
6004 return getMappedElement(element);
6007 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6009 byte header[DC_LEVEL_HEADER_SIZE];
6011 int envelope_header_pos = 62;
6012 int envelope_content_pos = 94;
6013 int level_name_pos = 251;
6014 int level_author_pos = 292;
6015 int envelope_header_len;
6016 int envelope_content_len;
6018 int level_author_len;
6020 int num_yamyam_contents;
6023 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6025 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6027 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6029 header[i * 2 + 0] = header_word >> 8;
6030 header[i * 2 + 1] = header_word & 0xff;
6033 // read some values from level header to check level decoding integrity
6034 fieldx = header[6] | (header[7] << 8);
6035 fieldy = header[8] | (header[9] << 8);
6036 num_yamyam_contents = header[60] | (header[61] << 8);
6038 // do some simple sanity checks to ensure that level was correctly decoded
6039 if (fieldx < 1 || fieldx > 256 ||
6040 fieldy < 1 || fieldy > 256 ||
6041 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6043 level->no_valid_file = TRUE;
6045 Warn("cannot decode level from stream -- using empty level");
6050 // maximum envelope header size is 31 bytes
6051 envelope_header_len = header[envelope_header_pos];
6052 // maximum envelope content size is 110 (156?) bytes
6053 envelope_content_len = header[envelope_content_pos];
6055 // maximum level title size is 40 bytes
6056 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6057 // maximum level author size is 30 (51?) bytes
6058 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6062 for (i = 0; i < envelope_header_len; i++)
6063 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6064 level->envelope[0].text[envelope_size++] =
6065 header[envelope_header_pos + 1 + i];
6067 if (envelope_header_len > 0 && envelope_content_len > 0)
6069 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6070 level->envelope[0].text[envelope_size++] = '\n';
6071 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6072 level->envelope[0].text[envelope_size++] = '\n';
6075 for (i = 0; i < envelope_content_len; i++)
6076 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6077 level->envelope[0].text[envelope_size++] =
6078 header[envelope_content_pos + 1 + i];
6080 level->envelope[0].text[envelope_size] = '\0';
6082 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6083 level->envelope[0].ysize = 10;
6084 level->envelope[0].autowrap = TRUE;
6085 level->envelope[0].centered = TRUE;
6087 for (i = 0; i < level_name_len; i++)
6088 level->name[i] = header[level_name_pos + 1 + i];
6089 level->name[level_name_len] = '\0';
6091 for (i = 0; i < level_author_len; i++)
6092 level->author[i] = header[level_author_pos + 1 + i];
6093 level->author[level_author_len] = '\0';
6095 num_yamyam_contents = header[60] | (header[61] << 8);
6096 level->num_yamyam_contents =
6097 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6099 for (i = 0; i < num_yamyam_contents; i++)
6101 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6103 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6104 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6106 if (i < MAX_ELEMENT_CONTENTS)
6107 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6111 fieldx = header[6] | (header[7] << 8);
6112 fieldy = header[8] | (header[9] << 8);
6113 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6114 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6116 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6118 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6119 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6121 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6122 level->field[x][y] = getMappedElement_DC(element_dc);
6125 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6126 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6127 level->field[x][y] = EL_PLAYER_1;
6129 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6130 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6131 level->field[x][y] = EL_PLAYER_2;
6133 level->gems_needed = header[18] | (header[19] << 8);
6135 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6136 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6137 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6138 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6139 level->score[SC_NUT] = header[28] | (header[29] << 8);
6140 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6141 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6142 level->score[SC_BUG] = header[34] | (header[35] << 8);
6143 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6144 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6145 level->score[SC_KEY] = header[40] | (header[41] << 8);
6146 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6148 level->time = header[44] | (header[45] << 8);
6150 level->amoeba_speed = header[46] | (header[47] << 8);
6151 level->time_light = header[48] | (header[49] << 8);
6152 level->time_timegate = header[50] | (header[51] << 8);
6153 level->time_wheel = header[52] | (header[53] << 8);
6154 level->time_magic_wall = header[54] | (header[55] << 8);
6155 level->extra_time = header[56] | (header[57] << 8);
6156 level->shield_normal_time = header[58] | (header[59] << 8);
6158 // shield and extra time elements do not have a score
6159 level->score[SC_SHIELD] = 0;
6160 level->extra_time_score = 0;
6162 // set time for normal and deadly shields to the same value
6163 level->shield_deadly_time = level->shield_normal_time;
6165 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6166 // can slip down from flat walls, like normal walls and steel walls
6167 level->em_slippery_gems = TRUE;
6169 // time score is counted for each 10 seconds left in Diamond Caves levels
6170 level->time_score_base = 10;
6173 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6174 struct LevelFileInfo *level_file_info,
6175 boolean level_info_only)
6177 char *filename = level_file_info->filename;
6179 int num_magic_bytes = 8;
6180 char magic_bytes[num_magic_bytes + 1];
6181 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6183 if (!(file = openFile(filename, MODE_READ)))
6185 level->no_valid_file = TRUE;
6187 if (!level_info_only)
6188 Warn("cannot read level '%s' -- using empty level", filename);
6193 // fseek(file, 0x0000, SEEK_SET);
6195 if (level_file_info->packed)
6197 // read "magic bytes" from start of file
6198 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6199 magic_bytes[0] = '\0';
6201 // check "magic bytes" for correct file format
6202 if (!strPrefix(magic_bytes, "DC2"))
6204 level->no_valid_file = TRUE;
6206 Warn("unknown DC level file '%s' -- using empty level", filename);
6211 if (strPrefix(magic_bytes, "DC2Win95") ||
6212 strPrefix(magic_bytes, "DC2Win98"))
6214 int position_first_level = 0x00fa;
6215 int extra_bytes = 4;
6218 // advance file stream to first level inside the level package
6219 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6221 // each block of level data is followed by block of non-level data
6222 num_levels_to_skip *= 2;
6224 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6225 while (num_levels_to_skip >= 0)
6227 // advance file stream to next level inside the level package
6228 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6230 level->no_valid_file = TRUE;
6232 Warn("cannot fseek in file '%s' -- using empty level", filename);
6237 // skip apparently unused extra bytes following each level
6238 ReadUnusedBytesFromFile(file, extra_bytes);
6240 // read size of next level in level package
6241 skip_bytes = getFile32BitLE(file);
6243 num_levels_to_skip--;
6248 level->no_valid_file = TRUE;
6250 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6256 LoadLevelFromFileStream_DC(file, level);
6262 // ----------------------------------------------------------------------------
6263 // functions for loading SB level
6264 // ----------------------------------------------------------------------------
6266 int getMappedElement_SB(int element_ascii, boolean use_ces)
6274 sb_element_mapping[] =
6276 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6277 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6278 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6279 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6280 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6281 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6282 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6283 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6290 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6291 if (element_ascii == sb_element_mapping[i].ascii)
6292 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6294 return EL_UNDEFINED;
6297 static void SetLevelSettings_SB(struct LevelInfo *level)
6301 level->use_step_counter = TRUE;
6304 level->score[SC_TIME_BONUS] = 0;
6305 level->time_score_base = 1;
6306 level->rate_time_over_score = TRUE;
6309 level->auto_exit_sokoban = TRUE;
6312 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6313 struct LevelFileInfo *level_file_info,
6314 boolean level_info_only)
6316 char *filename = level_file_info->filename;
6317 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6318 char last_comment[MAX_LINE_LEN];
6319 char level_name[MAX_LINE_LEN];
6322 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6323 boolean read_continued_line = FALSE;
6324 boolean reading_playfield = FALSE;
6325 boolean got_valid_playfield_line = FALSE;
6326 boolean invalid_playfield_char = FALSE;
6327 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6328 int file_level_nr = 0;
6329 int x = 0, y = 0; // initialized to make compilers happy
6331 last_comment[0] = '\0';
6332 level_name[0] = '\0';
6334 if (!(file = openFile(filename, MODE_READ)))
6336 level->no_valid_file = TRUE;
6338 if (!level_info_only)
6339 Warn("cannot read level '%s' -- using empty level", filename);
6344 while (!checkEndOfFile(file))
6346 // level successfully read, but next level may follow here
6347 if (!got_valid_playfield_line && reading_playfield)
6349 // read playfield from single level file -- skip remaining file
6350 if (!level_file_info->packed)
6353 if (file_level_nr >= num_levels_to_skip)
6358 last_comment[0] = '\0';
6359 level_name[0] = '\0';
6361 reading_playfield = FALSE;
6364 got_valid_playfield_line = FALSE;
6366 // read next line of input file
6367 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6370 // cut trailing line break (this can be newline and/or carriage return)
6371 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6372 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6375 // copy raw input line for later use (mainly debugging output)
6376 strcpy(line_raw, line);
6378 if (read_continued_line)
6380 // append new line to existing line, if there is enough space
6381 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6382 strcat(previous_line, line_ptr);
6384 strcpy(line, previous_line); // copy storage buffer to line
6386 read_continued_line = FALSE;
6389 // if the last character is '\', continue at next line
6390 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6392 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6393 strcpy(previous_line, line); // copy line to storage buffer
6395 read_continued_line = TRUE;
6401 if (line[0] == '\0')
6404 // extract comment text from comment line
6407 for (line_ptr = line; *line_ptr; line_ptr++)
6408 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6411 strcpy(last_comment, line_ptr);
6416 // extract level title text from line containing level title
6417 if (line[0] == '\'')
6419 strcpy(level_name, &line[1]);
6421 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6422 level_name[strlen(level_name) - 1] = '\0';
6427 // skip lines containing only spaces (or empty lines)
6428 for (line_ptr = line; *line_ptr; line_ptr++)
6429 if (*line_ptr != ' ')
6431 if (*line_ptr == '\0')
6434 // at this point, we have found a line containing part of a playfield
6436 got_valid_playfield_line = TRUE;
6438 if (!reading_playfield)
6440 reading_playfield = TRUE;
6441 invalid_playfield_char = FALSE;
6443 for (x = 0; x < MAX_LEV_FIELDX; x++)
6444 for (y = 0; y < MAX_LEV_FIELDY; y++)
6445 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6450 // start with topmost tile row
6454 // skip playfield line if larger row than allowed
6455 if (y >= MAX_LEV_FIELDY)
6458 // start with leftmost tile column
6461 // read playfield elements from line
6462 for (line_ptr = line; *line_ptr; line_ptr++)
6464 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6466 // stop parsing playfield line if larger column than allowed
6467 if (x >= MAX_LEV_FIELDX)
6470 if (mapped_sb_element == EL_UNDEFINED)
6472 invalid_playfield_char = TRUE;
6477 level->field[x][y] = mapped_sb_element;
6479 // continue with next tile column
6482 level->fieldx = MAX(x, level->fieldx);
6485 if (invalid_playfield_char)
6487 // if first playfield line, treat invalid lines as comment lines
6489 reading_playfield = FALSE;
6494 // continue with next tile row
6502 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6503 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6505 if (!reading_playfield)
6507 level->no_valid_file = TRUE;
6509 Warn("cannot read level '%s' -- using empty level", filename);
6514 if (*level_name != '\0')
6516 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6517 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6519 else if (*last_comment != '\0')
6521 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6522 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6526 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6529 // set all empty fields beyond the border walls to invisible steel wall
6530 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6532 if ((x == 0 || x == level->fieldx - 1 ||
6533 y == 0 || y == level->fieldy - 1) &&
6534 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6535 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6536 level->field, level->fieldx, level->fieldy);
6539 // set special level settings for Sokoban levels
6540 SetLevelSettings_SB(level);
6542 if (load_xsb_to_ces)
6544 // special global settings can now be set in level template
6545 level->use_custom_template = TRUE;
6550 // -------------------------------------------------------------------------
6551 // functions for handling native levels
6552 // -------------------------------------------------------------------------
6554 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6555 struct LevelFileInfo *level_file_info,
6556 boolean level_info_only)
6560 // determine position of requested level inside level package
6561 if (level_file_info->packed)
6562 pos = level_file_info->nr - leveldir_current->first_level;
6564 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6565 level->no_valid_file = TRUE;
6568 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6569 struct LevelFileInfo *level_file_info,
6570 boolean level_info_only)
6572 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6573 level->no_valid_file = TRUE;
6576 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6577 struct LevelFileInfo *level_file_info,
6578 boolean level_info_only)
6582 // determine position of requested level inside level package
6583 if (level_file_info->packed)
6584 pos = level_file_info->nr - leveldir_current->first_level;
6586 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6587 level->no_valid_file = TRUE;
6590 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6591 struct LevelFileInfo *level_file_info,
6592 boolean level_info_only)
6594 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6595 level->no_valid_file = TRUE;
6598 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6600 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6601 CopyNativeLevel_RND_to_BD(level);
6602 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6603 CopyNativeLevel_RND_to_EM(level);
6604 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6605 CopyNativeLevel_RND_to_SP(level);
6606 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6607 CopyNativeLevel_RND_to_MM(level);
6610 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6612 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6613 CopyNativeLevel_BD_to_RND(level);
6614 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6615 CopyNativeLevel_EM_to_RND(level);
6616 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6617 CopyNativeLevel_SP_to_RND(level);
6618 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6619 CopyNativeLevel_MM_to_RND(level);
6622 void SaveNativeLevel(struct LevelInfo *level)
6624 // saving native level files only supported for some game engines
6625 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6626 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6629 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6630 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6631 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6632 char *filename = getLevelFilenameFromBasename(basename);
6634 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6637 boolean success = FALSE;
6639 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6641 CopyNativeLevel_RND_to_BD(level);
6642 // CopyNativeTape_RND_to_BD(level);
6644 success = SaveNativeLevel_BD(filename);
6646 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6648 CopyNativeLevel_RND_to_SP(level);
6649 CopyNativeTape_RND_to_SP(level);
6651 success = SaveNativeLevel_SP(filename);
6655 Request("Native level file saved!", REQ_CONFIRM);
6657 Request("Failed to save native level file!", REQ_CONFIRM);
6661 // ----------------------------------------------------------------------------
6662 // functions for loading generic level
6663 // ----------------------------------------------------------------------------
6665 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6666 struct LevelFileInfo *level_file_info,
6667 boolean level_info_only)
6669 // always start with reliable default values
6670 setLevelInfoToDefaults(level, level_info_only, TRUE);
6672 switch (level_file_info->type)
6674 case LEVEL_FILE_TYPE_RND:
6675 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6678 case LEVEL_FILE_TYPE_BD:
6679 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6680 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6683 case LEVEL_FILE_TYPE_EM:
6684 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6685 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6688 case LEVEL_FILE_TYPE_SP:
6689 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6690 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6693 case LEVEL_FILE_TYPE_MM:
6694 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6695 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6698 case LEVEL_FILE_TYPE_DC:
6699 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6702 case LEVEL_FILE_TYPE_SB:
6703 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6707 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6711 // if level file is invalid, restore level structure to default values
6712 if (level->no_valid_file)
6713 setLevelInfoToDefaults(level, level_info_only, FALSE);
6715 if (check_special_flags("use_native_bd_game_engine"))
6716 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6718 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6719 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6721 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6722 CopyNativeLevel_Native_to_RND(level);
6725 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6727 static struct LevelFileInfo level_file_info;
6729 // always start with reliable default values
6730 setFileInfoToDefaults(&level_file_info);
6732 level_file_info.nr = 0; // unknown level number
6733 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6735 setString(&level_file_info.filename, filename);
6737 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6740 static void LoadLevel_InitVersion(struct LevelInfo *level)
6744 if (leveldir_current == NULL) // only when dumping level
6747 // all engine modifications also valid for levels which use latest engine
6748 if (level->game_version < VERSION_IDENT(3,2,0,5))
6750 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6751 level->time_score_base = 10;
6754 if (leveldir_current->latest_engine)
6756 // ---------- use latest game engine --------------------------------------
6758 /* For all levels which are forced to use the latest game engine version
6759 (normally all but user contributed, private and undefined levels), set
6760 the game engine version to the actual version; this allows for actual
6761 corrections in the game engine to take effect for existing, converted
6762 levels (from "classic" or other existing games) to make the emulation
6763 of the corresponding game more accurate, while (hopefully) not breaking
6764 existing levels created from other players. */
6766 level->game_version = GAME_VERSION_ACTUAL;
6768 /* Set special EM style gems behaviour: EM style gems slip down from
6769 normal, steel and growing wall. As this is a more fundamental change,
6770 it seems better to set the default behaviour to "off" (as it is more
6771 natural) and make it configurable in the level editor (as a property
6772 of gem style elements). Already existing converted levels (neither
6773 private nor contributed levels) are changed to the new behaviour. */
6775 if (level->file_version < FILE_VERSION_2_0)
6776 level->em_slippery_gems = TRUE;
6781 // ---------- use game engine the level was created with --------------------
6783 /* For all levels which are not forced to use the latest game engine
6784 version (normally user contributed, private and undefined levels),
6785 use the version of the game engine the levels were created for.
6787 Since 2.0.1, the game engine version is now directly stored
6788 in the level file (chunk "VERS"), so there is no need anymore
6789 to set the game version from the file version (except for old,
6790 pre-2.0 levels, where the game version is still taken from the
6791 file format version used to store the level -- see above). */
6793 // player was faster than enemies in 1.0.0 and before
6794 if (level->file_version == FILE_VERSION_1_0)
6795 for (i = 0; i < MAX_PLAYERS; i++)
6796 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6798 // default behaviour for EM style gems was "slippery" only in 2.0.1
6799 if (level->game_version == VERSION_IDENT(2,0,1,0))
6800 level->em_slippery_gems = TRUE;
6802 // springs could be pushed over pits before (pre-release version) 2.2.0
6803 if (level->game_version < VERSION_IDENT(2,2,0,0))
6804 level->use_spring_bug = TRUE;
6806 if (level->game_version < VERSION_IDENT(3,2,0,5))
6808 // time orb caused limited time in endless time levels before 3.2.0-5
6809 level->use_time_orb_bug = TRUE;
6811 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6812 level->block_snap_field = FALSE;
6814 // extra time score was same value as time left score before 3.2.0-5
6815 level->extra_time_score = level->score[SC_TIME_BONUS];
6818 if (level->game_version < VERSION_IDENT(3,2,0,7))
6820 // default behaviour for snapping was "not continuous" before 3.2.0-7
6821 level->continuous_snapping = FALSE;
6824 // only few elements were able to actively move into acid before 3.1.0
6825 // trigger settings did not exist before 3.1.0; set to default "any"
6826 if (level->game_version < VERSION_IDENT(3,1,0,0))
6828 // correct "can move into acid" settings (all zero in old levels)
6830 level->can_move_into_acid_bits = 0; // nothing can move into acid
6831 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6833 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6834 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6835 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6836 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6838 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6839 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6841 // correct trigger settings (stored as zero == "none" in old levels)
6843 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6845 int element = EL_CUSTOM_START + i;
6846 struct ElementInfo *ei = &element_info[element];
6848 for (j = 0; j < ei->num_change_pages; j++)
6850 struct ElementChangeInfo *change = &ei->change_page[j];
6852 change->trigger_player = CH_PLAYER_ANY;
6853 change->trigger_page = CH_PAGE_ANY;
6858 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6860 int element = EL_CUSTOM_256;
6861 struct ElementInfo *ei = &element_info[element];
6862 struct ElementChangeInfo *change = &ei->change_page[0];
6864 /* This is needed to fix a problem that was caused by a bugfix in function
6865 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6866 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6867 not replace walkable elements, but instead just placed the player on it,
6868 without placing the Sokoban field under the player). Unfortunately, this
6869 breaks "Snake Bite" style levels when the snake is halfway through a door
6870 that just closes (the snake head is still alive and can be moved in this
6871 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6872 player (without Sokoban element) which then gets killed as designed). */
6874 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6875 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6876 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6877 change->target_element = EL_PLAYER_1;
6880 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6881 if (level->game_version < VERSION_IDENT(3,2,5,0))
6883 /* This is needed to fix a problem that was caused by a bugfix in function
6884 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6885 corrects the behaviour when a custom element changes to another custom
6886 element with a higher element number that has change actions defined.
6887 Normally, only one change per frame is allowed for custom elements.
6888 Therefore, it is checked if a custom element already changed in the
6889 current frame; if it did, subsequent changes are suppressed.
6890 Unfortunately, this is only checked for element changes, but not for
6891 change actions, which are still executed. As the function above loops
6892 through all custom elements from lower to higher, an element change
6893 resulting in a lower CE number won't be checked again, while a target
6894 element with a higher number will also be checked, and potential change
6895 actions will get executed for this CE, too (which is wrong), while
6896 further changes are ignored (which is correct). As this bugfix breaks
6897 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6898 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6899 behaviour for existing levels and tapes that make use of this bug */
6901 level->use_action_after_change_bug = TRUE;
6904 // not centering level after relocating player was default only in 3.2.3
6905 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6906 level->shifted_relocation = TRUE;
6908 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6909 if (level->game_version < VERSION_IDENT(3,2,6,0))
6910 level->em_explodes_by_fire = TRUE;
6912 // levels were solved by the first player entering an exit up to 4.1.0.0
6913 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6914 level->solved_by_one_player = TRUE;
6916 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6917 if (level->game_version < VERSION_IDENT(4,1,1,1))
6918 level->use_life_bugs = TRUE;
6920 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6921 if (level->game_version < VERSION_IDENT(4,1,1,1))
6922 level->sb_objects_needed = FALSE;
6924 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6925 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6926 level->finish_dig_collect = FALSE;
6928 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6929 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6930 level->keep_walkable_ce = TRUE;
6933 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6935 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6938 // check if this level is (not) a Sokoban level
6939 for (y = 0; y < level->fieldy; y++)
6940 for (x = 0; x < level->fieldx; x++)
6941 if (!IS_SB_ELEMENT(Tile[x][y]))
6942 is_sokoban_level = FALSE;
6944 if (is_sokoban_level)
6946 // set special level settings for Sokoban levels
6947 SetLevelSettings_SB(level);
6951 static void LoadLevel_InitSettings(struct LevelInfo *level)
6953 // adjust level settings for (non-native) Sokoban-style levels
6954 LoadLevel_InitSettings_SB(level);
6956 // rename levels with title "nameless level" or if renaming is forced
6957 if (leveldir_current->empty_level_name != NULL &&
6958 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6959 leveldir_current->force_level_name))
6960 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6961 leveldir_current->empty_level_name, level_nr);
6964 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6968 // map elements that have changed in newer versions
6969 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6970 level->game_version);
6971 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6972 for (x = 0; x < 3; x++)
6973 for (y = 0; y < 3; y++)
6974 level->yamyam_content[i].e[x][y] =
6975 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6976 level->game_version);
6980 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6984 // map custom element change events that have changed in newer versions
6985 // (these following values were accidentally changed in version 3.0.1)
6986 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6987 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6989 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6991 int element = EL_CUSTOM_START + i;
6993 // order of checking and copying events to be mapped is important
6994 // (do not change the start and end value -- they are constant)
6995 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6997 if (HAS_CHANGE_EVENT(element, j - 2))
6999 SET_CHANGE_EVENT(element, j - 2, FALSE);
7000 SET_CHANGE_EVENT(element, j, TRUE);
7004 // order of checking and copying events to be mapped is important
7005 // (do not change the start and end value -- they are constant)
7006 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7008 if (HAS_CHANGE_EVENT(element, j - 1))
7010 SET_CHANGE_EVENT(element, j - 1, FALSE);
7011 SET_CHANGE_EVENT(element, j, TRUE);
7017 // initialize "can_change" field for old levels with only one change page
7018 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7020 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7022 int element = EL_CUSTOM_START + i;
7024 if (CAN_CHANGE(element))
7025 element_info[element].change->can_change = TRUE;
7029 // correct custom element values (for old levels without these options)
7030 if (level->game_version < VERSION_IDENT(3,1,1,0))
7032 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7034 int element = EL_CUSTOM_START + i;
7035 struct ElementInfo *ei = &element_info[element];
7037 if (ei->access_direction == MV_NO_DIRECTION)
7038 ei->access_direction = MV_ALL_DIRECTIONS;
7042 // correct custom element values (fix invalid values for all versions)
7045 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7047 int element = EL_CUSTOM_START + i;
7048 struct ElementInfo *ei = &element_info[element];
7050 for (j = 0; j < ei->num_change_pages; j++)
7052 struct ElementChangeInfo *change = &ei->change_page[j];
7054 if (change->trigger_player == CH_PLAYER_NONE)
7055 change->trigger_player = CH_PLAYER_ANY;
7057 if (change->trigger_side == CH_SIDE_NONE)
7058 change->trigger_side = CH_SIDE_ANY;
7063 // initialize "can_explode" field for old levels which did not store this
7064 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7065 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7067 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7069 int element = EL_CUSTOM_START + i;
7071 if (EXPLODES_1X1_OLD(element))
7072 element_info[element].explosion_type = EXPLODES_1X1;
7074 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7075 EXPLODES_SMASHED(element) ||
7076 EXPLODES_IMPACT(element)));
7080 // correct previously hard-coded move delay values for maze runner style
7081 if (level->game_version < VERSION_IDENT(3,1,1,0))
7083 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7085 int element = EL_CUSTOM_START + i;
7087 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7089 // previously hard-coded and therefore ignored
7090 element_info[element].move_delay_fixed = 9;
7091 element_info[element].move_delay_random = 0;
7096 // set some other uninitialized values of custom elements in older levels
7097 if (level->game_version < VERSION_IDENT(3,1,0,0))
7099 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7101 int element = EL_CUSTOM_START + i;
7103 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7105 element_info[element].explosion_delay = 17;
7106 element_info[element].ignition_delay = 8;
7110 // set mouse click change events to work for left/middle/right mouse button
7111 if (level->game_version < VERSION_IDENT(4,2,3,0))
7113 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7115 int element = EL_CUSTOM_START + i;
7116 struct ElementInfo *ei = &element_info[element];
7118 for (j = 0; j < ei->num_change_pages; j++)
7120 struct ElementChangeInfo *change = &ei->change_page[j];
7122 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7123 change->has_event[CE_PRESSED_BY_MOUSE] ||
7124 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7125 change->has_event[CE_MOUSE_PRESSED_ON_X])
7126 change->trigger_side = CH_SIDE_ANY;
7132 static void LoadLevel_InitElements(struct LevelInfo *level)
7134 LoadLevel_InitStandardElements(level);
7136 if (level->file_has_custom_elements)
7137 LoadLevel_InitCustomElements(level);
7139 // initialize element properties for level editor etc.
7140 InitElementPropertiesEngine(level->game_version);
7141 InitElementPropertiesGfxElement();
7144 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7148 // map elements that have changed in newer versions
7149 for (y = 0; y < level->fieldy; y++)
7150 for (x = 0; x < level->fieldx; x++)
7151 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7152 level->game_version);
7154 // clear unused playfield data (nicer if level gets resized in editor)
7155 for (x = 0; x < MAX_LEV_FIELDX; x++)
7156 for (y = 0; y < MAX_LEV_FIELDY; y++)
7157 if (x >= level->fieldx || y >= level->fieldy)
7158 level->field[x][y] = EL_EMPTY;
7160 // copy elements to runtime playfield array
7161 for (x = 0; x < MAX_LEV_FIELDX; x++)
7162 for (y = 0; y < MAX_LEV_FIELDY; y++)
7163 Tile[x][y] = level->field[x][y];
7165 // initialize level size variables for faster access
7166 lev_fieldx = level->fieldx;
7167 lev_fieldy = level->fieldy;
7169 // determine border element for this level
7170 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7171 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7176 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7178 struct LevelFileInfo *level_file_info = &level->file_info;
7180 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7181 CopyNativeLevel_RND_to_Native(level);
7184 static void LoadLevelTemplate_LoadAndInit(void)
7186 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7188 LoadLevel_InitVersion(&level_template);
7189 LoadLevel_InitElements(&level_template);
7190 LoadLevel_InitSettings(&level_template);
7192 ActivateLevelTemplate();
7195 void LoadLevelTemplate(int nr)
7197 if (!fileExists(getGlobalLevelTemplateFilename()))
7199 Warn("no level template found for this level");
7204 setLevelFileInfo(&level_template.file_info, nr);
7206 LoadLevelTemplate_LoadAndInit();
7209 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7211 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7213 LoadLevelTemplate_LoadAndInit();
7216 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7218 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7220 if (level.use_custom_template)
7222 if (network_level != NULL)
7223 LoadNetworkLevelTemplate(network_level);
7225 LoadLevelTemplate(-1);
7228 LoadLevel_InitVersion(&level);
7229 LoadLevel_InitElements(&level);
7230 LoadLevel_InitPlayfield(&level);
7231 LoadLevel_InitSettings(&level);
7233 LoadLevel_InitNativeEngines(&level);
7236 void LoadLevel(int nr)
7238 SetLevelSetInfo(leveldir_current->identifier, nr);
7240 setLevelFileInfo(&level.file_info, nr);
7242 LoadLevel_LoadAndInit(NULL);
7245 void LoadLevelInfoOnly(int nr)
7247 setLevelFileInfo(&level.file_info, nr);
7249 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7252 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7254 SetLevelSetInfo(network_level->leveldir_identifier,
7255 network_level->file_info.nr);
7257 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7259 LoadLevel_LoadAndInit(network_level);
7262 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7266 chunk_size += putFileVersion(file, level->file_version);
7267 chunk_size += putFileVersion(file, level->game_version);
7272 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7276 chunk_size += putFile16BitBE(file, level->creation_date.year);
7277 chunk_size += putFile8Bit(file, level->creation_date.month);
7278 chunk_size += putFile8Bit(file, level->creation_date.day);
7283 #if ENABLE_HISTORIC_CHUNKS
7284 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7288 putFile8Bit(file, level->fieldx);
7289 putFile8Bit(file, level->fieldy);
7291 putFile16BitBE(file, level->time);
7292 putFile16BitBE(file, level->gems_needed);
7294 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7295 putFile8Bit(file, level->name[i]);
7297 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7298 putFile8Bit(file, level->score[i]);
7300 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7301 for (y = 0; y < 3; y++)
7302 for (x = 0; x < 3; x++)
7303 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7304 level->yamyam_content[i].e[x][y]));
7305 putFile8Bit(file, level->amoeba_speed);
7306 putFile8Bit(file, level->time_magic_wall);
7307 putFile8Bit(file, level->time_wheel);
7308 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7309 level->amoeba_content));
7310 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7311 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7312 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7313 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7315 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7317 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7318 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7319 putFile32BitBE(file, level->can_move_into_acid_bits);
7320 putFile8Bit(file, level->dont_collide_with_bits);
7322 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7323 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7325 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7326 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7327 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7329 putFile8Bit(file, level->game_engine_type);
7331 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7335 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7340 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7341 chunk_size += putFile8Bit(file, level->name[i]);
7346 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7351 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7352 chunk_size += putFile8Bit(file, level->author[i]);
7357 #if ENABLE_HISTORIC_CHUNKS
7358 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7363 for (y = 0; y < level->fieldy; y++)
7364 for (x = 0; x < level->fieldx; x++)
7365 if (level->encoding_16bit_field)
7366 chunk_size += putFile16BitBE(file, level->field[x][y]);
7368 chunk_size += putFile8Bit(file, level->field[x][y]);
7374 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7379 for (y = 0; y < level->fieldy; y++)
7380 for (x = 0; x < level->fieldx; x++)
7381 chunk_size += putFile16BitBE(file, level->field[x][y]);
7386 #if ENABLE_HISTORIC_CHUNKS
7387 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7391 putFile8Bit(file, EL_YAMYAM);
7392 putFile8Bit(file, level->num_yamyam_contents);
7393 putFile8Bit(file, 0);
7394 putFile8Bit(file, 0);
7396 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7397 for (y = 0; y < 3; y++)
7398 for (x = 0; x < 3; x++)
7399 if (level->encoding_16bit_field)
7400 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7402 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7406 #if ENABLE_HISTORIC_CHUNKS
7407 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7410 int num_contents, content_xsize, content_ysize;
7411 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7413 if (element == EL_YAMYAM)
7415 num_contents = level->num_yamyam_contents;
7419 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7420 for (y = 0; y < 3; y++)
7421 for (x = 0; x < 3; x++)
7422 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7424 else if (element == EL_BD_AMOEBA)
7430 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7431 for (y = 0; y < 3; y++)
7432 for (x = 0; x < 3; x++)
7433 content_array[i][x][y] = EL_EMPTY;
7434 content_array[0][0][0] = level->amoeba_content;
7438 // chunk header already written -- write empty chunk data
7439 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7441 Warn("cannot save content for element '%d'", element);
7446 putFile16BitBE(file, element);
7447 putFile8Bit(file, num_contents);
7448 putFile8Bit(file, content_xsize);
7449 putFile8Bit(file, content_ysize);
7451 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7453 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7454 for (y = 0; y < 3; y++)
7455 for (x = 0; x < 3; x++)
7456 putFile16BitBE(file, content_array[i][x][y]);
7460 #if ENABLE_HISTORIC_CHUNKS
7461 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7463 int envelope_nr = element - EL_ENVELOPE_1;
7464 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7468 chunk_size += putFile16BitBE(file, element);
7469 chunk_size += putFile16BitBE(file, envelope_len);
7470 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7471 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7473 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7474 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7476 for (i = 0; i < envelope_len; i++)
7477 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7483 #if ENABLE_HISTORIC_CHUNKS
7484 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7485 int num_changed_custom_elements)
7489 putFile16BitBE(file, num_changed_custom_elements);
7491 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7493 int element = EL_CUSTOM_START + i;
7495 struct ElementInfo *ei = &element_info[element];
7497 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7499 if (check < num_changed_custom_elements)
7501 putFile16BitBE(file, element);
7502 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7509 if (check != num_changed_custom_elements) // should not happen
7510 Warn("inconsistent number of custom element properties");
7514 #if ENABLE_HISTORIC_CHUNKS
7515 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7516 int num_changed_custom_elements)
7520 putFile16BitBE(file, num_changed_custom_elements);
7522 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7524 int element = EL_CUSTOM_START + i;
7526 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7528 if (check < num_changed_custom_elements)
7530 putFile16BitBE(file, element);
7531 putFile16BitBE(file, element_info[element].change->target_element);
7538 if (check != num_changed_custom_elements) // should not happen
7539 Warn("inconsistent number of custom target elements");
7543 #if ENABLE_HISTORIC_CHUNKS
7544 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7545 int num_changed_custom_elements)
7547 int i, j, x, y, check = 0;
7549 putFile16BitBE(file, num_changed_custom_elements);
7551 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7553 int element = EL_CUSTOM_START + i;
7554 struct ElementInfo *ei = &element_info[element];
7556 if (ei->modified_settings)
7558 if (check < num_changed_custom_elements)
7560 putFile16BitBE(file, element);
7562 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7563 putFile8Bit(file, ei->description[j]);
7565 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7567 // some free bytes for future properties and padding
7568 WriteUnusedBytesToFile(file, 7);
7570 putFile8Bit(file, ei->use_gfx_element);
7571 putFile16BitBE(file, ei->gfx_element_initial);
7573 putFile8Bit(file, ei->collect_score_initial);
7574 putFile8Bit(file, ei->collect_count_initial);
7576 putFile16BitBE(file, ei->push_delay_fixed);
7577 putFile16BitBE(file, ei->push_delay_random);
7578 putFile16BitBE(file, ei->move_delay_fixed);
7579 putFile16BitBE(file, ei->move_delay_random);
7581 putFile16BitBE(file, ei->move_pattern);
7582 putFile8Bit(file, ei->move_direction_initial);
7583 putFile8Bit(file, ei->move_stepsize);
7585 for (y = 0; y < 3; y++)
7586 for (x = 0; x < 3; x++)
7587 putFile16BitBE(file, ei->content.e[x][y]);
7589 putFile32BitBE(file, ei->change->events);
7591 putFile16BitBE(file, ei->change->target_element);
7593 putFile16BitBE(file, ei->change->delay_fixed);
7594 putFile16BitBE(file, ei->change->delay_random);
7595 putFile16BitBE(file, ei->change->delay_frames);
7597 putFile16BitBE(file, ei->change->initial_trigger_element);
7599 putFile8Bit(file, ei->change->explode);
7600 putFile8Bit(file, ei->change->use_target_content);
7601 putFile8Bit(file, ei->change->only_if_complete);
7602 putFile8Bit(file, ei->change->use_random_replace);
7604 putFile8Bit(file, ei->change->random_percentage);
7605 putFile8Bit(file, ei->change->replace_when);
7607 for (y = 0; y < 3; y++)
7608 for (x = 0; x < 3; x++)
7609 putFile16BitBE(file, ei->change->content.e[x][y]);
7611 putFile8Bit(file, ei->slippery_type);
7613 // some free bytes for future properties and padding
7614 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7621 if (check != num_changed_custom_elements) // should not happen
7622 Warn("inconsistent number of custom element properties");
7626 #if ENABLE_HISTORIC_CHUNKS
7627 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7629 struct ElementInfo *ei = &element_info[element];
7632 // ---------- custom element base property values (96 bytes) ----------------
7634 putFile16BitBE(file, element);
7636 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7637 putFile8Bit(file, ei->description[i]);
7639 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7641 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7643 putFile8Bit(file, ei->num_change_pages);
7645 putFile16BitBE(file, ei->ce_value_fixed_initial);
7646 putFile16BitBE(file, ei->ce_value_random_initial);
7647 putFile8Bit(file, ei->use_last_ce_value);
7649 putFile8Bit(file, ei->use_gfx_element);
7650 putFile16BitBE(file, ei->gfx_element_initial);
7652 putFile8Bit(file, ei->collect_score_initial);
7653 putFile8Bit(file, ei->collect_count_initial);
7655 putFile8Bit(file, ei->drop_delay_fixed);
7656 putFile8Bit(file, ei->push_delay_fixed);
7657 putFile8Bit(file, ei->drop_delay_random);
7658 putFile8Bit(file, ei->push_delay_random);
7659 putFile16BitBE(file, ei->move_delay_fixed);
7660 putFile16BitBE(file, ei->move_delay_random);
7662 // bits 0 - 15 of "move_pattern" ...
7663 putFile16BitBE(file, ei->move_pattern & 0xffff);
7664 putFile8Bit(file, ei->move_direction_initial);
7665 putFile8Bit(file, ei->move_stepsize);
7667 putFile8Bit(file, ei->slippery_type);
7669 for (y = 0; y < 3; y++)
7670 for (x = 0; x < 3; x++)
7671 putFile16BitBE(file, ei->content.e[x][y]);
7673 putFile16BitBE(file, ei->move_enter_element);
7674 putFile16BitBE(file, ei->move_leave_element);
7675 putFile8Bit(file, ei->move_leave_type);
7677 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7678 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7680 putFile8Bit(file, ei->access_direction);
7682 putFile8Bit(file, ei->explosion_delay);
7683 putFile8Bit(file, ei->ignition_delay);
7684 putFile8Bit(file, ei->explosion_type);
7686 // some free bytes for future custom property values and padding
7687 WriteUnusedBytesToFile(file, 1);
7689 // ---------- change page property values (48 bytes) ------------------------
7691 for (i = 0; i < ei->num_change_pages; i++)
7693 struct ElementChangeInfo *change = &ei->change_page[i];
7694 unsigned int event_bits;
7696 // bits 0 - 31 of "has_event[]" ...
7698 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7699 if (change->has_event[j])
7700 event_bits |= (1u << j);
7701 putFile32BitBE(file, event_bits);
7703 putFile16BitBE(file, change->target_element);
7705 putFile16BitBE(file, change->delay_fixed);
7706 putFile16BitBE(file, change->delay_random);
7707 putFile16BitBE(file, change->delay_frames);
7709 putFile16BitBE(file, change->initial_trigger_element);
7711 putFile8Bit(file, change->explode);
7712 putFile8Bit(file, change->use_target_content);
7713 putFile8Bit(file, change->only_if_complete);
7714 putFile8Bit(file, change->use_random_replace);
7716 putFile8Bit(file, change->random_percentage);
7717 putFile8Bit(file, change->replace_when);
7719 for (y = 0; y < 3; y++)
7720 for (x = 0; x < 3; x++)
7721 putFile16BitBE(file, change->target_content.e[x][y]);
7723 putFile8Bit(file, change->can_change);
7725 putFile8Bit(file, change->trigger_side);
7727 putFile8Bit(file, change->trigger_player);
7728 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7729 log_2(change->trigger_page)));
7731 putFile8Bit(file, change->has_action);
7732 putFile8Bit(file, change->action_type);
7733 putFile8Bit(file, change->action_mode);
7734 putFile16BitBE(file, change->action_arg);
7736 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7738 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7739 if (change->has_event[j])
7740 event_bits |= (1u << (j - 32));
7741 putFile8Bit(file, event_bits);
7746 #if ENABLE_HISTORIC_CHUNKS
7747 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7749 struct ElementInfo *ei = &element_info[element];
7750 struct ElementGroupInfo *group = ei->group;
7753 putFile16BitBE(file, element);
7755 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7756 putFile8Bit(file, ei->description[i]);
7758 putFile8Bit(file, group->num_elements);
7760 putFile8Bit(file, ei->use_gfx_element);
7761 putFile16BitBE(file, ei->gfx_element_initial);
7763 putFile8Bit(file, group->choice_mode);
7765 // some free bytes for future values and padding
7766 WriteUnusedBytesToFile(file, 3);
7768 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7769 putFile16BitBE(file, group->element[i]);
7773 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7774 boolean write_element)
7776 int save_type = entry->save_type;
7777 int data_type = entry->data_type;
7778 int conf_type = entry->conf_type;
7779 int byte_mask = conf_type & CONF_MASK_BYTES;
7780 int element = entry->element;
7781 int default_value = entry->default_value;
7783 boolean modified = FALSE;
7785 if (byte_mask != CONF_MASK_MULTI_BYTES)
7787 void *value_ptr = entry->value;
7788 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7791 // check if any settings have been modified before saving them
7792 if (value != default_value)
7795 // do not save if explicitly told or if unmodified default settings
7796 if ((save_type == SAVE_CONF_NEVER) ||
7797 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7801 num_bytes += putFile16BitBE(file, element);
7803 num_bytes += putFile8Bit(file, conf_type);
7804 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7805 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7806 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7809 else if (data_type == TYPE_STRING)
7811 char *default_string = entry->default_string;
7812 char *string = (char *)(entry->value);
7813 int string_length = strlen(string);
7816 // check if any settings have been modified before saving them
7817 if (!strEqual(string, default_string))
7820 // do not save if explicitly told or if unmodified default settings
7821 if ((save_type == SAVE_CONF_NEVER) ||
7822 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7826 num_bytes += putFile16BitBE(file, element);
7828 num_bytes += putFile8Bit(file, conf_type);
7829 num_bytes += putFile16BitBE(file, string_length);
7831 for (i = 0; i < string_length; i++)
7832 num_bytes += putFile8Bit(file, string[i]);
7834 else if (data_type == TYPE_ELEMENT_LIST)
7836 int *element_array = (int *)(entry->value);
7837 int num_elements = *(int *)(entry->num_entities);
7840 // check if any settings have been modified before saving them
7841 for (i = 0; i < num_elements; i++)
7842 if (element_array[i] != default_value)
7845 // do not save if explicitly told or if unmodified default settings
7846 if ((save_type == SAVE_CONF_NEVER) ||
7847 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7851 num_bytes += putFile16BitBE(file, element);
7853 num_bytes += putFile8Bit(file, conf_type);
7854 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7856 for (i = 0; i < num_elements; i++)
7857 num_bytes += putFile16BitBE(file, element_array[i]);
7859 else if (data_type == TYPE_CONTENT_LIST)
7861 struct Content *content = (struct Content *)(entry->value);
7862 int num_contents = *(int *)(entry->num_entities);
7865 // check if any settings have been modified before saving them
7866 for (i = 0; i < num_contents; i++)
7867 for (y = 0; y < 3; y++)
7868 for (x = 0; x < 3; x++)
7869 if (content[i].e[x][y] != default_value)
7872 // do not save if explicitly told or if unmodified default settings
7873 if ((save_type == SAVE_CONF_NEVER) ||
7874 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7878 num_bytes += putFile16BitBE(file, element);
7880 num_bytes += putFile8Bit(file, conf_type);
7881 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7883 for (i = 0; i < num_contents; i++)
7884 for (y = 0; y < 3; y++)
7885 for (x = 0; x < 3; x++)
7886 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7892 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7897 li = *level; // copy level data into temporary buffer
7899 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7900 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7905 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7910 li = *level; // copy level data into temporary buffer
7912 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7913 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7918 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7920 int envelope_nr = element - EL_ENVELOPE_1;
7924 chunk_size += putFile16BitBE(file, element);
7926 // copy envelope data into temporary buffer
7927 xx_envelope = level->envelope[envelope_nr];
7929 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7930 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7935 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7937 struct ElementInfo *ei = &element_info[element];
7941 chunk_size += putFile16BitBE(file, element);
7943 xx_ei = *ei; // copy element data into temporary buffer
7945 // set default description string for this specific element
7946 strcpy(xx_default_description, getDefaultElementDescription(ei));
7948 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7949 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7951 for (i = 0; i < ei->num_change_pages; i++)
7953 struct ElementChangeInfo *change = &ei->change_page[i];
7955 xx_current_change_page = i;
7957 xx_change = *change; // copy change data into temporary buffer
7960 setEventBitsFromEventFlags(change);
7962 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7963 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7970 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7972 struct ElementInfo *ei = &element_info[element];
7973 struct ElementGroupInfo *group = ei->group;
7977 chunk_size += putFile16BitBE(file, element);
7979 xx_ei = *ei; // copy element data into temporary buffer
7980 xx_group = *group; // copy group data into temporary buffer
7982 // set default description string for this specific element
7983 strcpy(xx_default_description, getDefaultElementDescription(ei));
7985 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7986 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7991 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7993 struct ElementInfo *ei = &element_info[element];
7997 chunk_size += putFile16BitBE(file, element);
7999 xx_ei = *ei; // copy element data into temporary buffer
8001 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8002 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8007 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8008 boolean save_as_template)
8014 if (!(file = fopen(filename, MODE_WRITE)))
8016 Warn("cannot save level file '%s'", filename);
8021 level->file_version = FILE_VERSION_ACTUAL;
8022 level->game_version = GAME_VERSION_ACTUAL;
8024 level->creation_date = getCurrentDate();
8026 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8027 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8029 chunk_size = SaveLevel_VERS(NULL, level);
8030 putFileChunkBE(file, "VERS", chunk_size);
8031 SaveLevel_VERS(file, level);
8033 chunk_size = SaveLevel_DATE(NULL, level);
8034 putFileChunkBE(file, "DATE", chunk_size);
8035 SaveLevel_DATE(file, level);
8037 chunk_size = SaveLevel_NAME(NULL, level);
8038 putFileChunkBE(file, "NAME", chunk_size);
8039 SaveLevel_NAME(file, level);
8041 chunk_size = SaveLevel_AUTH(NULL, level);
8042 putFileChunkBE(file, "AUTH", chunk_size);
8043 SaveLevel_AUTH(file, level);
8045 chunk_size = SaveLevel_INFO(NULL, level);
8046 putFileChunkBE(file, "INFO", chunk_size);
8047 SaveLevel_INFO(file, level);
8049 chunk_size = SaveLevel_BODY(NULL, level);
8050 putFileChunkBE(file, "BODY", chunk_size);
8051 SaveLevel_BODY(file, level);
8053 chunk_size = SaveLevel_ELEM(NULL, level);
8054 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8056 putFileChunkBE(file, "ELEM", chunk_size);
8057 SaveLevel_ELEM(file, level);
8060 for (i = 0; i < NUM_ENVELOPES; i++)
8062 int element = EL_ENVELOPE_1 + i;
8064 chunk_size = SaveLevel_NOTE(NULL, level, element);
8065 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8067 putFileChunkBE(file, "NOTE", chunk_size);
8068 SaveLevel_NOTE(file, level, element);
8072 // if not using template level, check for non-default custom/group elements
8073 if (!level->use_custom_template || save_as_template)
8075 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8077 int element = EL_CUSTOM_START + i;
8079 chunk_size = SaveLevel_CUSX(NULL, level, element);
8080 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8082 putFileChunkBE(file, "CUSX", chunk_size);
8083 SaveLevel_CUSX(file, level, element);
8087 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8089 int element = EL_GROUP_START + i;
8091 chunk_size = SaveLevel_GRPX(NULL, level, element);
8092 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8094 putFileChunkBE(file, "GRPX", chunk_size);
8095 SaveLevel_GRPX(file, level, element);
8099 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8101 int element = GET_EMPTY_ELEMENT(i);
8103 chunk_size = SaveLevel_EMPX(NULL, level, element);
8104 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8106 putFileChunkBE(file, "EMPX", chunk_size);
8107 SaveLevel_EMPX(file, level, element);
8114 SetFilePermissions(filename, PERMS_PRIVATE);
8117 void SaveLevel(int nr)
8119 char *filename = getDefaultLevelFilename(nr);
8121 SaveLevelFromFilename(&level, filename, FALSE);
8124 void SaveLevelTemplate(void)
8126 char *filename = getLocalLevelTemplateFilename();
8128 SaveLevelFromFilename(&level, filename, TRUE);
8131 boolean SaveLevelChecked(int nr)
8133 char *filename = getDefaultLevelFilename(nr);
8134 boolean new_level = !fileExists(filename);
8135 boolean level_saved = FALSE;
8137 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8142 Request("Level saved!", REQ_CONFIRM);
8150 void DumpLevel(struct LevelInfo *level)
8152 if (level->no_level_file || level->no_valid_file)
8154 Warn("cannot dump -- no valid level file found");
8160 Print("Level xxx (file version %08d, game version %08d)\n",
8161 level->file_version, level->game_version);
8164 Print("Level author: '%s'\n", level->author);
8165 Print("Level title: '%s'\n", level->name);
8167 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8169 Print("Level time: %d seconds\n", level->time);
8170 Print("Gems needed: %d\n", level->gems_needed);
8172 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8173 Print("Time for wheel: %d seconds\n", level->time_wheel);
8174 Print("Time for light: %d seconds\n", level->time_light);
8175 Print("Time for timegate: %d seconds\n", level->time_timegate);
8177 Print("Amoeba speed: %d\n", level->amoeba_speed);
8180 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8181 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8182 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8183 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8184 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8185 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8191 for (i = 0; i < NUM_ENVELOPES; i++)
8193 char *text = level->envelope[i].text;
8194 int text_len = strlen(text);
8195 boolean has_text = FALSE;
8197 for (j = 0; j < text_len; j++)
8198 if (text[j] != ' ' && text[j] != '\n')
8204 Print("Envelope %d:\n'%s'\n", i + 1, text);
8212 void DumpLevels(void)
8214 static LevelDirTree *dumplevel_leveldir = NULL;
8216 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8217 global.dumplevel_leveldir);
8219 if (dumplevel_leveldir == NULL)
8220 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8222 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8223 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8224 Fail("no such level number: %d", global.dumplevel_level_nr);
8226 leveldir_current = dumplevel_leveldir;
8228 LoadLevel(global.dumplevel_level_nr);
8235 // ============================================================================
8236 // tape file functions
8237 // ============================================================================
8239 static void setTapeInfoToDefaults(void)
8243 // always start with reliable default values (empty tape)
8246 // default values (also for pre-1.2 tapes) with only the first player
8247 tape.player_participates[0] = TRUE;
8248 for (i = 1; i < MAX_PLAYERS; i++)
8249 tape.player_participates[i] = FALSE;
8251 // at least one (default: the first) player participates in every tape
8252 tape.num_participating_players = 1;
8254 tape.property_bits = TAPE_PROPERTY_NONE;
8256 tape.level_nr = level_nr;
8258 tape.changed = FALSE;
8259 tape.solved = FALSE;
8261 tape.recording = FALSE;
8262 tape.playing = FALSE;
8263 tape.pausing = FALSE;
8265 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8266 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8268 tape.no_info_chunk = TRUE;
8269 tape.no_valid_file = FALSE;
8272 static int getTapePosSize(struct TapeInfo *tape)
8274 int tape_pos_size = 0;
8276 if (tape->use_key_actions)
8277 tape_pos_size += tape->num_participating_players;
8279 if (tape->use_mouse_actions)
8280 tape_pos_size += 3; // x and y position and mouse button mask
8282 tape_pos_size += 1; // tape action delay value
8284 return tape_pos_size;
8287 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8289 tape->use_key_actions = FALSE;
8290 tape->use_mouse_actions = FALSE;
8292 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8293 tape->use_key_actions = TRUE;
8295 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8296 tape->use_mouse_actions = TRUE;
8299 static int getTapeActionValue(struct TapeInfo *tape)
8301 return (tape->use_key_actions &&
8302 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8303 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8304 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8305 TAPE_ACTIONS_DEFAULT);
8308 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8310 tape->file_version = getFileVersion(file);
8311 tape->game_version = getFileVersion(file);
8316 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8320 tape->random_seed = getFile32BitBE(file);
8321 tape->date = getFile32BitBE(file);
8322 tape->length = getFile32BitBE(file);
8324 // read header fields that are new since version 1.2
8325 if (tape->file_version >= FILE_VERSION_1_2)
8327 byte store_participating_players = getFile8Bit(file);
8330 // since version 1.2, tapes store which players participate in the tape
8331 tape->num_participating_players = 0;
8332 for (i = 0; i < MAX_PLAYERS; i++)
8334 tape->player_participates[i] = FALSE;
8336 if (store_participating_players & (1 << i))
8338 tape->player_participates[i] = TRUE;
8339 tape->num_participating_players++;
8343 setTapeActionFlags(tape, getFile8Bit(file));
8345 tape->property_bits = getFile8Bit(file);
8346 tape->solved = getFile8Bit(file);
8348 engine_version = getFileVersion(file);
8349 if (engine_version > 0)
8350 tape->engine_version = engine_version;
8352 tape->engine_version = tape->game_version;
8358 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8360 tape->scr_fieldx = getFile8Bit(file);
8361 tape->scr_fieldy = getFile8Bit(file);
8366 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8368 char *level_identifier = NULL;
8369 int level_identifier_size;
8372 tape->no_info_chunk = FALSE;
8374 level_identifier_size = getFile16BitBE(file);
8376 level_identifier = checked_malloc(level_identifier_size);
8378 for (i = 0; i < level_identifier_size; i++)
8379 level_identifier[i] = getFile8Bit(file);
8381 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8382 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8384 checked_free(level_identifier);
8386 tape->level_nr = getFile16BitBE(file);
8388 chunk_size = 2 + level_identifier_size + 2;
8393 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8396 int tape_pos_size = getTapePosSize(tape);
8397 int chunk_size_expected = tape_pos_size * tape->length;
8399 if (chunk_size_expected != chunk_size)
8401 ReadUnusedBytesFromFile(file, chunk_size);
8402 return chunk_size_expected;
8405 for (i = 0; i < tape->length; i++)
8407 if (i >= MAX_TAPE_LEN)
8409 Warn("tape truncated -- size exceeds maximum tape size %d",
8412 // tape too large; read and ignore remaining tape data from this chunk
8413 for (;i < tape->length; i++)
8414 ReadUnusedBytesFromFile(file, tape_pos_size);
8419 if (tape->use_key_actions)
8421 for (j = 0; j < MAX_PLAYERS; j++)
8423 tape->pos[i].action[j] = MV_NONE;
8425 if (tape->player_participates[j])
8426 tape->pos[i].action[j] = getFile8Bit(file);
8430 if (tape->use_mouse_actions)
8432 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8433 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8434 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8437 tape->pos[i].delay = getFile8Bit(file);
8439 if (tape->file_version == FILE_VERSION_1_0)
8441 // eliminate possible diagonal moves in old tapes
8442 // this is only for backward compatibility
8444 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8445 byte action = tape->pos[i].action[0];
8446 int k, num_moves = 0;
8448 for (k = 0; k < 4; k++)
8450 if (action & joy_dir[k])
8452 tape->pos[i + num_moves].action[0] = joy_dir[k];
8454 tape->pos[i + num_moves].delay = 0;
8463 tape->length += num_moves;
8466 else if (tape->file_version < FILE_VERSION_2_0)
8468 // convert pre-2.0 tapes to new tape format
8470 if (tape->pos[i].delay > 1)
8473 tape->pos[i + 1] = tape->pos[i];
8474 tape->pos[i + 1].delay = 1;
8477 for (j = 0; j < MAX_PLAYERS; j++)
8478 tape->pos[i].action[j] = MV_NONE;
8479 tape->pos[i].delay--;
8486 if (checkEndOfFile(file))
8490 if (i != tape->length)
8491 chunk_size = tape_pos_size * i;
8496 static void LoadTape_SokobanSolution(char *filename)
8499 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8501 if (!(file = openFile(filename, MODE_READ)))
8503 tape.no_valid_file = TRUE;
8508 while (!checkEndOfFile(file))
8510 unsigned char c = getByteFromFile(file);
8512 if (checkEndOfFile(file))
8519 tape.pos[tape.length].action[0] = MV_UP;
8520 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8526 tape.pos[tape.length].action[0] = MV_DOWN;
8527 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8533 tape.pos[tape.length].action[0] = MV_LEFT;
8534 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8540 tape.pos[tape.length].action[0] = MV_RIGHT;
8541 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8549 // ignore white-space characters
8553 tape.no_valid_file = TRUE;
8555 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8563 if (tape.no_valid_file)
8566 tape.length_frames = GetTapeLengthFrames();
8567 tape.length_seconds = GetTapeLengthSeconds();
8570 void LoadTapeFromFilename(char *filename)
8572 char cookie[MAX_LINE_LEN];
8573 char chunk_name[CHUNK_ID_LEN + 1];
8577 // always start with reliable default values
8578 setTapeInfoToDefaults();
8580 if (strSuffix(filename, ".sln"))
8582 LoadTape_SokobanSolution(filename);
8587 if (!(file = openFile(filename, MODE_READ)))
8589 tape.no_valid_file = TRUE;
8594 getFileChunkBE(file, chunk_name, NULL);
8595 if (strEqual(chunk_name, "RND1"))
8597 getFile32BitBE(file); // not used
8599 getFileChunkBE(file, chunk_name, NULL);
8600 if (!strEqual(chunk_name, "TAPE"))
8602 tape.no_valid_file = TRUE;
8604 Warn("unknown format of tape file '%s'", filename);
8611 else // check for pre-2.0 file format with cookie string
8613 strcpy(cookie, chunk_name);
8614 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8616 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8617 cookie[strlen(cookie) - 1] = '\0';
8619 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8621 tape.no_valid_file = TRUE;
8623 Warn("unknown format of tape file '%s'", filename);
8630 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8632 tape.no_valid_file = TRUE;
8634 Warn("unsupported version of tape file '%s'", filename);
8641 // pre-2.0 tape files have no game version, so use file version here
8642 tape.game_version = tape.file_version;
8645 if (tape.file_version < FILE_VERSION_1_2)
8647 // tape files from versions before 1.2.0 without chunk structure
8648 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8649 LoadTape_BODY(file, 2 * tape.length, &tape);
8657 int (*loader)(File *, int, struct TapeInfo *);
8661 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8662 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8663 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8664 { "INFO", -1, LoadTape_INFO },
8665 { "BODY", -1, LoadTape_BODY },
8669 while (getFileChunkBE(file, chunk_name, &chunk_size))
8673 while (chunk_info[i].name != NULL &&
8674 !strEqual(chunk_name, chunk_info[i].name))
8677 if (chunk_info[i].name == NULL)
8679 Warn("unknown chunk '%s' in tape file '%s'",
8680 chunk_name, filename);
8682 ReadUnusedBytesFromFile(file, chunk_size);
8684 else if (chunk_info[i].size != -1 &&
8685 chunk_info[i].size != chunk_size)
8687 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8688 chunk_size, chunk_name, filename);
8690 ReadUnusedBytesFromFile(file, chunk_size);
8694 // call function to load this tape chunk
8695 int chunk_size_expected =
8696 (chunk_info[i].loader)(file, chunk_size, &tape);
8698 // the size of some chunks cannot be checked before reading other
8699 // chunks first (like "HEAD" and "BODY") that contain some header
8700 // information, so check them here
8701 if (chunk_size_expected != chunk_size)
8703 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8704 chunk_size, chunk_name, filename);
8712 tape.length_frames = GetTapeLengthFrames();
8713 tape.length_seconds = GetTapeLengthSeconds();
8716 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8718 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8720 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8721 tape.engine_version);
8725 void LoadTape(int nr)
8727 char *filename = getTapeFilename(nr);
8729 LoadTapeFromFilename(filename);
8732 void LoadSolutionTape(int nr)
8734 char *filename = getSolutionTapeFilename(nr);
8736 LoadTapeFromFilename(filename);
8738 if (TAPE_IS_EMPTY(tape))
8740 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8741 level.native_bd_level->replay != NULL)
8742 CopyNativeTape_BD_to_RND(&level);
8743 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8744 level.native_sp_level->demo.is_available)
8745 CopyNativeTape_SP_to_RND(&level);
8749 void LoadScoreTape(char *score_tape_basename, int nr)
8751 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8753 LoadTapeFromFilename(filename);
8756 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8758 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8760 LoadTapeFromFilename(filename);
8763 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8765 // chunk required for team mode tapes with non-default screen size
8766 return (tape->num_participating_players > 1 &&
8767 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8768 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8771 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8773 putFileVersion(file, tape->file_version);
8774 putFileVersion(file, tape->game_version);
8777 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8780 byte store_participating_players = 0;
8782 // set bits for participating players for compact storage
8783 for (i = 0; i < MAX_PLAYERS; i++)
8784 if (tape->player_participates[i])
8785 store_participating_players |= (1 << i);
8787 putFile32BitBE(file, tape->random_seed);
8788 putFile32BitBE(file, tape->date);
8789 putFile32BitBE(file, tape->length);
8791 putFile8Bit(file, store_participating_players);
8793 putFile8Bit(file, getTapeActionValue(tape));
8795 putFile8Bit(file, tape->property_bits);
8796 putFile8Bit(file, tape->solved);
8798 putFileVersion(file, tape->engine_version);
8801 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8803 putFile8Bit(file, tape->scr_fieldx);
8804 putFile8Bit(file, tape->scr_fieldy);
8807 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8809 int level_identifier_size = strlen(tape->level_identifier) + 1;
8812 putFile16BitBE(file, level_identifier_size);
8814 for (i = 0; i < level_identifier_size; i++)
8815 putFile8Bit(file, tape->level_identifier[i]);
8817 putFile16BitBE(file, tape->level_nr);
8820 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8824 for (i = 0; i < tape->length; i++)
8826 if (tape->use_key_actions)
8828 for (j = 0; j < MAX_PLAYERS; j++)
8829 if (tape->player_participates[j])
8830 putFile8Bit(file, tape->pos[i].action[j]);
8833 if (tape->use_mouse_actions)
8835 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8836 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8837 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8840 putFile8Bit(file, tape->pos[i].delay);
8844 void SaveTapeToFilename(char *filename)
8848 int info_chunk_size;
8849 int body_chunk_size;
8851 if (!(file = fopen(filename, MODE_WRITE)))
8853 Warn("cannot save level recording file '%s'", filename);
8858 tape_pos_size = getTapePosSize(&tape);
8860 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8861 body_chunk_size = tape_pos_size * tape.length;
8863 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8864 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8866 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8867 SaveTape_VERS(file, &tape);
8869 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8870 SaveTape_HEAD(file, &tape);
8872 if (checkSaveTape_SCRN(&tape))
8874 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8875 SaveTape_SCRN(file, &tape);
8878 putFileChunkBE(file, "INFO", info_chunk_size);
8879 SaveTape_INFO(file, &tape);
8881 putFileChunkBE(file, "BODY", body_chunk_size);
8882 SaveTape_BODY(file, &tape);
8886 SetFilePermissions(filename, PERMS_PRIVATE);
8889 static void SaveTapeExt(char *filename)
8893 tape.file_version = FILE_VERSION_ACTUAL;
8894 tape.game_version = GAME_VERSION_ACTUAL;
8896 tape.num_participating_players = 0;
8898 // count number of participating players
8899 for (i = 0; i < MAX_PLAYERS; i++)
8900 if (tape.player_participates[i])
8901 tape.num_participating_players++;
8903 SaveTapeToFilename(filename);
8905 tape.changed = FALSE;
8908 void SaveTape(int nr)
8910 char *filename = getTapeFilename(nr);
8912 InitTapeDirectory(leveldir_current->subdir);
8914 SaveTapeExt(filename);
8917 void SaveScoreTape(int nr)
8919 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8921 // used instead of "leveldir_current->subdir" (for network games)
8922 InitScoreTapeDirectory(levelset.identifier, nr);
8924 SaveTapeExt(filename);
8927 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8928 unsigned int req_state_added)
8930 char *filename = getTapeFilename(nr);
8931 boolean new_tape = !fileExists(filename);
8932 boolean tape_saved = FALSE;
8934 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8939 Request(msg_saved, REQ_CONFIRM | req_state_added);
8947 boolean SaveTapeChecked(int nr)
8949 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8952 boolean SaveTapeChecked_LevelSolved(int nr)
8954 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8955 "Level solved! Tape saved!", REQ_STAY_OPEN);
8958 void DumpTape(struct TapeInfo *tape)
8960 int tape_frame_counter;
8963 if (tape->no_valid_file)
8965 Warn("cannot dump -- no valid tape file found");
8972 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8973 tape->level_nr, tape->file_version, tape->game_version);
8974 Print(" (effective engine version %08d)\n",
8975 tape->engine_version);
8976 Print("Level series identifier: '%s'\n", tape->level_identifier);
8978 Print("Solution tape: %s\n",
8979 tape->solved ? "yes" :
8980 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8982 Print("Special tape properties: ");
8983 if (tape->property_bits == TAPE_PROPERTY_NONE)
8985 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8986 Print("[em_random_bug]");
8987 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8988 Print("[game_speed]");
8989 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8991 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8992 Print("[single_step]");
8993 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8994 Print("[snapshot]");
8995 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8996 Print("[replayed]");
8997 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8998 Print("[tas_keys]");
8999 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9000 Print("[small_graphics]");
9003 int year2 = tape->date / 10000;
9004 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9005 int month_index_raw = (tape->date / 100) % 100;
9006 int month_index = month_index_raw % 12; // prevent invalid index
9007 int month = month_index + 1;
9008 int day = tape->date % 100;
9010 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9014 tape_frame_counter = 0;
9016 for (i = 0; i < tape->length; i++)
9018 if (i >= MAX_TAPE_LEN)
9023 for (j = 0; j < MAX_PLAYERS; j++)
9025 if (tape->player_participates[j])
9027 int action = tape->pos[i].action[j];
9029 Print("%d:%02x ", j, action);
9030 Print("[%c%c%c%c|%c%c] - ",
9031 (action & JOY_LEFT ? '<' : ' '),
9032 (action & JOY_RIGHT ? '>' : ' '),
9033 (action & JOY_UP ? '^' : ' '),
9034 (action & JOY_DOWN ? 'v' : ' '),
9035 (action & JOY_BUTTON_1 ? '1' : ' '),
9036 (action & JOY_BUTTON_2 ? '2' : ' '));
9040 Print("(%03d) ", tape->pos[i].delay);
9041 Print("[%05d]\n", tape_frame_counter);
9043 tape_frame_counter += tape->pos[i].delay;
9049 void DumpTapes(void)
9051 static LevelDirTree *dumptape_leveldir = NULL;
9053 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9054 global.dumptape_leveldir);
9056 if (dumptape_leveldir == NULL)
9057 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9059 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9060 global.dumptape_level_nr > dumptape_leveldir->last_level)
9061 Fail("no such level number: %d", global.dumptape_level_nr);
9063 leveldir_current = dumptape_leveldir;
9065 if (options.mytapes)
9066 LoadTape(global.dumptape_level_nr);
9068 LoadSolutionTape(global.dumptape_level_nr);
9076 // ============================================================================
9077 // score file functions
9078 // ============================================================================
9080 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9084 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9086 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9087 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9088 scores->entry[i].score = 0;
9089 scores->entry[i].time = 0;
9091 scores->entry[i].id = -1;
9092 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9093 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9094 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9095 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9096 strcpy(scores->entry[i].country_code, "??");
9099 scores->num_entries = 0;
9100 scores->last_added = -1;
9101 scores->last_added_local = -1;
9103 scores->updated = FALSE;
9104 scores->uploaded = FALSE;
9105 scores->tape_downloaded = FALSE;
9106 scores->force_last_added = FALSE;
9108 // The following values are intentionally not reset here:
9112 // - continue_playing
9113 // - continue_on_return
9116 static void setScoreInfoToDefaults(void)
9118 setScoreInfoToDefaultsExt(&scores);
9121 static void setServerScoreInfoToDefaults(void)
9123 setScoreInfoToDefaultsExt(&server_scores);
9126 static void LoadScore_OLD(int nr)
9129 char *filename = getScoreFilename(nr);
9130 char cookie[MAX_LINE_LEN];
9131 char line[MAX_LINE_LEN];
9135 if (!(file = fopen(filename, MODE_READ)))
9138 // check file identifier
9139 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9141 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9142 cookie[strlen(cookie) - 1] = '\0';
9144 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9146 Warn("unknown format of score file '%s'", filename);
9153 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9155 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9156 Warn("fscanf() failed; %s", strerror(errno));
9158 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9161 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9162 line[strlen(line) - 1] = '\0';
9164 for (line_ptr = line; *line_ptr; line_ptr++)
9166 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9168 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9169 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9178 static void ConvertScore_OLD(void)
9180 // only convert score to time for levels that rate playing time over score
9181 if (!level.rate_time_over_score)
9184 // convert old score to playing time for score-less levels (like Supaplex)
9185 int time_final_max = 999;
9188 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9190 int score = scores.entry[i].score;
9192 if (score > 0 && score < time_final_max)
9193 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9197 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9199 scores->file_version = getFileVersion(file);
9200 scores->game_version = getFileVersion(file);
9205 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9207 char *level_identifier = NULL;
9208 int level_identifier_size;
9211 level_identifier_size = getFile16BitBE(file);
9213 level_identifier = checked_malloc(level_identifier_size);
9215 for (i = 0; i < level_identifier_size; i++)
9216 level_identifier[i] = getFile8Bit(file);
9218 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9219 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9221 checked_free(level_identifier);
9223 scores->level_nr = getFile16BitBE(file);
9224 scores->num_entries = getFile16BitBE(file);
9226 chunk_size = 2 + level_identifier_size + 2 + 2;
9231 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9235 for (i = 0; i < scores->num_entries; i++)
9237 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9238 scores->entry[i].name[j] = getFile8Bit(file);
9240 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9243 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9248 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9252 for (i = 0; i < scores->num_entries; i++)
9253 scores->entry[i].score = getFile16BitBE(file);
9255 chunk_size = scores->num_entries * 2;
9260 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9264 for (i = 0; i < scores->num_entries; i++)
9265 scores->entry[i].score = getFile32BitBE(file);
9267 chunk_size = scores->num_entries * 4;
9272 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9276 for (i = 0; i < scores->num_entries; i++)
9277 scores->entry[i].time = getFile32BitBE(file);
9279 chunk_size = scores->num_entries * 4;
9284 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9288 for (i = 0; i < scores->num_entries; i++)
9290 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9291 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9293 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9296 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9301 void LoadScore(int nr)
9303 char *filename = getScoreFilename(nr);
9304 char cookie[MAX_LINE_LEN];
9305 char chunk_name[CHUNK_ID_LEN + 1];
9307 boolean old_score_file_format = FALSE;
9310 // always start with reliable default values
9311 setScoreInfoToDefaults();
9313 if (!(file = openFile(filename, MODE_READ)))
9316 getFileChunkBE(file, chunk_name, NULL);
9317 if (strEqual(chunk_name, "RND1"))
9319 getFile32BitBE(file); // not used
9321 getFileChunkBE(file, chunk_name, NULL);
9322 if (!strEqual(chunk_name, "SCOR"))
9324 Warn("unknown format of score file '%s'", filename);
9331 else // check for old file format with cookie string
9333 strcpy(cookie, chunk_name);
9334 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9336 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9337 cookie[strlen(cookie) - 1] = '\0';
9339 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9341 Warn("unknown format of score file '%s'", filename);
9348 old_score_file_format = TRUE;
9351 if (old_score_file_format)
9353 // score files from versions before 4.2.4.0 without chunk structure
9356 // convert score to time, if possible (mainly for Supaplex levels)
9365 int (*loader)(File *, int, struct ScoreInfo *);
9369 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9370 { "INFO", -1, LoadScore_INFO },
9371 { "NAME", -1, LoadScore_NAME },
9372 { "SCOR", -1, LoadScore_SCOR },
9373 { "SC4R", -1, LoadScore_SC4R },
9374 { "TIME", -1, LoadScore_TIME },
9375 { "TAPE", -1, LoadScore_TAPE },
9380 while (getFileChunkBE(file, chunk_name, &chunk_size))
9384 while (chunk_info[i].name != NULL &&
9385 !strEqual(chunk_name, chunk_info[i].name))
9388 if (chunk_info[i].name == NULL)
9390 Warn("unknown chunk '%s' in score file '%s'",
9391 chunk_name, filename);
9393 ReadUnusedBytesFromFile(file, chunk_size);
9395 else if (chunk_info[i].size != -1 &&
9396 chunk_info[i].size != chunk_size)
9398 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9399 chunk_size, chunk_name, filename);
9401 ReadUnusedBytesFromFile(file, chunk_size);
9405 // call function to load this score chunk
9406 int chunk_size_expected =
9407 (chunk_info[i].loader)(file, chunk_size, &scores);
9409 // the size of some chunks cannot be checked before reading other
9410 // chunks first (like "HEAD" and "BODY") that contain some header
9411 // information, so check them here
9412 if (chunk_size_expected != chunk_size)
9414 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9415 chunk_size, chunk_name, filename);
9424 #if ENABLE_HISTORIC_CHUNKS
9425 void SaveScore_OLD(int nr)
9428 char *filename = getScoreFilename(nr);
9431 // used instead of "leveldir_current->subdir" (for network games)
9432 InitScoreDirectory(levelset.identifier);
9434 if (!(file = fopen(filename, MODE_WRITE)))
9436 Warn("cannot save score for level %d", nr);
9441 fprintf(file, "%s\n\n", SCORE_COOKIE);
9443 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9444 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9448 SetFilePermissions(filename, PERMS_PRIVATE);
9452 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9454 putFileVersion(file, scores->file_version);
9455 putFileVersion(file, scores->game_version);
9458 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9460 int level_identifier_size = strlen(scores->level_identifier) + 1;
9463 putFile16BitBE(file, level_identifier_size);
9465 for (i = 0; i < level_identifier_size; i++)
9466 putFile8Bit(file, scores->level_identifier[i]);
9468 putFile16BitBE(file, scores->level_nr);
9469 putFile16BitBE(file, scores->num_entries);
9472 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9476 for (i = 0; i < scores->num_entries; i++)
9478 int name_size = strlen(scores->entry[i].name);
9480 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9481 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9485 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9489 for (i = 0; i < scores->num_entries; i++)
9490 putFile16BitBE(file, scores->entry[i].score);
9493 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9497 for (i = 0; i < scores->num_entries; i++)
9498 putFile32BitBE(file, scores->entry[i].score);
9501 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9505 for (i = 0; i < scores->num_entries; i++)
9506 putFile32BitBE(file, scores->entry[i].time);
9509 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9513 for (i = 0; i < scores->num_entries; i++)
9515 int size = strlen(scores->entry[i].tape_basename);
9517 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9518 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9522 static void SaveScoreToFilename(char *filename)
9525 int info_chunk_size;
9526 int name_chunk_size;
9527 int scor_chunk_size;
9528 int sc4r_chunk_size;
9529 int time_chunk_size;
9530 int tape_chunk_size;
9531 boolean has_large_score_values;
9534 if (!(file = fopen(filename, MODE_WRITE)))
9536 Warn("cannot save score file '%s'", filename);
9541 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9542 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9543 scor_chunk_size = scores.num_entries * 2;
9544 sc4r_chunk_size = scores.num_entries * 4;
9545 time_chunk_size = scores.num_entries * 4;
9546 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9548 has_large_score_values = FALSE;
9549 for (i = 0; i < scores.num_entries; i++)
9550 if (scores.entry[i].score > 0xffff)
9551 has_large_score_values = TRUE;
9553 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9554 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9556 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9557 SaveScore_VERS(file, &scores);
9559 putFileChunkBE(file, "INFO", info_chunk_size);
9560 SaveScore_INFO(file, &scores);
9562 putFileChunkBE(file, "NAME", name_chunk_size);
9563 SaveScore_NAME(file, &scores);
9565 if (has_large_score_values)
9567 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9568 SaveScore_SC4R(file, &scores);
9572 putFileChunkBE(file, "SCOR", scor_chunk_size);
9573 SaveScore_SCOR(file, &scores);
9576 putFileChunkBE(file, "TIME", time_chunk_size);
9577 SaveScore_TIME(file, &scores);
9579 putFileChunkBE(file, "TAPE", tape_chunk_size);
9580 SaveScore_TAPE(file, &scores);
9584 SetFilePermissions(filename, PERMS_PRIVATE);
9587 void SaveScore(int nr)
9589 char *filename = getScoreFilename(nr);
9592 // used instead of "leveldir_current->subdir" (for network games)
9593 InitScoreDirectory(levelset.identifier);
9595 scores.file_version = FILE_VERSION_ACTUAL;
9596 scores.game_version = GAME_VERSION_ACTUAL;
9598 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9599 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9600 scores.level_nr = level_nr;
9602 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9603 if (scores.entry[i].score == 0 &&
9604 scores.entry[i].time == 0 &&
9605 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9608 scores.num_entries = i;
9610 if (scores.num_entries == 0)
9613 SaveScoreToFilename(filename);
9616 static void LoadServerScoreFromCache(int nr)
9618 struct ScoreEntry score_entry;
9627 { &score_entry.score, FALSE, 0 },
9628 { &score_entry.time, FALSE, 0 },
9629 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9630 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9631 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9632 { &score_entry.id, FALSE, 0 },
9633 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9634 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9635 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9636 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9640 char *filename = getScoreCacheFilename(nr);
9641 SetupFileHash *score_hash = loadSetupFileHash(filename);
9644 server_scores.num_entries = 0;
9646 if (score_hash == NULL)
9649 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9651 score_entry = server_scores.entry[i];
9653 for (j = 0; score_mapping[j].value != NULL; j++)
9657 sprintf(token, "%02d.%d", i, j);
9659 char *value = getHashEntry(score_hash, token);
9664 if (score_mapping[j].is_string)
9666 char *score_value = (char *)score_mapping[j].value;
9667 int value_size = score_mapping[j].string_size;
9669 strncpy(score_value, value, value_size);
9670 score_value[value_size] = '\0';
9674 int *score_value = (int *)score_mapping[j].value;
9676 *score_value = atoi(value);
9679 server_scores.num_entries = i + 1;
9682 server_scores.entry[i] = score_entry;
9685 freeSetupFileHash(score_hash);
9688 void LoadServerScore(int nr, boolean download_score)
9690 if (!setup.use_api_server)
9693 // always start with reliable default values
9694 setServerScoreInfoToDefaults();
9696 // 1st step: load server scores from cache file (which may not exist)
9697 // (this should prevent reading it while the thread is writing to it)
9698 LoadServerScoreFromCache(nr);
9700 if (download_score && runtime.use_api_server)
9702 // 2nd step: download server scores from score server to cache file
9703 // (as thread, as it might time out if the server is not reachable)
9704 ApiGetScoreAsThread(nr);
9708 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9710 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9712 // if score tape not uploaded, ask for uploading missing tapes later
9713 if (!setup.has_remaining_tapes)
9714 setup.ask_for_remaining_tapes = TRUE;
9716 setup.provide_uploading_tapes = TRUE;
9717 setup.has_remaining_tapes = TRUE;
9719 SaveSetup_ServerSetup();
9722 void SaveServerScore(int nr, boolean tape_saved)
9724 if (!runtime.use_api_server)
9726 PrepareScoreTapesForUpload(leveldir_current->subdir);
9731 ApiAddScoreAsThread(nr, tape_saved, NULL);
9734 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9735 char *score_tape_filename)
9737 if (!runtime.use_api_server)
9740 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9743 void LoadLocalAndServerScore(int nr, boolean download_score)
9745 int last_added_local = scores.last_added_local;
9746 boolean force_last_added = scores.force_last_added;
9748 // needed if only showing server scores
9749 setScoreInfoToDefaults();
9751 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9754 // restore last added local score entry (before merging server scores)
9755 scores.last_added = scores.last_added_local = last_added_local;
9757 if (setup.use_api_server &&
9758 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9760 // load server scores from cache file and trigger update from server
9761 LoadServerScore(nr, download_score);
9763 // merge local scores with scores from server
9767 if (force_last_added)
9768 scores.force_last_added = force_last_added;
9772 // ============================================================================
9773 // setup file functions
9774 // ============================================================================
9776 #define TOKEN_STR_PLAYER_PREFIX "player_"
9779 static struct TokenInfo global_setup_tokens[] =
9783 &setup.player_name, "player_name"
9787 &setup.multiple_users, "multiple_users"
9791 &setup.sound, "sound"
9795 &setup.sound_loops, "repeating_sound_loops"
9799 &setup.sound_music, "background_music"
9803 &setup.sound_simple, "simple_sound_effects"
9807 &setup.toons, "toons"
9811 &setup.global_animations, "global_animations"
9815 &setup.scroll_delay, "scroll_delay"
9819 &setup.forced_scroll_delay, "forced_scroll_delay"
9823 &setup.scroll_delay_value, "scroll_delay_value"
9827 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9831 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9835 &setup.fade_screens, "fade_screens"
9839 &setup.autorecord, "automatic_tape_recording"
9843 &setup.autorecord_after_replay, "autorecord_after_replay"
9847 &setup.auto_pause_on_start, "auto_pause_on_start"
9851 &setup.show_titlescreen, "show_titlescreen"
9855 &setup.quick_doors, "quick_doors"
9859 &setup.team_mode, "team_mode"
9863 &setup.handicap, "handicap"
9867 &setup.skip_levels, "skip_levels"
9871 &setup.increment_levels, "increment_levels"
9875 &setup.auto_play_next_level, "auto_play_next_level"
9879 &setup.count_score_after_game, "count_score_after_game"
9883 &setup.show_scores_after_game, "show_scores_after_game"
9887 &setup.time_limit, "time_limit"
9891 &setup.fullscreen, "fullscreen"
9895 &setup.window_scaling_percent, "window_scaling_percent"
9899 &setup.window_scaling_quality, "window_scaling_quality"
9903 &setup.screen_rendering_mode, "screen_rendering_mode"
9907 &setup.vsync_mode, "vsync_mode"
9911 &setup.ask_on_escape, "ask_on_escape"
9915 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9919 &setup.ask_on_game_over, "ask_on_game_over"
9923 &setup.ask_on_quit_game, "ask_on_quit_game"
9927 &setup.ask_on_quit_program, "ask_on_quit_program"
9931 &setup.quick_switch, "quick_player_switch"
9935 &setup.input_on_focus, "input_on_focus"
9939 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9943 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9947 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9951 &setup.game_speed_extended, "game_speed_extended"
9955 &setup.game_frame_delay, "game_frame_delay"
9959 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9963 &setup.bd_skip_hatching, "bd_skip_hatching"
9967 &setup.bd_scroll_delay, "bd_scroll_delay"
9971 &setup.bd_smooth_movements, "bd_smooth_movements"
9975 &setup.sp_show_border_elements, "sp_show_border_elements"
9979 &setup.small_game_graphics, "small_game_graphics"
9983 &setup.show_load_save_buttons, "show_load_save_buttons"
9987 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9991 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9995 &setup.graphics_set, "graphics_set"
9999 &setup.sounds_set, "sounds_set"
10003 &setup.music_set, "music_set"
10007 &setup.override_level_graphics, "override_level_graphics"
10011 &setup.override_level_sounds, "override_level_sounds"
10015 &setup.override_level_music, "override_level_music"
10019 &setup.volume_simple, "volume_simple"
10023 &setup.volume_loops, "volume_loops"
10027 &setup.volume_music, "volume_music"
10031 &setup.network_mode, "network_mode"
10035 &setup.network_player_nr, "network_player"
10039 &setup.network_server_hostname, "network_server_hostname"
10043 &setup.touch.control_type, "touch.control_type"
10047 &setup.touch.move_distance, "touch.move_distance"
10051 &setup.touch.drop_distance, "touch.drop_distance"
10055 &setup.touch.transparency, "touch.transparency"
10059 &setup.touch.draw_outlined, "touch.draw_outlined"
10063 &setup.touch.draw_pressed, "touch.draw_pressed"
10067 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10071 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10075 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10079 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10083 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10087 static struct TokenInfo auto_setup_tokens[] =
10091 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10095 static struct TokenInfo server_setup_tokens[] =
10099 &setup.player_uuid, "player_uuid"
10103 &setup.player_version, "player_version"
10107 &setup.use_api_server, TEST_PREFIX "use_api_server"
10111 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10115 &setup.api_server_password, TEST_PREFIX "api_server_password"
10119 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10123 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10127 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10131 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10135 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10139 static struct TokenInfo editor_setup_tokens[] =
10143 &setup.editor.el_classic, "editor.el_classic"
10147 &setup.editor.el_custom, "editor.el_custom"
10151 &setup.editor.el_user_defined, "editor.el_user_defined"
10155 &setup.editor.el_dynamic, "editor.el_dynamic"
10159 &setup.editor.el_headlines, "editor.el_headlines"
10163 &setup.editor.show_element_token, "editor.show_element_token"
10167 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10171 static struct TokenInfo editor_cascade_setup_tokens[] =
10175 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10179 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10183 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10187 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10191 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10195 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10199 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10203 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10207 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10211 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10215 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10219 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10223 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10227 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10231 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10235 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10239 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10243 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10247 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10251 static struct TokenInfo shortcut_setup_tokens[] =
10255 &setup.shortcut.save_game, "shortcut.save_game"
10259 &setup.shortcut.load_game, "shortcut.load_game"
10263 &setup.shortcut.restart_game, "shortcut.restart_game"
10267 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10271 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10275 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10279 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10283 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10287 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10291 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10295 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10299 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10303 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10307 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10311 &setup.shortcut.tape_record, "shortcut.tape_record"
10315 &setup.shortcut.tape_play, "shortcut.tape_play"
10319 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10323 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10327 &setup.shortcut.sound_music, "shortcut.sound_music"
10331 &setup.shortcut.snap_left, "shortcut.snap_left"
10335 &setup.shortcut.snap_right, "shortcut.snap_right"
10339 &setup.shortcut.snap_up, "shortcut.snap_up"
10343 &setup.shortcut.snap_down, "shortcut.snap_down"
10347 static struct SetupInputInfo setup_input;
10348 static struct TokenInfo player_setup_tokens[] =
10352 &setup_input.use_joystick, ".use_joystick"
10356 &setup_input.joy.device_name, ".joy.device_name"
10360 &setup_input.joy.xleft, ".joy.xleft"
10364 &setup_input.joy.xmiddle, ".joy.xmiddle"
10368 &setup_input.joy.xright, ".joy.xright"
10372 &setup_input.joy.yupper, ".joy.yupper"
10376 &setup_input.joy.ymiddle, ".joy.ymiddle"
10380 &setup_input.joy.ylower, ".joy.ylower"
10384 &setup_input.joy.snap, ".joy.snap_field"
10388 &setup_input.joy.drop, ".joy.place_bomb"
10392 &setup_input.key.left, ".key.move_left"
10396 &setup_input.key.right, ".key.move_right"
10400 &setup_input.key.up, ".key.move_up"
10404 &setup_input.key.down, ".key.move_down"
10408 &setup_input.key.snap, ".key.snap_field"
10412 &setup_input.key.drop, ".key.place_bomb"
10416 static struct TokenInfo system_setup_tokens[] =
10420 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10424 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10428 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10432 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10436 static struct TokenInfo internal_setup_tokens[] =
10440 &setup.internal.program_title, "program_title"
10444 &setup.internal.program_version, "program_version"
10448 &setup.internal.program_author, "program_author"
10452 &setup.internal.program_email, "program_email"
10456 &setup.internal.program_website, "program_website"
10460 &setup.internal.program_copyright, "program_copyright"
10464 &setup.internal.program_company, "program_company"
10468 &setup.internal.program_icon_file, "program_icon_file"
10472 &setup.internal.default_graphics_set, "default_graphics_set"
10476 &setup.internal.default_sounds_set, "default_sounds_set"
10480 &setup.internal.default_music_set, "default_music_set"
10484 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10488 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10492 &setup.internal.fallback_music_file, "fallback_music_file"
10496 &setup.internal.default_level_series, "default_level_series"
10500 &setup.internal.default_window_width, "default_window_width"
10504 &setup.internal.default_window_height, "default_window_height"
10508 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10512 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10516 &setup.internal.create_user_levelset, "create_user_levelset"
10520 &setup.internal.info_screens_from_main, "info_screens_from_main"
10524 &setup.internal.menu_game, "menu_game"
10528 &setup.internal.menu_engines, "menu_engines"
10532 &setup.internal.menu_editor, "menu_editor"
10536 &setup.internal.menu_graphics, "menu_graphics"
10540 &setup.internal.menu_sound, "menu_sound"
10544 &setup.internal.menu_artwork, "menu_artwork"
10548 &setup.internal.menu_input, "menu_input"
10552 &setup.internal.menu_touch, "menu_touch"
10556 &setup.internal.menu_shortcuts, "menu_shortcuts"
10560 &setup.internal.menu_exit, "menu_exit"
10564 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10568 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10572 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10576 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10580 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10584 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10588 &setup.internal.info_title, "info_title"
10592 &setup.internal.info_elements, "info_elements"
10596 &setup.internal.info_music, "info_music"
10600 &setup.internal.info_credits, "info_credits"
10604 &setup.internal.info_program, "info_program"
10608 &setup.internal.info_version, "info_version"
10612 &setup.internal.info_levelset, "info_levelset"
10616 &setup.internal.info_exit, "info_exit"
10620 static struct TokenInfo debug_setup_tokens[] =
10624 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10628 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10632 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10636 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10640 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10644 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10648 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10652 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10656 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10660 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10664 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10668 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10672 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10676 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10680 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10684 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10688 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10692 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10696 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10700 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10704 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10707 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10711 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10715 &setup.debug.xsn_mode, "debug.xsn_mode"
10719 &setup.debug.xsn_percent, "debug.xsn_percent"
10723 static struct TokenInfo options_setup_tokens[] =
10727 &setup.options.verbose, "options.verbose"
10731 &setup.options.debug, "options.debug"
10735 &setup.options.debug_mode, "options.debug_mode"
10739 static void setSetupInfoToDefaults(struct SetupInfo *si)
10743 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10745 si->multiple_users = TRUE;
10748 si->sound_loops = TRUE;
10749 si->sound_music = TRUE;
10750 si->sound_simple = TRUE;
10752 si->global_animations = TRUE;
10753 si->scroll_delay = TRUE;
10754 si->forced_scroll_delay = FALSE;
10755 si->scroll_delay_value = STD_SCROLL_DELAY;
10756 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10757 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10758 si->fade_screens = TRUE;
10759 si->autorecord = TRUE;
10760 si->autorecord_after_replay = TRUE;
10761 si->auto_pause_on_start = FALSE;
10762 si->show_titlescreen = TRUE;
10763 si->quick_doors = FALSE;
10764 si->team_mode = FALSE;
10765 si->handicap = TRUE;
10766 si->skip_levels = TRUE;
10767 si->increment_levels = TRUE;
10768 si->auto_play_next_level = TRUE;
10769 si->count_score_after_game = TRUE;
10770 si->show_scores_after_game = TRUE;
10771 si->time_limit = TRUE;
10772 si->fullscreen = FALSE;
10773 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10774 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10775 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10776 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10777 si->ask_on_escape = TRUE;
10778 si->ask_on_escape_editor = TRUE;
10779 si->ask_on_game_over = TRUE;
10780 si->ask_on_quit_game = TRUE;
10781 si->ask_on_quit_program = TRUE;
10782 si->quick_switch = FALSE;
10783 si->input_on_focus = FALSE;
10784 si->prefer_aga_graphics = TRUE;
10785 si->prefer_lowpass_sounds = FALSE;
10786 si->prefer_extra_panel_items = TRUE;
10787 si->game_speed_extended = FALSE;
10788 si->game_frame_delay = GAME_FRAME_DELAY;
10789 si->bd_skip_uncovering = FALSE;
10790 si->bd_skip_hatching = FALSE;
10791 si->bd_scroll_delay = TRUE;
10792 si->bd_smooth_movements = AUTO;
10793 si->sp_show_border_elements = FALSE;
10794 si->small_game_graphics = FALSE;
10795 si->show_load_save_buttons = FALSE;
10796 si->show_undo_redo_buttons = FALSE;
10797 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10799 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10800 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10801 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10803 si->override_level_graphics = FALSE;
10804 si->override_level_sounds = FALSE;
10805 si->override_level_music = FALSE;
10807 si->volume_simple = 100; // percent
10808 si->volume_loops = 100; // percent
10809 si->volume_music = 100; // percent
10811 si->network_mode = FALSE;
10812 si->network_player_nr = 0; // first player
10813 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10815 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10816 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10817 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10818 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10819 si->touch.draw_outlined = TRUE;
10820 si->touch.draw_pressed = TRUE;
10822 for (i = 0; i < 2; i++)
10824 char *default_grid_button[6][2] =
10830 { "111222", " vv " },
10831 { "111222", " vv " }
10833 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10834 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10835 int min_xsize = MIN(6, grid_xsize);
10836 int min_ysize = MIN(6, grid_ysize);
10837 int startx = grid_xsize - min_xsize;
10838 int starty = grid_ysize - min_ysize;
10841 // virtual buttons grid can only be set to defaults if video is initialized
10842 // (this will be repeated if virtual buttons are not loaded from setup file)
10843 if (video.initialized)
10845 si->touch.grid_xsize[i] = grid_xsize;
10846 si->touch.grid_ysize[i] = grid_ysize;
10850 si->touch.grid_xsize[i] = -1;
10851 si->touch.grid_ysize[i] = -1;
10854 for (x = 0; x < MAX_GRID_XSIZE; x++)
10855 for (y = 0; y < MAX_GRID_YSIZE; y++)
10856 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10858 for (x = 0; x < min_xsize; x++)
10859 for (y = 0; y < min_ysize; y++)
10860 si->touch.grid_button[i][x][starty + y] =
10861 default_grid_button[y][0][x];
10863 for (x = 0; x < min_xsize; x++)
10864 for (y = 0; y < min_ysize; y++)
10865 si->touch.grid_button[i][startx + x][starty + y] =
10866 default_grid_button[y][1][x];
10869 si->touch.grid_initialized = video.initialized;
10871 si->touch.overlay_buttons = FALSE;
10873 si->editor.el_boulderdash = TRUE;
10874 si->editor.el_boulderdash_native = TRUE;
10875 si->editor.el_emerald_mine = TRUE;
10876 si->editor.el_emerald_mine_club = TRUE;
10877 si->editor.el_more = TRUE;
10878 si->editor.el_sokoban = TRUE;
10879 si->editor.el_supaplex = TRUE;
10880 si->editor.el_diamond_caves = TRUE;
10881 si->editor.el_dx_boulderdash = TRUE;
10883 si->editor.el_mirror_magic = TRUE;
10884 si->editor.el_deflektor = TRUE;
10886 si->editor.el_chars = TRUE;
10887 si->editor.el_steel_chars = TRUE;
10889 si->editor.el_classic = TRUE;
10890 si->editor.el_custom = TRUE;
10892 si->editor.el_user_defined = FALSE;
10893 si->editor.el_dynamic = TRUE;
10895 si->editor.el_headlines = TRUE;
10897 si->editor.show_element_token = FALSE;
10899 si->editor.show_read_only_warning = TRUE;
10901 si->editor.use_template_for_new_levels = TRUE;
10903 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10904 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10905 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10906 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10907 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10909 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10910 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10911 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10912 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10913 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10915 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10916 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10917 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10918 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10919 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10920 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10922 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10923 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10924 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10926 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10927 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10928 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10929 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10931 for (i = 0; i < MAX_PLAYERS; i++)
10933 si->input[i].use_joystick = FALSE;
10934 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10935 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10936 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10937 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10938 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10939 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10940 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10941 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10942 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10943 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10944 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10945 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10946 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10947 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10948 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10951 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10952 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10953 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10954 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10956 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10957 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10958 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10959 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10960 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10961 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10962 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10964 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10966 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10967 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10968 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10970 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10971 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10972 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10974 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10975 si->internal.choose_from_top_leveldir = FALSE;
10976 si->internal.show_scaling_in_title = TRUE;
10977 si->internal.create_user_levelset = TRUE;
10978 si->internal.info_screens_from_main = FALSE;
10980 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10981 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10983 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10984 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10985 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10986 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10987 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10988 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10989 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10990 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10991 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10992 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10994 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10995 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10996 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10997 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10998 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10999 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11000 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11001 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11002 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11003 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11005 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11006 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11008 si->debug.show_frames_per_second = FALSE;
11010 si->debug.xsn_mode = AUTO;
11011 si->debug.xsn_percent = 0;
11013 si->options.verbose = FALSE;
11014 si->options.debug = FALSE;
11015 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11017 #if defined(PLATFORM_ANDROID)
11018 si->fullscreen = TRUE;
11019 si->touch.overlay_buttons = TRUE;
11022 setHideSetupEntry(&setup.debug.xsn_mode);
11025 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11027 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11030 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11032 si->player_uuid = NULL; // (will be set later)
11033 si->player_version = 1; // (will be set later)
11035 si->use_api_server = TRUE;
11036 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11037 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11038 si->ask_for_uploading_tapes = TRUE;
11039 si->ask_for_remaining_tapes = FALSE;
11040 si->provide_uploading_tapes = TRUE;
11041 si->ask_for_using_api_server = TRUE;
11042 si->has_remaining_tapes = FALSE;
11045 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11047 si->editor_cascade.el_bd = TRUE;
11048 si->editor_cascade.el_bd_native = TRUE;
11049 si->editor_cascade.el_em = TRUE;
11050 si->editor_cascade.el_emc = TRUE;
11051 si->editor_cascade.el_rnd = TRUE;
11052 si->editor_cascade.el_sb = TRUE;
11053 si->editor_cascade.el_sp = TRUE;
11054 si->editor_cascade.el_dc = TRUE;
11055 si->editor_cascade.el_dx = TRUE;
11057 si->editor_cascade.el_mm = TRUE;
11058 si->editor_cascade.el_df = TRUE;
11060 si->editor_cascade.el_chars = FALSE;
11061 si->editor_cascade.el_steel_chars = FALSE;
11062 si->editor_cascade.el_ce = FALSE;
11063 si->editor_cascade.el_ge = FALSE;
11064 si->editor_cascade.el_es = FALSE;
11065 si->editor_cascade.el_ref = FALSE;
11066 si->editor_cascade.el_user = FALSE;
11067 si->editor_cascade.el_dynamic = FALSE;
11070 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11072 static char *getHideSetupToken(void *setup_value)
11074 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11076 if (setup_value != NULL)
11077 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11079 return hide_setup_token;
11082 void setHideSetupEntry(void *setup_value)
11084 char *hide_setup_token = getHideSetupToken(setup_value);
11086 if (hide_setup_hash == NULL)
11087 hide_setup_hash = newSetupFileHash();
11089 if (setup_value != NULL)
11090 setHashEntry(hide_setup_hash, hide_setup_token, "");
11093 void removeHideSetupEntry(void *setup_value)
11095 char *hide_setup_token = getHideSetupToken(setup_value);
11097 if (setup_value != NULL)
11098 removeHashEntry(hide_setup_hash, hide_setup_token);
11101 boolean hideSetupEntry(void *setup_value)
11103 char *hide_setup_token = getHideSetupToken(setup_value);
11105 return (setup_value != NULL &&
11106 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11109 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11110 struct TokenInfo *token_info,
11111 int token_nr, char *token_text)
11113 char *token_hide_text = getStringCat2(token_text, ".hide");
11114 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11116 // set the value of this setup option in the setup option structure
11117 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11119 // check if this setup option should be hidden in the setup menu
11120 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11121 setHideSetupEntry(token_info[token_nr].value);
11123 free(token_hide_text);
11126 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11127 struct TokenInfo *token_info,
11130 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11131 token_info[token_nr].text);
11134 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11138 if (!setup_file_hash)
11141 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11142 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11144 setup.touch.grid_initialized = TRUE;
11145 for (i = 0; i < 2; i++)
11147 int grid_xsize = setup.touch.grid_xsize[i];
11148 int grid_ysize = setup.touch.grid_ysize[i];
11151 // if virtual buttons are not loaded from setup file, repeat initializing
11152 // virtual buttons grid with default values later when video is initialized
11153 if (grid_xsize == -1 ||
11156 setup.touch.grid_initialized = FALSE;
11161 for (y = 0; y < grid_ysize; y++)
11163 char token_string[MAX_LINE_LEN];
11165 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11167 char *value_string = getHashEntry(setup_file_hash, token_string);
11169 if (value_string == NULL)
11172 for (x = 0; x < grid_xsize; x++)
11174 char c = value_string[x];
11176 setup.touch.grid_button[i][x][y] =
11177 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11182 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11183 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11185 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11186 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11188 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11192 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11194 setup_input = setup.input[pnr];
11195 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11197 char full_token[100];
11199 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11200 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11203 setup.input[pnr] = setup_input;
11206 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11207 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11209 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11210 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11212 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11213 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11215 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11216 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11218 setHideRelatedSetupEntries();
11221 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11225 if (!setup_file_hash)
11228 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11229 setSetupInfo(auto_setup_tokens, i,
11230 getHashEntry(setup_file_hash,
11231 auto_setup_tokens[i].text));
11234 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11238 if (!setup_file_hash)
11241 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11242 setSetupInfo(server_setup_tokens, i,
11243 getHashEntry(setup_file_hash,
11244 server_setup_tokens[i].text));
11247 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11251 if (!setup_file_hash)
11254 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11255 setSetupInfo(editor_cascade_setup_tokens, i,
11256 getHashEntry(setup_file_hash,
11257 editor_cascade_setup_tokens[i].text));
11260 void LoadUserNames(void)
11262 int last_user_nr = user.nr;
11265 if (global.user_names != NULL)
11267 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11268 checked_free(global.user_names[i]);
11270 checked_free(global.user_names);
11273 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11275 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11279 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11281 if (setup_file_hash)
11283 char *player_name = getHashEntry(setup_file_hash, "player_name");
11285 global.user_names[i] = getFixedUserName(player_name);
11287 freeSetupFileHash(setup_file_hash);
11290 if (global.user_names[i] == NULL)
11291 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11294 user.nr = last_user_nr;
11297 void LoadSetupFromFilename(char *filename)
11299 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11301 if (setup_file_hash)
11303 decodeSetupFileHash_Default(setup_file_hash);
11305 freeSetupFileHash(setup_file_hash);
11309 Debug("setup", "using default setup values");
11313 static void LoadSetup_SpecialPostProcessing(void)
11315 char *player_name_new;
11317 // needed to work around problems with fixed length strings
11318 player_name_new = getFixedUserName(setup.player_name);
11319 free(setup.player_name);
11320 setup.player_name = player_name_new;
11322 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11323 if (setup.scroll_delay == FALSE)
11325 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11326 setup.scroll_delay = TRUE; // now always "on"
11329 // make sure that scroll delay value stays inside valid range
11330 setup.scroll_delay_value =
11331 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11334 void LoadSetup_Default(void)
11338 // always start with reliable default values
11339 setSetupInfoToDefaults(&setup);
11341 // try to load setup values from default setup file
11342 filename = getDefaultSetupFilename();
11344 if (fileExists(filename))
11345 LoadSetupFromFilename(filename);
11347 // try to load setup values from platform setup file
11348 filename = getPlatformSetupFilename();
11350 if (fileExists(filename))
11351 LoadSetupFromFilename(filename);
11353 // try to load setup values from user setup file
11354 filename = getSetupFilename();
11356 LoadSetupFromFilename(filename);
11358 LoadSetup_SpecialPostProcessing();
11361 void LoadSetup_AutoSetup(void)
11363 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11364 SetupFileHash *setup_file_hash = NULL;
11366 // always start with reliable default values
11367 setSetupInfoToDefaults_AutoSetup(&setup);
11369 setup_file_hash = loadSetupFileHash(filename);
11371 if (setup_file_hash)
11373 decodeSetupFileHash_AutoSetup(setup_file_hash);
11375 freeSetupFileHash(setup_file_hash);
11381 void LoadSetup_ServerSetup(void)
11383 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11384 SetupFileHash *setup_file_hash = NULL;
11386 // always start with reliable default values
11387 setSetupInfoToDefaults_ServerSetup(&setup);
11389 setup_file_hash = loadSetupFileHash(filename);
11391 if (setup_file_hash)
11393 decodeSetupFileHash_ServerSetup(setup_file_hash);
11395 freeSetupFileHash(setup_file_hash);
11400 if (setup.player_uuid == NULL)
11402 // player UUID does not yet exist in setup file
11403 setup.player_uuid = getStringCopy(getUUID());
11404 setup.player_version = 2;
11406 SaveSetup_ServerSetup();
11410 void LoadSetup_EditorCascade(void)
11412 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11413 SetupFileHash *setup_file_hash = NULL;
11415 // always start with reliable default values
11416 setSetupInfoToDefaults_EditorCascade(&setup);
11418 setup_file_hash = loadSetupFileHash(filename);
11420 if (setup_file_hash)
11422 decodeSetupFileHash_EditorCascade(setup_file_hash);
11424 freeSetupFileHash(setup_file_hash);
11430 void LoadSetup(void)
11432 LoadSetup_Default();
11433 LoadSetup_AutoSetup();
11434 LoadSetup_ServerSetup();
11435 LoadSetup_EditorCascade();
11438 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11439 char *mapping_line)
11441 char mapping_guid[MAX_LINE_LEN];
11442 char *mapping_start, *mapping_end;
11444 // get GUID from game controller mapping line: copy complete line
11445 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11446 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11448 // get GUID from game controller mapping line: cut after GUID part
11449 mapping_start = strchr(mapping_guid, ',');
11450 if (mapping_start != NULL)
11451 *mapping_start = '\0';
11453 // cut newline from game controller mapping line
11454 mapping_end = strchr(mapping_line, '\n');
11455 if (mapping_end != NULL)
11456 *mapping_end = '\0';
11458 // add mapping entry to game controller mappings hash
11459 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11462 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11467 if (!(file = fopen(filename, MODE_READ)))
11469 Warn("cannot read game controller mappings file '%s'", filename);
11474 while (!feof(file))
11476 char line[MAX_LINE_LEN];
11478 if (!fgets(line, MAX_LINE_LEN, file))
11481 addGameControllerMappingToHash(mappings_hash, line);
11487 void SaveSetup_Default(void)
11489 char *filename = getSetupFilename();
11493 InitUserDataDirectory();
11495 if (!(file = fopen(filename, MODE_WRITE)))
11497 Warn("cannot write setup file '%s'", filename);
11502 fprintFileHeader(file, SETUP_FILENAME);
11504 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11506 // just to make things nicer :)
11507 if (global_setup_tokens[i].value == &setup.multiple_users ||
11508 global_setup_tokens[i].value == &setup.sound ||
11509 global_setup_tokens[i].value == &setup.graphics_set ||
11510 global_setup_tokens[i].value == &setup.volume_simple ||
11511 global_setup_tokens[i].value == &setup.network_mode ||
11512 global_setup_tokens[i].value == &setup.touch.control_type ||
11513 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11514 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11515 fprintf(file, "\n");
11517 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11520 for (i = 0; i < 2; i++)
11522 int grid_xsize = setup.touch.grid_xsize[i];
11523 int grid_ysize = setup.touch.grid_ysize[i];
11526 fprintf(file, "\n");
11528 for (y = 0; y < grid_ysize; y++)
11530 char token_string[MAX_LINE_LEN];
11531 char value_string[MAX_LINE_LEN];
11533 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11535 for (x = 0; x < grid_xsize; x++)
11537 char c = setup.touch.grid_button[i][x][y];
11539 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11542 value_string[grid_xsize] = '\0';
11544 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11548 fprintf(file, "\n");
11549 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11550 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11552 fprintf(file, "\n");
11553 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11554 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11556 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11560 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11561 fprintf(file, "\n");
11563 setup_input = setup.input[pnr];
11564 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11565 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11568 fprintf(file, "\n");
11569 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11570 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11572 // (internal setup values not saved to user setup file)
11574 fprintf(file, "\n");
11575 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11576 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11577 setup.debug.xsn_mode != AUTO)
11578 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11580 fprintf(file, "\n");
11581 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11582 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11586 SetFilePermissions(filename, PERMS_PRIVATE);
11589 void SaveSetup_AutoSetup(void)
11591 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11595 InitUserDataDirectory();
11597 if (!(file = fopen(filename, MODE_WRITE)))
11599 Warn("cannot write auto setup file '%s'", filename);
11606 fprintFileHeader(file, AUTOSETUP_FILENAME);
11608 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11609 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11613 SetFilePermissions(filename, PERMS_PRIVATE);
11618 void SaveSetup_ServerSetup(void)
11620 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11624 InitUserDataDirectory();
11626 if (!(file = fopen(filename, MODE_WRITE)))
11628 Warn("cannot write server setup file '%s'", filename);
11635 fprintFileHeader(file, SERVERSETUP_FILENAME);
11637 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11639 // just to make things nicer :)
11640 if (server_setup_tokens[i].value == &setup.use_api_server)
11641 fprintf(file, "\n");
11643 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11648 SetFilePermissions(filename, PERMS_PRIVATE);
11653 void SaveSetup_EditorCascade(void)
11655 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11659 InitUserDataDirectory();
11661 if (!(file = fopen(filename, MODE_WRITE)))
11663 Warn("cannot write editor cascade state file '%s'", filename);
11670 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11672 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11673 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11677 SetFilePermissions(filename, PERMS_PRIVATE);
11682 void SaveSetup(void)
11684 SaveSetup_Default();
11685 SaveSetup_AutoSetup();
11686 SaveSetup_ServerSetup();
11687 SaveSetup_EditorCascade();
11690 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11695 if (!(file = fopen(filename, MODE_WRITE)))
11697 Warn("cannot write game controller mappings file '%s'", filename);
11702 BEGIN_HASH_ITERATION(mappings_hash, itr)
11704 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11706 END_HASH_ITERATION(mappings_hash, itr)
11711 void SaveSetup_AddGameControllerMapping(char *mapping)
11713 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11714 SetupFileHash *mappings_hash = newSetupFileHash();
11716 InitUserDataDirectory();
11718 // load existing personal game controller mappings
11719 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11721 // add new mapping to personal game controller mappings
11722 addGameControllerMappingToHash(mappings_hash, mapping);
11724 // save updated personal game controller mappings
11725 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11727 freeSetupFileHash(mappings_hash);
11731 void LoadCustomElementDescriptions(void)
11733 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11734 SetupFileHash *setup_file_hash;
11737 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11739 if (element_info[i].custom_description != NULL)
11741 free(element_info[i].custom_description);
11742 element_info[i].custom_description = NULL;
11746 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11749 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11751 char *token = getStringCat2(element_info[i].token_name, ".name");
11752 char *value = getHashEntry(setup_file_hash, token);
11755 element_info[i].custom_description = getStringCopy(value);
11760 freeSetupFileHash(setup_file_hash);
11763 static int getElementFromToken(char *token)
11765 char *value = getHashEntry(element_token_hash, token);
11768 return atoi(value);
11770 Warn("unknown element token '%s'", token);
11772 return EL_UNDEFINED;
11775 void FreeGlobalAnimEventInfo(void)
11777 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11779 if (gaei->event_list == NULL)
11784 for (i = 0; i < gaei->num_event_lists; i++)
11786 checked_free(gaei->event_list[i]->event_value);
11787 checked_free(gaei->event_list[i]);
11790 checked_free(gaei->event_list);
11792 gaei->event_list = NULL;
11793 gaei->num_event_lists = 0;
11796 static int AddGlobalAnimEventList(void)
11798 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11799 int list_pos = gaei->num_event_lists++;
11801 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11802 sizeof(struct GlobalAnimEventListInfo *));
11804 gaei->event_list[list_pos] =
11805 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11807 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11809 gaeli->event_value = NULL;
11810 gaeli->num_event_values = 0;
11815 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11817 // do not add empty global animation events
11818 if (event_value == ANIM_EVENT_NONE)
11821 // if list position is undefined, create new list
11822 if (list_pos == ANIM_EVENT_UNDEFINED)
11823 list_pos = AddGlobalAnimEventList();
11825 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11826 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11827 int value_pos = gaeli->num_event_values++;
11829 gaeli->event_value = checked_realloc(gaeli->event_value,
11830 gaeli->num_event_values * sizeof(int *));
11832 gaeli->event_value[value_pos] = event_value;
11837 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11839 if (list_pos == ANIM_EVENT_UNDEFINED)
11840 return ANIM_EVENT_NONE;
11842 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11843 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11845 return gaeli->event_value[value_pos];
11848 int GetGlobalAnimEventValueCount(int list_pos)
11850 if (list_pos == ANIM_EVENT_UNDEFINED)
11853 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11854 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11856 return gaeli->num_event_values;
11859 // This function checks if a string <s> of the format "string1, string2, ..."
11860 // exactly contains a string <s_contained>.
11862 static boolean string_has_parameter(char *s, char *s_contained)
11866 if (s == NULL || s_contained == NULL)
11869 if (strlen(s_contained) > strlen(s))
11872 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11874 char next_char = s[strlen(s_contained)];
11876 // check if next character is delimiter or whitespace
11877 if (next_char == ',' || next_char == '\0' ||
11878 next_char == ' ' || next_char == '\t')
11882 // check if string contains another parameter string after a comma
11883 substring = strchr(s, ',');
11884 if (substring == NULL) // string does not contain a comma
11887 // advance string pointer to next character after the comma
11890 // skip potential whitespaces after the comma
11891 while (*substring == ' ' || *substring == '\t')
11894 return string_has_parameter(substring, s_contained);
11897 static int get_anim_parameter_value_ce(char *s)
11900 char *pattern_1 = "ce_change:custom_";
11901 char *pattern_2 = ".page_";
11902 int pattern_1_len = strlen(pattern_1);
11903 char *matching_char = strstr(s_ptr, pattern_1);
11904 int result = ANIM_EVENT_NONE;
11906 if (matching_char == NULL)
11907 return ANIM_EVENT_NONE;
11909 result = ANIM_EVENT_CE_CHANGE;
11911 s_ptr = matching_char + pattern_1_len;
11913 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11914 if (*s_ptr >= '0' && *s_ptr <= '9')
11916 int gic_ce_nr = (*s_ptr++ - '0');
11918 if (*s_ptr >= '0' && *s_ptr <= '9')
11920 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11922 if (*s_ptr >= '0' && *s_ptr <= '9')
11923 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11926 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11927 return ANIM_EVENT_NONE;
11929 // custom element stored as 0 to 255
11932 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11936 // invalid custom element number specified
11938 return ANIM_EVENT_NONE;
11941 // check for change page number ("page_X" or "page_XX") (optional)
11942 if (strPrefix(s_ptr, pattern_2))
11944 s_ptr += strlen(pattern_2);
11946 if (*s_ptr >= '0' && *s_ptr <= '9')
11948 int gic_page_nr = (*s_ptr++ - '0');
11950 if (*s_ptr >= '0' && *s_ptr <= '9')
11951 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11953 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11954 return ANIM_EVENT_NONE;
11956 // change page stored as 1 to 32 (0 means "all change pages")
11958 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11962 // invalid animation part number specified
11964 return ANIM_EVENT_NONE;
11968 // discard result if next character is neither delimiter nor whitespace
11969 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11970 *s_ptr == ' ' || *s_ptr == '\t'))
11971 return ANIM_EVENT_NONE;
11976 static int get_anim_parameter_value(char *s)
11978 int event_value[] =
11986 char *pattern_1[] =
11994 char *pattern_2 = ".part_";
11995 char *matching_char = NULL;
11997 int pattern_1_len = 0;
11998 int result = ANIM_EVENT_NONE;
12001 result = get_anim_parameter_value_ce(s);
12003 if (result != ANIM_EVENT_NONE)
12006 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12008 matching_char = strstr(s_ptr, pattern_1[i]);
12009 pattern_1_len = strlen(pattern_1[i]);
12010 result = event_value[i];
12012 if (matching_char != NULL)
12016 if (matching_char == NULL)
12017 return ANIM_EVENT_NONE;
12019 s_ptr = matching_char + pattern_1_len;
12021 // check for main animation number ("anim_X" or "anim_XX")
12022 if (*s_ptr >= '0' && *s_ptr <= '9')
12024 int gic_anim_nr = (*s_ptr++ - '0');
12026 if (*s_ptr >= '0' && *s_ptr <= '9')
12027 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12029 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12030 return ANIM_EVENT_NONE;
12032 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12036 // invalid main animation number specified
12038 return ANIM_EVENT_NONE;
12041 // check for animation part number ("part_X" or "part_XX") (optional)
12042 if (strPrefix(s_ptr, pattern_2))
12044 s_ptr += strlen(pattern_2);
12046 if (*s_ptr >= '0' && *s_ptr <= '9')
12048 int gic_part_nr = (*s_ptr++ - '0');
12050 if (*s_ptr >= '0' && *s_ptr <= '9')
12051 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12053 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12054 return ANIM_EVENT_NONE;
12056 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12060 // invalid animation part number specified
12062 return ANIM_EVENT_NONE;
12066 // discard result if next character is neither delimiter nor whitespace
12067 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12068 *s_ptr == ' ' || *s_ptr == '\t'))
12069 return ANIM_EVENT_NONE;
12074 static int get_anim_parameter_values(char *s)
12076 int list_pos = ANIM_EVENT_UNDEFINED;
12077 int event_value = ANIM_EVENT_DEFAULT;
12079 if (string_has_parameter(s, "any"))
12080 event_value |= ANIM_EVENT_ANY;
12082 if (string_has_parameter(s, "click:self") ||
12083 string_has_parameter(s, "click") ||
12084 string_has_parameter(s, "self"))
12085 event_value |= ANIM_EVENT_SELF;
12087 if (string_has_parameter(s, "unclick:any"))
12088 event_value |= ANIM_EVENT_UNCLICK_ANY;
12090 // if animation event found, add it to global animation event list
12091 if (event_value != ANIM_EVENT_NONE)
12092 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12096 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12097 event_value = get_anim_parameter_value(s);
12099 // if animation event found, add it to global animation event list
12100 if (event_value != ANIM_EVENT_NONE)
12101 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12103 // continue with next part of the string, starting with next comma
12104 s = strchr(s + 1, ',');
12110 static int get_anim_action_parameter_value(char *token)
12112 // check most common default case first to massively speed things up
12113 if (strEqual(token, ARG_UNDEFINED))
12114 return ANIM_EVENT_ACTION_NONE;
12116 int result = getImageIDFromToken(token);
12120 char *gfx_token = getStringCat2("gfx.", token);
12122 result = getImageIDFromToken(gfx_token);
12124 checked_free(gfx_token);
12129 Key key = getKeyFromX11KeyName(token);
12131 if (key != KSYM_UNDEFINED)
12132 result = -(int)key;
12139 result = get_hash_from_string(token); // unsigned int => int
12140 result = ABS(result); // may be negative now
12141 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12143 setHashEntry(anim_url_hash, int2str(result, 0), token);
12148 result = ANIM_EVENT_ACTION_NONE;
12153 int get_parameter_value(char *value_raw, char *suffix, int type)
12155 char *value = getStringToLower(value_raw);
12156 int result = 0; // probably a save default value
12158 if (strEqual(suffix, ".direction"))
12160 result = (strEqual(value, "left") ? MV_LEFT :
12161 strEqual(value, "right") ? MV_RIGHT :
12162 strEqual(value, "up") ? MV_UP :
12163 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12165 else if (strEqual(suffix, ".position"))
12167 result = (strEqual(value, "left") ? POS_LEFT :
12168 strEqual(value, "right") ? POS_RIGHT :
12169 strEqual(value, "top") ? POS_TOP :
12170 strEqual(value, "upper") ? POS_UPPER :
12171 strEqual(value, "middle") ? POS_MIDDLE :
12172 strEqual(value, "lower") ? POS_LOWER :
12173 strEqual(value, "bottom") ? POS_BOTTOM :
12174 strEqual(value, "any") ? POS_ANY :
12175 strEqual(value, "ce") ? POS_CE :
12176 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12177 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12179 else if (strEqual(suffix, ".align"))
12181 result = (strEqual(value, "left") ? ALIGN_LEFT :
12182 strEqual(value, "right") ? ALIGN_RIGHT :
12183 strEqual(value, "center") ? ALIGN_CENTER :
12184 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12186 else if (strEqual(suffix, ".valign"))
12188 result = (strEqual(value, "top") ? VALIGN_TOP :
12189 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12190 strEqual(value, "middle") ? VALIGN_MIDDLE :
12191 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12193 else if (strEqual(suffix, ".anim_mode"))
12195 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12196 string_has_parameter(value, "loop") ? ANIM_LOOP :
12197 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12198 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12199 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12200 string_has_parameter(value, "random") ? ANIM_RANDOM :
12201 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12202 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12203 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12204 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12205 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12206 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12207 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12208 string_has_parameter(value, "all") ? ANIM_ALL :
12209 string_has_parameter(value, "tiled") ? ANIM_TILED :
12210 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12213 if (string_has_parameter(value, "once"))
12214 result |= ANIM_ONCE;
12216 if (string_has_parameter(value, "reverse"))
12217 result |= ANIM_REVERSE;
12219 if (string_has_parameter(value, "opaque_player"))
12220 result |= ANIM_OPAQUE_PLAYER;
12222 if (string_has_parameter(value, "static_panel"))
12223 result |= ANIM_STATIC_PANEL;
12225 else if (strEqual(suffix, ".init_event") ||
12226 strEqual(suffix, ".anim_event"))
12228 result = get_anim_parameter_values(value);
12230 else if (strEqual(suffix, ".init_delay_action") ||
12231 strEqual(suffix, ".anim_delay_action") ||
12232 strEqual(suffix, ".post_delay_action") ||
12233 strEqual(suffix, ".init_event_action") ||
12234 strEqual(suffix, ".anim_event_action"))
12236 result = get_anim_action_parameter_value(value_raw);
12238 else if (strEqual(suffix, ".class"))
12240 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12241 get_hash_from_string(value));
12243 else if (strEqual(suffix, ".style"))
12245 result = STYLE_DEFAULT;
12247 if (string_has_parameter(value, "accurate_borders"))
12248 result |= STYLE_ACCURATE_BORDERS;
12250 if (string_has_parameter(value, "inner_corners"))
12251 result |= STYLE_INNER_CORNERS;
12253 if (string_has_parameter(value, "reverse"))
12254 result |= STYLE_REVERSE;
12256 if (string_has_parameter(value, "leftmost_position"))
12257 result |= STYLE_LEFTMOST_POSITION;
12259 if (string_has_parameter(value, "block_clicks"))
12260 result |= STYLE_BLOCK;
12262 if (string_has_parameter(value, "passthrough_clicks"))
12263 result |= STYLE_PASSTHROUGH;
12265 if (string_has_parameter(value, "multiple_actions"))
12266 result |= STYLE_MULTIPLE_ACTIONS;
12268 if (string_has_parameter(value, "consume_ce_event"))
12269 result |= STYLE_CONSUME_CE_EVENT;
12271 else if (strEqual(suffix, ".fade_mode"))
12273 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12274 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12275 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12276 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12277 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12278 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12279 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12280 FADE_MODE_DEFAULT);
12282 else if (strEqual(suffix, ".auto_delay_unit"))
12284 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12285 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12286 AUTO_DELAY_UNIT_DEFAULT);
12288 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12290 result = gfx.get_font_from_token_function(value);
12292 else // generic parameter of type integer or boolean
12294 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12295 type == TYPE_INTEGER ? get_integer_from_string(value) :
12296 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12297 ARG_UNDEFINED_VALUE);
12305 static int get_token_parameter_value(char *token, char *value_raw)
12309 if (token == NULL || value_raw == NULL)
12310 return ARG_UNDEFINED_VALUE;
12312 suffix = strrchr(token, '.');
12313 if (suffix == NULL)
12316 if (strEqual(suffix, ".element"))
12317 return getElementFromToken(value_raw);
12319 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12320 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12323 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12324 boolean ignore_defaults)
12328 for (i = 0; image_config_vars[i].token != NULL; i++)
12330 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12332 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12333 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12337 *image_config_vars[i].value =
12338 get_token_parameter_value(image_config_vars[i].token, value);
12342 void InitMenuDesignSettings_Static(void)
12344 // always start with reliable default values from static default config
12345 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12348 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12352 // the following initializes hierarchical values from static configuration
12354 // special case: initialize "ARG_DEFAULT" values in static default config
12355 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12356 titlescreen_initial_first_default.fade_mode =
12357 title_initial_first_default.fade_mode;
12358 titlescreen_initial_first_default.fade_delay =
12359 title_initial_first_default.fade_delay;
12360 titlescreen_initial_first_default.post_delay =
12361 title_initial_first_default.post_delay;
12362 titlescreen_initial_first_default.auto_delay =
12363 title_initial_first_default.auto_delay;
12364 titlescreen_initial_first_default.auto_delay_unit =
12365 title_initial_first_default.auto_delay_unit;
12366 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12367 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12368 titlescreen_first_default.post_delay = title_first_default.post_delay;
12369 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12370 titlescreen_first_default.auto_delay_unit =
12371 title_first_default.auto_delay_unit;
12372 titlemessage_initial_first_default.fade_mode =
12373 title_initial_first_default.fade_mode;
12374 titlemessage_initial_first_default.fade_delay =
12375 title_initial_first_default.fade_delay;
12376 titlemessage_initial_first_default.post_delay =
12377 title_initial_first_default.post_delay;
12378 titlemessage_initial_first_default.auto_delay =
12379 title_initial_first_default.auto_delay;
12380 titlemessage_initial_first_default.auto_delay_unit =
12381 title_initial_first_default.auto_delay_unit;
12382 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12383 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12384 titlemessage_first_default.post_delay = title_first_default.post_delay;
12385 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12386 titlemessage_first_default.auto_delay_unit =
12387 title_first_default.auto_delay_unit;
12389 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12390 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12391 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12392 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12393 titlescreen_initial_default.auto_delay_unit =
12394 title_initial_default.auto_delay_unit;
12395 titlescreen_default.fade_mode = title_default.fade_mode;
12396 titlescreen_default.fade_delay = title_default.fade_delay;
12397 titlescreen_default.post_delay = title_default.post_delay;
12398 titlescreen_default.auto_delay = title_default.auto_delay;
12399 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12400 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12401 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12402 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12403 titlemessage_initial_default.auto_delay_unit =
12404 title_initial_default.auto_delay_unit;
12405 titlemessage_default.fade_mode = title_default.fade_mode;
12406 titlemessage_default.fade_delay = title_default.fade_delay;
12407 titlemessage_default.post_delay = title_default.post_delay;
12408 titlemessage_default.auto_delay = title_default.auto_delay;
12409 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12411 // special case: initialize "ARG_DEFAULT" values in static default config
12412 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12413 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12415 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12416 titlescreen_first[i] = titlescreen_first_default;
12417 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12418 titlemessage_first[i] = titlemessage_first_default;
12420 titlescreen_initial[i] = titlescreen_initial_default;
12421 titlescreen[i] = titlescreen_default;
12422 titlemessage_initial[i] = titlemessage_initial_default;
12423 titlemessage[i] = titlemessage_default;
12426 // special case: initialize "ARG_DEFAULT" values in static default config
12427 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12428 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12430 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12433 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12434 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12435 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12438 // special case: initialize "ARG_DEFAULT" values in static default config
12439 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12440 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12442 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12443 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12444 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12446 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12449 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12453 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12457 struct XY *dst, *src;
12459 game_buttons_xy[] =
12461 { &game.button.save, &game.button.stop },
12462 { &game.button.pause2, &game.button.pause },
12463 { &game.button.load, &game.button.play },
12464 { &game.button.undo, &game.button.stop },
12465 { &game.button.redo, &game.button.play },
12471 // special case: initialize later added SETUP list size from LEVELS value
12472 if (menu.list_size[GAME_MODE_SETUP] == -1)
12473 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12475 // set default position for snapshot buttons to stop/pause/play buttons
12476 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12477 if ((*game_buttons_xy[i].dst).x == -1 &&
12478 (*game_buttons_xy[i].dst).y == -1)
12479 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12481 // --------------------------------------------------------------------------
12482 // dynamic viewports (including playfield margins, borders and alignments)
12483 // --------------------------------------------------------------------------
12485 // dynamic viewports currently only supported for landscape mode
12486 int display_width = MAX(video.display_width, video.display_height);
12487 int display_height = MIN(video.display_width, video.display_height);
12489 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12491 struct RectWithBorder *vp_window = &viewport.window[i];
12492 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12493 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12494 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12495 boolean dynamic_window_width = (vp_window->min_width != -1);
12496 boolean dynamic_window_height = (vp_window->min_height != -1);
12497 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12498 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12500 // adjust window size if min/max width/height is specified
12502 if (vp_window->min_width != -1)
12504 int window_width = display_width;
12506 // when using static window height, use aspect ratio of display
12507 if (vp_window->min_height == -1)
12508 window_width = vp_window->height * display_width / display_height;
12510 vp_window->width = MAX(vp_window->min_width, window_width);
12513 if (vp_window->min_height != -1)
12515 int window_height = display_height;
12517 // when using static window width, use aspect ratio of display
12518 if (vp_window->min_width == -1)
12519 window_height = vp_window->width * display_height / display_width;
12521 vp_window->height = MAX(vp_window->min_height, window_height);
12524 if (vp_window->max_width != -1)
12525 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12527 if (vp_window->max_height != -1)
12528 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12530 int playfield_width = vp_window->width;
12531 int playfield_height = vp_window->height;
12533 // adjust playfield size and position according to specified margins
12535 playfield_width -= vp_playfield->margin_left;
12536 playfield_width -= vp_playfield->margin_right;
12538 playfield_height -= vp_playfield->margin_top;
12539 playfield_height -= vp_playfield->margin_bottom;
12541 // adjust playfield size if min/max width/height is specified
12543 if (vp_playfield->min_width != -1)
12544 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12546 if (vp_playfield->min_height != -1)
12547 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12549 if (vp_playfield->max_width != -1)
12550 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12552 if (vp_playfield->max_height != -1)
12553 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12555 // adjust playfield position according to specified alignment
12557 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12558 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12559 else if (vp_playfield->align == ALIGN_CENTER)
12560 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12561 else if (vp_playfield->align == ALIGN_RIGHT)
12562 vp_playfield->x += playfield_width - vp_playfield->width;
12564 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12565 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12566 else if (vp_playfield->valign == VALIGN_MIDDLE)
12567 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12568 else if (vp_playfield->valign == VALIGN_BOTTOM)
12569 vp_playfield->y += playfield_height - vp_playfield->height;
12571 vp_playfield->x += vp_playfield->margin_left;
12572 vp_playfield->y += vp_playfield->margin_top;
12574 // adjust individual playfield borders if only default border is specified
12576 if (vp_playfield->border_left == -1)
12577 vp_playfield->border_left = vp_playfield->border_size;
12578 if (vp_playfield->border_right == -1)
12579 vp_playfield->border_right = vp_playfield->border_size;
12580 if (vp_playfield->border_top == -1)
12581 vp_playfield->border_top = vp_playfield->border_size;
12582 if (vp_playfield->border_bottom == -1)
12583 vp_playfield->border_bottom = vp_playfield->border_size;
12585 // set dynamic playfield borders if borders are specified as undefined
12586 // (but only if window size was dynamic and playfield size was static)
12588 if (dynamic_window_width && !dynamic_playfield_width)
12590 if (vp_playfield->border_left == -1)
12592 vp_playfield->border_left = (vp_playfield->x -
12593 vp_playfield->margin_left);
12594 vp_playfield->x -= vp_playfield->border_left;
12595 vp_playfield->width += vp_playfield->border_left;
12598 if (vp_playfield->border_right == -1)
12600 vp_playfield->border_right = (vp_window->width -
12602 vp_playfield->width -
12603 vp_playfield->margin_right);
12604 vp_playfield->width += vp_playfield->border_right;
12608 if (dynamic_window_height && !dynamic_playfield_height)
12610 if (vp_playfield->border_top == -1)
12612 vp_playfield->border_top = (vp_playfield->y -
12613 vp_playfield->margin_top);
12614 vp_playfield->y -= vp_playfield->border_top;
12615 vp_playfield->height += vp_playfield->border_top;
12618 if (vp_playfield->border_bottom == -1)
12620 vp_playfield->border_bottom = (vp_window->height -
12622 vp_playfield->height -
12623 vp_playfield->margin_bottom);
12624 vp_playfield->height += vp_playfield->border_bottom;
12628 // adjust playfield size to be a multiple of a defined alignment tile size
12630 int align_size = vp_playfield->align_size;
12631 int playfield_xtiles = vp_playfield->width / align_size;
12632 int playfield_ytiles = vp_playfield->height / align_size;
12633 int playfield_width_corrected = playfield_xtiles * align_size;
12634 int playfield_height_corrected = playfield_ytiles * align_size;
12635 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12636 i == GFX_SPECIAL_ARG_EDITOR);
12638 if (is_playfield_mode &&
12639 dynamic_playfield_width &&
12640 vp_playfield->width != playfield_width_corrected)
12642 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12644 vp_playfield->width = playfield_width_corrected;
12646 if (vp_playfield->align == ALIGN_LEFT)
12648 vp_playfield->border_left += playfield_xdiff;
12650 else if (vp_playfield->align == ALIGN_RIGHT)
12652 vp_playfield->border_right += playfield_xdiff;
12654 else if (vp_playfield->align == ALIGN_CENTER)
12656 int border_left_diff = playfield_xdiff / 2;
12657 int border_right_diff = playfield_xdiff - border_left_diff;
12659 vp_playfield->border_left += border_left_diff;
12660 vp_playfield->border_right += border_right_diff;
12664 if (is_playfield_mode &&
12665 dynamic_playfield_height &&
12666 vp_playfield->height != playfield_height_corrected)
12668 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12670 vp_playfield->height = playfield_height_corrected;
12672 if (vp_playfield->valign == VALIGN_TOP)
12674 vp_playfield->border_top += playfield_ydiff;
12676 else if (vp_playfield->align == VALIGN_BOTTOM)
12678 vp_playfield->border_right += playfield_ydiff;
12680 else if (vp_playfield->align == VALIGN_MIDDLE)
12682 int border_top_diff = playfield_ydiff / 2;
12683 int border_bottom_diff = playfield_ydiff - border_top_diff;
12685 vp_playfield->border_top += border_top_diff;
12686 vp_playfield->border_bottom += border_bottom_diff;
12690 // adjust door positions according to specified alignment
12692 for (j = 0; j < 2; j++)
12694 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12696 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12697 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12698 else if (vp_door->align == ALIGN_CENTER)
12699 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12700 else if (vp_door->align == ALIGN_RIGHT)
12701 vp_door->x += vp_window->width - vp_door->width;
12703 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12704 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12705 else if (vp_door->valign == VALIGN_MIDDLE)
12706 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12707 else if (vp_door->valign == VALIGN_BOTTOM)
12708 vp_door->y += vp_window->height - vp_door->height;
12713 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12717 struct XYTileSize *dst, *src;
12720 editor_buttons_xy[] =
12723 &editor.button.element_left, &editor.palette.element_left,
12724 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12727 &editor.button.element_middle, &editor.palette.element_middle,
12728 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12731 &editor.button.element_right, &editor.palette.element_right,
12732 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12739 // set default position for element buttons to element graphics
12740 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12742 if ((*editor_buttons_xy[i].dst).x == -1 &&
12743 (*editor_buttons_xy[i].dst).y == -1)
12745 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12747 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12749 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12753 // adjust editor palette rows and columns if specified to be dynamic
12755 if (editor.palette.cols == -1)
12757 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12758 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12759 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12761 editor.palette.cols = (vp_width - sc_width) / bt_width;
12763 if (editor.palette.x == -1)
12765 int palette_width = editor.palette.cols * bt_width + sc_width;
12767 editor.palette.x = (vp_width - palette_width) / 2;
12771 if (editor.palette.rows == -1)
12773 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12774 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12775 int tx_height = getFontHeight(FONT_TEXT_2);
12777 editor.palette.rows = (vp_height - tx_height) / bt_height;
12779 if (editor.palette.y == -1)
12781 int palette_height = editor.palette.rows * bt_height + tx_height;
12783 editor.palette.y = (vp_height - palette_height) / 2;
12788 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12789 boolean initialize)
12791 // special case: check if network and preview player positions are redefined,
12792 // to compare this later against the main menu level preview being redefined
12793 struct TokenIntPtrInfo menu_config_players[] =
12795 { "main.network_players.x", &menu.main.network_players.redefined },
12796 { "main.network_players.y", &menu.main.network_players.redefined },
12797 { "main.preview_players.x", &menu.main.preview_players.redefined },
12798 { "main.preview_players.y", &menu.main.preview_players.redefined },
12799 { "preview.x", &preview.redefined },
12800 { "preview.y", &preview.redefined }
12806 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12807 *menu_config_players[i].value = FALSE;
12811 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12812 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12813 *menu_config_players[i].value = TRUE;
12817 static void InitMenuDesignSettings_PreviewPlayers(void)
12819 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12822 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12824 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12827 static void LoadMenuDesignSettingsFromFilename(char *filename)
12829 static struct TitleFadingInfo tfi;
12830 static struct TitleMessageInfo tmi;
12831 static struct TokenInfo title_tokens[] =
12833 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12834 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12835 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12836 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12837 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12841 static struct TokenInfo titlemessage_tokens[] =
12843 { TYPE_INTEGER, &tmi.x, ".x" },
12844 { TYPE_INTEGER, &tmi.y, ".y" },
12845 { TYPE_INTEGER, &tmi.width, ".width" },
12846 { TYPE_INTEGER, &tmi.height, ".height" },
12847 { TYPE_INTEGER, &tmi.chars, ".chars" },
12848 { TYPE_INTEGER, &tmi.lines, ".lines" },
12849 { TYPE_INTEGER, &tmi.align, ".align" },
12850 { TYPE_INTEGER, &tmi.valign, ".valign" },
12851 { TYPE_INTEGER, &tmi.font, ".font" },
12852 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12853 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12854 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12855 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12856 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12857 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12858 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12859 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12860 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12866 struct TitleFadingInfo *info;
12871 // initialize first titles from "enter screen" definitions, if defined
12872 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12873 { &title_first_default, "menu.enter_screen.TITLE" },
12875 // initialize title screens from "next screen" definitions, if defined
12876 { &title_initial_default, "menu.next_screen.TITLE" },
12877 { &title_default, "menu.next_screen.TITLE" },
12883 struct TitleMessageInfo *array;
12886 titlemessage_arrays[] =
12888 // initialize first titles from "enter screen" definitions, if defined
12889 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12890 { titlescreen_first, "menu.enter_screen.TITLE" },
12891 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12892 { titlemessage_first, "menu.enter_screen.TITLE" },
12894 // initialize titles from "next screen" definitions, if defined
12895 { titlescreen_initial, "menu.next_screen.TITLE" },
12896 { titlescreen, "menu.next_screen.TITLE" },
12897 { titlemessage_initial, "menu.next_screen.TITLE" },
12898 { titlemessage, "menu.next_screen.TITLE" },
12900 // overwrite titles with title definitions, if defined
12901 { titlescreen_initial_first, "[title_initial]" },
12902 { titlescreen_first, "[title]" },
12903 { titlemessage_initial_first, "[title_initial]" },
12904 { titlemessage_first, "[title]" },
12906 { titlescreen_initial, "[title_initial]" },
12907 { titlescreen, "[title]" },
12908 { titlemessage_initial, "[title_initial]" },
12909 { titlemessage, "[title]" },
12911 // overwrite titles with title screen/message definitions, if defined
12912 { titlescreen_initial_first, "[titlescreen_initial]" },
12913 { titlescreen_first, "[titlescreen]" },
12914 { titlemessage_initial_first, "[titlemessage_initial]" },
12915 { titlemessage_first, "[titlemessage]" },
12917 { titlescreen_initial, "[titlescreen_initial]" },
12918 { titlescreen, "[titlescreen]" },
12919 { titlemessage_initial, "[titlemessage_initial]" },
12920 { titlemessage, "[titlemessage]" },
12924 SetupFileHash *setup_file_hash;
12927 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12930 // the following initializes hierarchical values from dynamic configuration
12932 // special case: initialize with default values that may be overwritten
12933 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12934 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12936 struct TokenIntPtrInfo menu_config[] =
12938 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12939 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12940 { "menu.list_size", &menu.list_size[i] }
12943 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12945 char *token = menu_config[j].token;
12946 char *value = getHashEntry(setup_file_hash, token);
12949 *menu_config[j].value = get_integer_from_string(value);
12953 // special case: initialize with default values that may be overwritten
12954 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12955 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12957 struct TokenIntPtrInfo menu_config[] =
12959 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12960 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12961 { "menu.list_size.INFO", &menu.list_size_info[i] },
12962 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12963 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12966 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12968 char *token = menu_config[j].token;
12969 char *value = getHashEntry(setup_file_hash, token);
12972 *menu_config[j].value = get_integer_from_string(value);
12976 // special case: initialize with default values that may be overwritten
12977 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12978 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12980 struct TokenIntPtrInfo menu_config[] =
12982 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12983 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12986 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12988 char *token = menu_config[j].token;
12989 char *value = getHashEntry(setup_file_hash, token);
12992 *menu_config[j].value = get_integer_from_string(value);
12996 // special case: initialize with default values that may be overwritten
12997 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12998 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13000 struct TokenIntPtrInfo menu_config[] =
13002 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13003 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13004 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13005 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13006 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13007 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13008 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13009 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13010 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13011 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13014 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13016 char *token = menu_config[j].token;
13017 char *value = getHashEntry(setup_file_hash, token);
13020 *menu_config[j].value = get_integer_from_string(value);
13024 // special case: initialize with default values that may be overwritten
13025 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13026 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13028 struct TokenIntPtrInfo menu_config[] =
13030 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13031 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13032 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13033 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13034 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13035 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13036 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13037 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13038 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13041 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13043 char *token = menu_config[j].token;
13044 char *value = getHashEntry(setup_file_hash, token);
13047 *menu_config[j].value = get_token_parameter_value(token, value);
13051 // special case: initialize with default values that may be overwritten
13052 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13053 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13057 char *token_prefix;
13058 struct RectWithBorder *struct_ptr;
13062 { "viewport.window", &viewport.window[i] },
13063 { "viewport.playfield", &viewport.playfield[i] },
13064 { "viewport.door_1", &viewport.door_1[i] },
13065 { "viewport.door_2", &viewport.door_2[i] }
13068 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13070 struct TokenIntPtrInfo vp_config[] =
13072 { ".x", &vp_struct[j].struct_ptr->x },
13073 { ".y", &vp_struct[j].struct_ptr->y },
13074 { ".width", &vp_struct[j].struct_ptr->width },
13075 { ".height", &vp_struct[j].struct_ptr->height },
13076 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13077 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13078 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13079 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13080 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13081 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13082 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13083 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13084 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13085 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13086 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13087 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13088 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13089 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13090 { ".align", &vp_struct[j].struct_ptr->align },
13091 { ".valign", &vp_struct[j].struct_ptr->valign }
13094 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13096 char *token = getStringCat2(vp_struct[j].token_prefix,
13097 vp_config[k].token);
13098 char *value = getHashEntry(setup_file_hash, token);
13101 *vp_config[k].value = get_token_parameter_value(token, value);
13108 // special case: initialize with default values that may be overwritten
13109 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13110 for (i = 0; title_info[i].info != NULL; i++)
13112 struct TitleFadingInfo *info = title_info[i].info;
13113 char *base_token = title_info[i].text;
13115 for (j = 0; title_tokens[j].type != -1; j++)
13117 char *token = getStringCat2(base_token, title_tokens[j].text);
13118 char *value = getHashEntry(setup_file_hash, token);
13122 int parameter_value = get_token_parameter_value(token, value);
13126 *(int *)title_tokens[j].value = (int)parameter_value;
13135 // special case: initialize with default values that may be overwritten
13136 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13137 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13139 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13140 char *base_token = titlemessage_arrays[i].text;
13142 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13144 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13145 char *value = getHashEntry(setup_file_hash, token);
13149 int parameter_value = get_token_parameter_value(token, value);
13151 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13155 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13156 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13158 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13168 // read (and overwrite with) values that may be specified in config file
13169 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13171 // special case: check if network and preview player positions are redefined
13172 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13174 freeSetupFileHash(setup_file_hash);
13177 void LoadMenuDesignSettings(void)
13179 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13181 InitMenuDesignSettings_Static();
13182 InitMenuDesignSettings_SpecialPreProcessing();
13183 InitMenuDesignSettings_PreviewPlayers();
13185 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13187 // first look for special settings configured in level series config
13188 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13190 if (fileExists(filename_base))
13191 LoadMenuDesignSettingsFromFilename(filename_base);
13194 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13196 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13197 LoadMenuDesignSettingsFromFilename(filename_local);
13199 InitMenuDesignSettings_SpecialPostProcessing();
13202 void LoadMenuDesignSettings_AfterGraphics(void)
13204 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13207 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13208 boolean ignore_defaults)
13212 for (i = 0; sound_config_vars[i].token != NULL; i++)
13214 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13216 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13217 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13221 *sound_config_vars[i].value =
13222 get_token_parameter_value(sound_config_vars[i].token, value);
13226 void InitSoundSettings_Static(void)
13228 // always start with reliable default values from static default config
13229 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13232 static void LoadSoundSettingsFromFilename(char *filename)
13234 SetupFileHash *setup_file_hash;
13236 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13239 // read (and overwrite with) values that may be specified in config file
13240 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13242 freeSetupFileHash(setup_file_hash);
13245 void LoadSoundSettings(void)
13247 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13249 InitSoundSettings_Static();
13251 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13253 // first look for special settings configured in level series config
13254 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13256 if (fileExists(filename_base))
13257 LoadSoundSettingsFromFilename(filename_base);
13260 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13262 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13263 LoadSoundSettingsFromFilename(filename_local);
13266 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13268 char *filename = getEditorSetupFilename();
13269 SetupFileList *setup_file_list, *list;
13270 SetupFileHash *element_hash;
13271 int num_unknown_tokens = 0;
13274 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13277 element_hash = newSetupFileHash();
13279 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13280 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13282 // determined size may be larger than needed (due to unknown elements)
13284 for (list = setup_file_list; list != NULL; list = list->next)
13287 // add space for up to 3 more elements for padding that may be needed
13288 *num_elements += 3;
13290 // free memory for old list of elements, if needed
13291 checked_free(*elements);
13293 // allocate memory for new list of elements
13294 *elements = checked_malloc(*num_elements * sizeof(int));
13297 for (list = setup_file_list; list != NULL; list = list->next)
13299 char *value = getHashEntry(element_hash, list->token);
13301 if (value == NULL) // try to find obsolete token mapping
13303 char *mapped_token = get_mapped_token(list->token);
13305 if (mapped_token != NULL)
13307 value = getHashEntry(element_hash, mapped_token);
13309 free(mapped_token);
13315 (*elements)[(*num_elements)++] = atoi(value);
13319 if (num_unknown_tokens == 0)
13322 Warn("unknown token(s) found in config file:");
13323 Warn("- config file: '%s'", filename);
13325 num_unknown_tokens++;
13328 Warn("- token: '%s'", list->token);
13332 if (num_unknown_tokens > 0)
13335 while (*num_elements % 4) // pad with empty elements, if needed
13336 (*elements)[(*num_elements)++] = EL_EMPTY;
13338 freeSetupFileList(setup_file_list);
13339 freeSetupFileHash(element_hash);
13342 for (i = 0; i < *num_elements; i++)
13343 Debug("editor", "element '%s' [%d]\n",
13344 element_info[(*elements)[i]].token_name, (*elements)[i]);
13348 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13351 SetupFileHash *setup_file_hash = NULL;
13352 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13353 char *filename_music, *filename_prefix, *filename_info;
13359 token_to_value_ptr[] =
13361 { "title_header", &tmp_music_file_info.title_header },
13362 { "artist_header", &tmp_music_file_info.artist_header },
13363 { "album_header", &tmp_music_file_info.album_header },
13364 { "year_header", &tmp_music_file_info.year_header },
13365 { "played_header", &tmp_music_file_info.played_header },
13367 { "title", &tmp_music_file_info.title },
13368 { "artist", &tmp_music_file_info.artist },
13369 { "album", &tmp_music_file_info.album },
13370 { "year", &tmp_music_file_info.year },
13371 { "played", &tmp_music_file_info.played },
13377 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13378 getCustomMusicFilename(basename));
13380 if (filename_music == NULL)
13383 // ---------- try to replace file extension ----------
13385 filename_prefix = getStringCopy(filename_music);
13386 if (strrchr(filename_prefix, '.') != NULL)
13387 *strrchr(filename_prefix, '.') = '\0';
13388 filename_info = getStringCat2(filename_prefix, ".txt");
13390 if (fileExists(filename_info))
13391 setup_file_hash = loadSetupFileHash(filename_info);
13393 free(filename_prefix);
13394 free(filename_info);
13396 if (setup_file_hash == NULL)
13398 // ---------- try to add file extension ----------
13400 filename_prefix = getStringCopy(filename_music);
13401 filename_info = getStringCat2(filename_prefix, ".txt");
13403 if (fileExists(filename_info))
13404 setup_file_hash = loadSetupFileHash(filename_info);
13406 free(filename_prefix);
13407 free(filename_info);
13410 if (setup_file_hash == NULL)
13413 // ---------- music file info found ----------
13415 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13417 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13419 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13421 *token_to_value_ptr[i].value_ptr =
13422 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13425 tmp_music_file_info.basename = getStringCopy(basename);
13426 tmp_music_file_info.music = music;
13427 tmp_music_file_info.is_sound = is_sound;
13429 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13430 *new_music_file_info = tmp_music_file_info;
13432 return new_music_file_info;
13435 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13437 return get_music_file_info_ext(basename, music, FALSE);
13440 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13442 return get_music_file_info_ext(basename, sound, TRUE);
13445 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13446 char *basename, boolean is_sound)
13448 for (; list != NULL; list = list->next)
13449 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13455 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13457 return music_info_listed_ext(list, basename, FALSE);
13460 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13462 return music_info_listed_ext(list, basename, TRUE);
13465 void LoadMusicInfo(void)
13467 int num_music_noconf = getMusicListSize_NoConf();
13468 int num_music = getMusicListSize();
13469 int num_sounds = getSoundListSize();
13470 struct FileInfo *music, *sound;
13471 struct MusicFileInfo *next, **new;
13475 while (music_file_info != NULL)
13477 next = music_file_info->next;
13479 checked_free(music_file_info->basename);
13481 checked_free(music_file_info->title_header);
13482 checked_free(music_file_info->artist_header);
13483 checked_free(music_file_info->album_header);
13484 checked_free(music_file_info->year_header);
13485 checked_free(music_file_info->played_header);
13487 checked_free(music_file_info->title);
13488 checked_free(music_file_info->artist);
13489 checked_free(music_file_info->album);
13490 checked_free(music_file_info->year);
13491 checked_free(music_file_info->played);
13493 free(music_file_info);
13495 music_file_info = next;
13498 new = &music_file_info;
13500 // get (configured or unconfigured) music file info for all levels
13501 for (i = leveldir_current->first_level;
13502 i <= leveldir_current->last_level; i++)
13506 if (levelset.music[i] != MUS_UNDEFINED)
13508 // get music file info for configured level music
13509 music_nr = levelset.music[i];
13511 else if (num_music_noconf > 0)
13513 // get music file info for unconfigured level music
13514 int level_pos = i - leveldir_current->first_level;
13516 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13523 char *basename = getMusicInfoEntryFilename(music_nr);
13525 if (basename == NULL)
13528 if (!music_info_listed(music_file_info, basename))
13530 *new = get_music_file_info(basename, music_nr);
13533 new = &(*new)->next;
13537 // get music file info for all remaining configured music files
13538 for (i = 0; i < num_music; i++)
13540 music = getMusicListEntry(i);
13542 if (music->filename == NULL)
13545 if (strEqual(music->filename, UNDEFINED_FILENAME))
13548 // a configured file may be not recognized as music
13549 if (!FileIsMusic(music->filename))
13552 if (!music_info_listed(music_file_info, music->filename))
13554 *new = get_music_file_info(music->filename, i);
13557 new = &(*new)->next;
13561 // get sound file info for all configured sound files
13562 for (i = 0; i < num_sounds; i++)
13564 sound = getSoundListEntry(i);
13566 if (sound->filename == NULL)
13569 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13572 // a configured file may be not recognized as sound
13573 if (!FileIsSound(sound->filename))
13576 if (!sound_info_listed(music_file_info, sound->filename))
13578 *new = get_sound_file_info(sound->filename, i);
13580 new = &(*new)->next;
13584 // add pointers to previous list nodes
13586 struct MusicFileInfo *node = music_file_info;
13588 while (node != NULL)
13591 node->next->prev = node;
13597 static void add_helpanim_entry(int element, int action, int direction,
13598 int delay, int *num_list_entries)
13600 struct HelpAnimInfo *new_list_entry;
13601 (*num_list_entries)++;
13604 checked_realloc(helpanim_info,
13605 *num_list_entries * sizeof(struct HelpAnimInfo));
13606 new_list_entry = &helpanim_info[*num_list_entries - 1];
13608 new_list_entry->element = element;
13609 new_list_entry->action = action;
13610 new_list_entry->direction = direction;
13611 new_list_entry->delay = delay;
13614 static void print_unknown_token(char *filename, char *token, int token_nr)
13619 Warn("unknown token(s) found in config file:");
13620 Warn("- config file: '%s'", filename);
13623 Warn("- token: '%s'", token);
13626 static void print_unknown_token_end(int token_nr)
13632 void LoadHelpAnimInfo(void)
13634 char *filename = getHelpAnimFilename();
13635 SetupFileList *setup_file_list = NULL, *list;
13636 SetupFileHash *element_hash, *action_hash, *direction_hash;
13637 int num_list_entries = 0;
13638 int num_unknown_tokens = 0;
13641 if (fileExists(filename))
13642 setup_file_list = loadSetupFileList(filename);
13644 if (setup_file_list == NULL)
13646 // use reliable default values from static configuration
13647 SetupFileList *insert_ptr;
13649 insert_ptr = setup_file_list =
13650 newSetupFileList(helpanim_config[0].token,
13651 helpanim_config[0].value);
13653 for (i = 1; helpanim_config[i].token; i++)
13654 insert_ptr = addListEntry(insert_ptr,
13655 helpanim_config[i].token,
13656 helpanim_config[i].value);
13659 element_hash = newSetupFileHash();
13660 action_hash = newSetupFileHash();
13661 direction_hash = newSetupFileHash();
13663 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13664 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13666 for (i = 0; i < NUM_ACTIONS; i++)
13667 setHashEntry(action_hash, element_action_info[i].suffix,
13668 i_to_a(element_action_info[i].value));
13670 // do not store direction index (bit) here, but direction value!
13671 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13672 setHashEntry(direction_hash, element_direction_info[i].suffix,
13673 i_to_a(1 << element_direction_info[i].value));
13675 for (list = setup_file_list; list != NULL; list = list->next)
13677 char *element_token, *action_token, *direction_token;
13678 char *element_value, *action_value, *direction_value;
13679 int delay = atoi(list->value);
13681 if (strEqual(list->token, "end"))
13683 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13688 /* first try to break element into element/action/direction parts;
13689 if this does not work, also accept combined "element[.act][.dir]"
13690 elements (like "dynamite.active"), which are unique elements */
13692 if (strchr(list->token, '.') == NULL) // token contains no '.'
13694 element_value = getHashEntry(element_hash, list->token);
13695 if (element_value != NULL) // element found
13696 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13697 &num_list_entries);
13700 // no further suffixes found -- this is not an element
13701 print_unknown_token(filename, list->token, num_unknown_tokens++);
13707 // token has format "<prefix>.<something>"
13709 action_token = strchr(list->token, '.'); // suffix may be action ...
13710 direction_token = action_token; // ... or direction
13712 element_token = getStringCopy(list->token);
13713 *strchr(element_token, '.') = '\0';
13715 element_value = getHashEntry(element_hash, element_token);
13717 if (element_value == NULL) // this is no element
13719 element_value = getHashEntry(element_hash, list->token);
13720 if (element_value != NULL) // combined element found
13721 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13722 &num_list_entries);
13724 print_unknown_token(filename, list->token, num_unknown_tokens++);
13726 free(element_token);
13731 action_value = getHashEntry(action_hash, action_token);
13733 if (action_value != NULL) // action found
13735 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13736 &num_list_entries);
13738 free(element_token);
13743 direction_value = getHashEntry(direction_hash, direction_token);
13745 if (direction_value != NULL) // direction found
13747 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13748 &num_list_entries);
13750 free(element_token);
13755 if (strchr(action_token + 1, '.') == NULL)
13757 // no further suffixes found -- this is not an action nor direction
13759 element_value = getHashEntry(element_hash, list->token);
13760 if (element_value != NULL) // combined element found
13761 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13762 &num_list_entries);
13764 print_unknown_token(filename, list->token, num_unknown_tokens++);
13766 free(element_token);
13771 // token has format "<prefix>.<suffix>.<something>"
13773 direction_token = strchr(action_token + 1, '.');
13775 action_token = getStringCopy(action_token);
13776 *strchr(action_token + 1, '.') = '\0';
13778 action_value = getHashEntry(action_hash, action_token);
13780 if (action_value == NULL) // this is no action
13782 element_value = getHashEntry(element_hash, list->token);
13783 if (element_value != NULL) // combined element found
13784 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13785 &num_list_entries);
13787 print_unknown_token(filename, list->token, num_unknown_tokens++);
13789 free(element_token);
13790 free(action_token);
13795 direction_value = getHashEntry(direction_hash, direction_token);
13797 if (direction_value != NULL) // direction found
13799 add_helpanim_entry(atoi(element_value), atoi(action_value),
13800 atoi(direction_value), delay, &num_list_entries);
13802 free(element_token);
13803 free(action_token);
13808 // this is no direction
13810 element_value = getHashEntry(element_hash, list->token);
13811 if (element_value != NULL) // combined element found
13812 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13813 &num_list_entries);
13815 print_unknown_token(filename, list->token, num_unknown_tokens++);
13817 free(element_token);
13818 free(action_token);
13821 print_unknown_token_end(num_unknown_tokens);
13823 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13824 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13826 freeSetupFileList(setup_file_list);
13827 freeSetupFileHash(element_hash);
13828 freeSetupFileHash(action_hash);
13829 freeSetupFileHash(direction_hash);
13832 for (i = 0; i < num_list_entries; i++)
13833 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13834 EL_NAME(helpanim_info[i].element),
13835 helpanim_info[i].element,
13836 helpanim_info[i].action,
13837 helpanim_info[i].direction,
13838 helpanim_info[i].delay);
13842 void LoadHelpTextInfo(void)
13844 char *filename = getHelpTextFilename();
13847 if (helptext_info != NULL)
13849 freeSetupFileHash(helptext_info);
13850 helptext_info = NULL;
13853 if (fileExists(filename))
13854 helptext_info = loadSetupFileHash(filename);
13856 if (helptext_info == NULL)
13858 // use reliable default values from static configuration
13859 helptext_info = newSetupFileHash();
13861 for (i = 0; helptext_config[i].token; i++)
13862 setHashEntry(helptext_info,
13863 helptext_config[i].token,
13864 helptext_config[i].value);
13868 BEGIN_HASH_ITERATION(helptext_info, itr)
13870 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13871 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13873 END_HASH_ITERATION(hash, itr)
13878 // ----------------------------------------------------------------------------
13880 // ----------------------------------------------------------------------------
13882 #define MAX_NUM_CONVERT_LEVELS 1000
13884 void ConvertLevels(void)
13886 static LevelDirTree *convert_leveldir = NULL;
13887 static int convert_level_nr = -1;
13888 static int num_levels_handled = 0;
13889 static int num_levels_converted = 0;
13890 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13893 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13894 global.convert_leveldir);
13896 if (convert_leveldir == NULL)
13897 Fail("no such level identifier: '%s'", global.convert_leveldir);
13899 leveldir_current = convert_leveldir;
13901 if (global.convert_level_nr != -1)
13903 convert_leveldir->first_level = global.convert_level_nr;
13904 convert_leveldir->last_level = global.convert_level_nr;
13907 convert_level_nr = convert_leveldir->first_level;
13909 PrintLine("=", 79);
13910 Print("Converting levels\n");
13911 PrintLine("-", 79);
13912 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13913 Print("Level series name: '%s'\n", convert_leveldir->name);
13914 Print("Level series author: '%s'\n", convert_leveldir->author);
13915 Print("Number of levels: %d\n", convert_leveldir->levels);
13916 PrintLine("=", 79);
13919 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13920 levels_failed[i] = FALSE;
13922 while (convert_level_nr <= convert_leveldir->last_level)
13924 char *level_filename;
13927 level_nr = convert_level_nr++;
13929 Print("Level %03d: ", level_nr);
13931 LoadLevel(level_nr);
13932 if (level.no_level_file || level.no_valid_file)
13934 Print("(no level)\n");
13938 Print("converting level ... ");
13941 // special case: conversion of some EMC levels as requested by ACME
13942 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13945 level_filename = getDefaultLevelFilename(level_nr);
13946 new_level = !fileExists(level_filename);
13950 SaveLevel(level_nr);
13952 num_levels_converted++;
13954 Print("converted.\n");
13958 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13959 levels_failed[level_nr] = TRUE;
13961 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13964 num_levels_handled++;
13968 PrintLine("=", 79);
13969 Print("Number of levels handled: %d\n", num_levels_handled);
13970 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13971 (num_levels_handled ?
13972 num_levels_converted * 100 / num_levels_handled : 0));
13973 PrintLine("-", 79);
13974 Print("Summary (for automatic parsing by scripts):\n");
13975 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13976 convert_leveldir->identifier, num_levels_converted,
13977 num_levels_handled,
13978 (num_levels_handled ?
13979 num_levels_converted * 100 / num_levels_handled : 0));
13981 if (num_levels_handled != num_levels_converted)
13983 Print(", FAILED:");
13984 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13985 if (levels_failed[i])
13990 PrintLine("=", 79);
13992 CloseAllAndExit(0);
13996 // ----------------------------------------------------------------------------
13997 // create and save images for use in level sketches (raw BMP format)
13998 // ----------------------------------------------------------------------------
14000 void CreateLevelSketchImages(void)
14006 InitElementPropertiesGfxElement();
14008 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14009 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14011 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14013 int element = getMappedElement(i);
14014 char basename1[16];
14015 char basename2[16];
14019 sprintf(basename1, "%04d.bmp", i);
14020 sprintf(basename2, "%04ds.bmp", i);
14022 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14023 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14025 DrawSizedElement(0, 0, element, TILESIZE);
14026 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14028 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14029 Fail("cannot save level sketch image file '%s'", filename1);
14031 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14032 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14034 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14035 Fail("cannot save level sketch image file '%s'", filename2);
14040 // create corresponding SQL statements (for normal and small images)
14043 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14044 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14047 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14048 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14050 // optional: create content for forum level sketch demonstration post
14052 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14055 FreeBitmap(bitmap1);
14056 FreeBitmap(bitmap2);
14059 fprintf(stderr, "\n");
14061 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14063 CloseAllAndExit(0);
14067 // ----------------------------------------------------------------------------
14068 // create and save images for element collecting animations (raw BMP format)
14069 // ----------------------------------------------------------------------------
14071 static boolean createCollectImage(int element)
14073 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14076 void CreateCollectElementImages(void)
14080 int anim_frames = num_steps - 1;
14081 int tile_size = TILESIZE;
14082 int anim_width = tile_size * anim_frames;
14083 int anim_height = tile_size;
14084 int num_collect_images = 0;
14085 int pos_collect_images = 0;
14087 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14088 if (createCollectImage(i))
14089 num_collect_images++;
14091 Info("Creating %d element collecting animation images ...",
14092 num_collect_images);
14094 int dst_width = anim_width * 2;
14095 int dst_height = anim_height * num_collect_images / 2;
14096 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14097 char *basename_bmp = "RocksCollect.bmp";
14098 char *basename_png = "RocksCollect.png";
14099 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14100 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14101 int len_filename_bmp = strlen(filename_bmp);
14102 int len_filename_png = strlen(filename_png);
14103 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14104 char cmd_convert[max_command_len];
14106 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14110 // force using RGBA surface for destination bitmap
14111 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14112 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14114 dst_bitmap->surface =
14115 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14117 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14119 if (!createCollectImage(i))
14122 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14123 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14124 int graphic = el2img(i);
14125 char *token_name = element_info[i].token_name;
14126 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14127 Bitmap *src_bitmap;
14130 Info("- creating collecting image for '%s' ...", token_name);
14132 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14134 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14135 tile_size, tile_size, 0, 0);
14137 // force using RGBA surface for temporary bitmap (using transparent black)
14138 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14139 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14141 tmp_bitmap->surface =
14142 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14144 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14146 for (j = 0; j < anim_frames; j++)
14148 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14149 int frame_size = frame_size_final * num_steps;
14150 int offset = (tile_size - frame_size_final) / 2;
14151 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14153 while (frame_size > frame_size_final)
14157 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14159 FreeBitmap(frame_bitmap);
14161 frame_bitmap = half_bitmap;
14164 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14165 frame_size_final, frame_size_final,
14166 dst_x + j * tile_size + offset, dst_y + offset);
14168 FreeBitmap(frame_bitmap);
14171 tmp_bitmap->surface_masked = NULL;
14173 FreeBitmap(tmp_bitmap);
14175 pos_collect_images++;
14178 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14179 Fail("cannot save element collecting image file '%s'", filename_bmp);
14181 FreeBitmap(dst_bitmap);
14183 Info("Converting image file from BMP to PNG ...");
14185 if (system(cmd_convert) != 0)
14186 Fail("converting image file failed");
14188 unlink(filename_bmp);
14192 CloseAllAndExit(0);
14196 // ----------------------------------------------------------------------------
14197 // create and save images for custom and group elements (raw BMP format)
14198 // ----------------------------------------------------------------------------
14200 void CreateCustomElementImages(char *directory)
14202 char *src_basename = "RocksCE-template.ilbm";
14203 char *dst_basename = "RocksCE.bmp";
14204 char *src_filename = getPath2(directory, src_basename);
14205 char *dst_filename = getPath2(directory, dst_basename);
14206 Bitmap *src_bitmap;
14208 int yoffset_ce = 0;
14209 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14212 InitVideoDefaults();
14214 ReCreateBitmap(&backbuffer, video.width, video.height);
14216 src_bitmap = LoadImage(src_filename);
14218 bitmap = CreateBitmap(TILEX * 16 * 2,
14219 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14222 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14229 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14230 TILEX * x, TILEY * y + yoffset_ce);
14232 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14234 TILEX * x + TILEX * 16,
14235 TILEY * y + yoffset_ce);
14237 for (j = 2; j >= 0; j--)
14241 BlitBitmap(src_bitmap, bitmap,
14242 TILEX + c * 7, 0, 6, 10,
14243 TILEX * x + 6 + j * 7,
14244 TILEY * y + 11 + yoffset_ce);
14246 BlitBitmap(src_bitmap, bitmap,
14247 TILEX + c * 8, TILEY, 6, 10,
14248 TILEX * 16 + TILEX * x + 6 + j * 8,
14249 TILEY * y + 10 + yoffset_ce);
14255 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14262 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14263 TILEX * x, TILEY * y + yoffset_ge);
14265 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14267 TILEX * x + TILEX * 16,
14268 TILEY * y + yoffset_ge);
14270 for (j = 1; j >= 0; j--)
14274 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14275 TILEX * x + 6 + j * 10,
14276 TILEY * y + 11 + yoffset_ge);
14278 BlitBitmap(src_bitmap, bitmap,
14279 TILEX + c * 8, TILEY + 12, 6, 10,
14280 TILEX * 16 + TILEX * x + 10 + j * 8,
14281 TILEY * y + 10 + yoffset_ge);
14287 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14288 Fail("cannot save CE graphics file '%s'", dst_filename);
14290 FreeBitmap(bitmap);
14292 CloseAllAndExit(0);