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
344 TYPE_BOOLEAN, CONF_VALUE_8_BIT(24),
345 &li.bd_gravity_affects_all, TRUE
355 static struct LevelFileConfigInfo chunk_config_ELEM[] =
357 // (these values are the same for each player)
360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
361 &li.block_last_field, FALSE // default case for EM levels
365 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
366 &li.sp_block_last_field, TRUE // default case for SP levels
370 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
371 &li.instant_relocation, FALSE
375 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
376 &li.can_pass_to_walkable, FALSE
380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
381 &li.block_snap_field, TRUE
385 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
386 &li.continuous_snapping, TRUE
390 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
391 &li.shifted_relocation, FALSE
395 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
396 &li.lazy_relocation, FALSE
400 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
401 &li.finish_dig_collect, TRUE
405 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
406 &li.keep_walkable_ce, FALSE
409 // (these values are different for each player)
412 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
413 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
417 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
418 &li.initial_player_gravity[0], FALSE
422 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
423 &li.use_start_element[0], FALSE
427 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
428 &li.start_element[0], EL_PLAYER_1
432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
433 &li.use_artwork_element[0], FALSE
437 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
438 &li.artwork_element[0], EL_PLAYER_1
442 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
443 &li.use_explosion_element[0], FALSE
447 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
448 &li.explosion_element[0], EL_PLAYER_1
452 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
453 &li.use_initial_inventory[0], FALSE
457 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
458 &li.initial_inventory_size[0], 1
462 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
463 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
464 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
469 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
470 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
474 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
475 &li.initial_player_gravity[1], FALSE
479 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
480 &li.use_start_element[1], FALSE
484 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
485 &li.start_element[1], EL_PLAYER_2
489 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
490 &li.use_artwork_element[1], FALSE
494 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
495 &li.artwork_element[1], EL_PLAYER_2
499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
500 &li.use_explosion_element[1], FALSE
504 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
505 &li.explosion_element[1], EL_PLAYER_2
509 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
510 &li.use_initial_inventory[1], FALSE
514 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
515 &li.initial_inventory_size[1], 1
519 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
520 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
521 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
526 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
527 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
531 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
532 &li.initial_player_gravity[2], FALSE
536 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
537 &li.use_start_element[2], FALSE
541 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
542 &li.start_element[2], EL_PLAYER_3
546 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
547 &li.use_artwork_element[2], FALSE
551 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
552 &li.artwork_element[2], EL_PLAYER_3
556 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
557 &li.use_explosion_element[2], FALSE
561 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
562 &li.explosion_element[2], EL_PLAYER_3
566 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
567 &li.use_initial_inventory[2], FALSE
571 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
572 &li.initial_inventory_size[2], 1
576 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
577 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
578 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
583 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
584 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
588 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
589 &li.initial_player_gravity[3], FALSE
593 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
594 &li.use_start_element[3], FALSE
598 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
599 &li.start_element[3], EL_PLAYER_4
603 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
604 &li.use_artwork_element[3], FALSE
608 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
609 &li.artwork_element[3], EL_PLAYER_4
613 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
614 &li.use_explosion_element[3], FALSE
618 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
619 &li.explosion_element[3], EL_PLAYER_4
623 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
624 &li.use_initial_inventory[3], FALSE
628 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
629 &li.initial_inventory_size[3], 1
633 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
634 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
635 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
638 // (these values are only valid for BD style levels)
641 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
642 &li.bd_diagonal_movements, FALSE
646 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
647 &li.bd_topmost_player_active, TRUE
652 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
653 &li.score[SC_DIAMOND_EXTRA], 20
656 // (the following values are related to various game elements)
660 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
661 &li.score[SC_EMERALD], 10
666 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
667 &li.score[SC_DIAMOND], 10
672 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
673 &li.score[SC_BUG], 10
678 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
679 &li.score[SC_SPACESHIP], 10
684 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
685 &li.score[SC_PACMAN], 10
690 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
691 &li.score[SC_NUT], 10
696 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
697 &li.score[SC_DYNAMITE], 10
702 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
703 &li.score[SC_KEY], 10
708 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
709 &li.score[SC_PEARL], 10
714 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
715 &li.score[SC_CRYSTAL], 10
720 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
721 &li.amoeba_content, EL_DIAMOND
725 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
730 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
731 &li.grow_into_diggable, TRUE
736 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
737 &li.yamyam_content, EL_ROCK, NULL,
738 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
742 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
743 &li.score[SC_YAMYAM], 10
748 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
749 &li.score[SC_ROBOT], 10
753 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
759 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
765 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
766 &li.time_magic_wall, 10
771 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
772 &li.game_of_life[0], 2
776 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
777 &li.game_of_life[1], 3
781 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
782 &li.game_of_life[2], 3
786 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
787 &li.game_of_life[3], 3
791 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
792 &li.use_life_bugs, FALSE
797 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
802 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
807 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
812 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
817 EL_TIMEGATE_SWITCH, -1,
818 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
819 &li.time_timegate, 10
823 EL_LIGHT_SWITCH_ACTIVE, -1,
824 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
829 EL_SHIELD_NORMAL, -1,
830 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
831 &li.shield_normal_time, 10
834 EL_SHIELD_NORMAL, -1,
835 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
836 &li.score[SC_SHIELD], 10
840 EL_SHIELD_DEADLY, -1,
841 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
842 &li.shield_deadly_time, 10
845 EL_SHIELD_DEADLY, -1,
846 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
847 &li.score[SC_SHIELD], 10
852 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
857 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
858 &li.extra_time_score, 10
862 EL_TIME_ORB_FULL, -1,
863 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
864 &li.time_orb_time, 10
867 EL_TIME_ORB_FULL, -1,
868 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
869 &li.use_time_orb_bug, FALSE
874 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
875 &li.use_spring_bug, FALSE
880 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
881 &li.android_move_time, 10
885 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
886 &li.android_clone_time, 10
889 EL_EMC_ANDROID, SAVE_CONF_NEVER,
890 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
891 &li.android_clone_element[0], EL_EMPTY, NULL,
892 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
896 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
897 &li.android_clone_element[0], EL_EMPTY, NULL,
898 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
903 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
908 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
913 EL_EMC_MAGNIFIER, -1,
914 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
915 &li.magnify_score, 10
918 EL_EMC_MAGNIFIER, -1,
919 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
924 EL_EMC_MAGIC_BALL, -1,
925 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
929 EL_EMC_MAGIC_BALL, -1,
930 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
931 &li.ball_random, FALSE
934 EL_EMC_MAGIC_BALL, -1,
935 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
936 &li.ball_active_initial, FALSE
939 EL_EMC_MAGIC_BALL, -1,
940 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
941 &li.ball_content, EL_EMPTY, NULL,
942 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
946 EL_SOKOBAN_FIELD_EMPTY, -1,
947 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
948 &li.sb_fields_needed, TRUE
952 EL_SOKOBAN_OBJECT, -1,
953 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
954 &li.sb_objects_needed, TRUE
959 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
960 &li.mm_laser_red, FALSE
964 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
965 &li.mm_laser_green, FALSE
969 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
970 &li.mm_laser_blue, TRUE
975 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
976 &li.df_laser_red, TRUE
980 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
981 &li.df_laser_green, TRUE
985 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
986 &li.df_laser_blue, FALSE
990 EL_MM_FUSE_ACTIVE, -1,
991 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
996 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1001 EL_MM_GRAY_BALL, -1,
1002 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1003 &li.mm_time_ball, 75
1006 EL_MM_GRAY_BALL, -1,
1007 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1008 &li.mm_ball_choice_mode, ANIM_RANDOM
1011 EL_MM_GRAY_BALL, -1,
1012 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
1013 &li.mm_ball_content, EL_EMPTY, NULL,
1014 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
1017 EL_MM_GRAY_BALL, -1,
1018 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1019 &li.rotate_mm_ball_content, TRUE
1022 EL_MM_GRAY_BALL, -1,
1023 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1024 &li.explode_mm_ball, FALSE
1028 EL_MM_STEEL_BLOCK, -1,
1029 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1030 &li.mm_time_block, 75
1033 EL_MM_LIGHTBALL, -1,
1034 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1035 &li.score[SC_ELEM_BONUS], 10
1045 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1049 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1050 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1054 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1055 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1060 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1061 &xx_envelope.autowrap, FALSE
1065 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1066 &xx_envelope.centered, FALSE
1071 TYPE_STRING, CONF_VALUE_BYTES(1),
1072 &xx_envelope.text, -1, NULL,
1073 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1074 &xx_default_string_empty[0]
1084 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1088 TYPE_STRING, CONF_VALUE_BYTES(1),
1089 &xx_ei.description[0], -1,
1090 &yy_ei.description[0],
1091 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1092 &xx_default_description[0]
1097 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1098 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1099 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1101 #if ENABLE_RESERVED_CODE
1102 // (reserved for later use)
1105 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1106 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1107 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1113 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1114 &xx_ei.use_gfx_element, FALSE,
1115 &yy_ei.use_gfx_element
1119 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1120 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1121 &yy_ei.gfx_element_initial
1126 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1127 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1128 &yy_ei.access_direction
1133 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1134 &xx_ei.collect_score_initial, 10,
1135 &yy_ei.collect_score_initial
1139 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1140 &xx_ei.collect_count_initial, 1,
1141 &yy_ei.collect_count_initial
1146 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1147 &xx_ei.ce_value_fixed_initial, 0,
1148 &yy_ei.ce_value_fixed_initial
1152 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1153 &xx_ei.ce_value_random_initial, 0,
1154 &yy_ei.ce_value_random_initial
1158 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1159 &xx_ei.use_last_ce_value, FALSE,
1160 &yy_ei.use_last_ce_value
1165 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1166 &xx_ei.push_delay_fixed, 8,
1167 &yy_ei.push_delay_fixed
1171 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1172 &xx_ei.push_delay_random, 8,
1173 &yy_ei.push_delay_random
1177 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1178 &xx_ei.drop_delay_fixed, 0,
1179 &yy_ei.drop_delay_fixed
1183 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1184 &xx_ei.drop_delay_random, 0,
1185 &yy_ei.drop_delay_random
1189 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1190 &xx_ei.move_delay_fixed, 0,
1191 &yy_ei.move_delay_fixed
1195 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1196 &xx_ei.move_delay_random, 0,
1197 &yy_ei.move_delay_random
1201 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1202 &xx_ei.step_delay_fixed, 0,
1203 &yy_ei.step_delay_fixed
1207 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1208 &xx_ei.step_delay_random, 0,
1209 &yy_ei.step_delay_random
1214 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1215 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1220 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1221 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1222 &yy_ei.move_direction_initial
1226 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1227 &xx_ei.move_stepsize, TILEX / 8,
1228 &yy_ei.move_stepsize
1233 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1234 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1235 &yy_ei.move_enter_element
1239 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1240 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1241 &yy_ei.move_leave_element
1245 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1246 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1247 &yy_ei.move_leave_type
1252 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1253 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1254 &yy_ei.slippery_type
1259 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1260 &xx_ei.explosion_type, EXPLODES_3X3,
1261 &yy_ei.explosion_type
1265 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1266 &xx_ei.explosion_delay, 16,
1267 &yy_ei.explosion_delay
1271 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1272 &xx_ei.ignition_delay, 8,
1273 &yy_ei.ignition_delay
1278 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1279 &xx_ei.content, EL_EMPTY_SPACE,
1281 &xx_num_contents, 1, 1
1284 // ---------- "num_change_pages" must be the last entry ---------------------
1287 -1, SAVE_CONF_ALWAYS,
1288 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1289 &xx_ei.num_change_pages, 1,
1290 &yy_ei.num_change_pages
1301 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1303 // ---------- "current_change_page" must be the first entry -----------------
1306 -1, SAVE_CONF_ALWAYS,
1307 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1308 &xx_current_change_page, -1
1311 // ---------- (the remaining entries can be in any order) -------------------
1315 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1316 &xx_change.can_change, FALSE
1321 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1322 &xx_event_bits[0], 0
1326 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1327 &xx_event_bits[1], 0
1332 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1333 &xx_change.trigger_player, CH_PLAYER_ANY
1337 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1338 &xx_change.trigger_side, CH_SIDE_ANY
1342 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1343 &xx_change.trigger_page, CH_PAGE_ANY
1348 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1349 &xx_change.target_element, EL_EMPTY_SPACE
1354 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1355 &xx_change.delay_fixed, 0
1359 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1360 &xx_change.delay_random, 0
1364 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1365 &xx_change.delay_frames, FRAMES_PER_SECOND
1370 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1371 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1377 &xx_change.explode, FALSE
1381 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1382 &xx_change.use_target_content, FALSE
1386 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1387 &xx_change.only_if_complete, FALSE
1391 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1392 &xx_change.use_random_replace, FALSE
1396 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1397 &xx_change.random_percentage, 100
1401 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1402 &xx_change.replace_when, CP_WHEN_EMPTY
1407 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1408 &xx_change.has_action, FALSE
1412 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1413 &xx_change.action_type, CA_NO_ACTION
1417 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1418 &xx_change.action_mode, CA_MODE_UNDEFINED
1422 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1423 &xx_change.action_arg, CA_ARG_UNDEFINED
1428 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1429 &xx_change.action_element, EL_EMPTY_SPACE
1434 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1435 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1436 &xx_num_contents, 1, 1
1446 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1450 TYPE_STRING, CONF_VALUE_BYTES(1),
1451 &xx_ei.description[0], -1, NULL,
1452 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1453 &xx_default_description[0]
1458 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1459 &xx_ei.use_gfx_element, FALSE
1463 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1464 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1469 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1470 &xx_group.choice_mode, ANIM_RANDOM
1475 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1476 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1477 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1487 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1491 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1492 &xx_ei.use_gfx_element, FALSE
1496 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1497 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1507 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1511 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1512 &li.block_snap_field, TRUE
1516 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1517 &li.continuous_snapping, TRUE
1521 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1522 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1526 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1527 &li.use_start_element[0], FALSE
1531 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1532 &li.start_element[0], EL_PLAYER_1
1536 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1537 &li.use_artwork_element[0], FALSE
1541 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1542 &li.artwork_element[0], EL_PLAYER_1
1546 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1547 &li.use_explosion_element[0], FALSE
1551 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1552 &li.explosion_element[0], EL_PLAYER_1
1567 filetype_id_list[] =
1569 { LEVEL_FILE_TYPE_RND, "RND" },
1570 { LEVEL_FILE_TYPE_BD, "BD" },
1571 { LEVEL_FILE_TYPE_EM, "EM" },
1572 { LEVEL_FILE_TYPE_SP, "SP" },
1573 { LEVEL_FILE_TYPE_DX, "DX" },
1574 { LEVEL_FILE_TYPE_SB, "SB" },
1575 { LEVEL_FILE_TYPE_DC, "DC" },
1576 { LEVEL_FILE_TYPE_MM, "MM" },
1577 { LEVEL_FILE_TYPE_MM, "DF" },
1582 // ============================================================================
1583 // level file functions
1584 // ============================================================================
1586 static boolean check_special_flags(char *flag)
1588 if (strEqual(options.special_flags, flag) ||
1589 strEqual(leveldir_current->special_flags, flag))
1595 static struct DateInfo getCurrentDate(void)
1597 time_t epoch_seconds = time(NULL);
1598 struct tm *now = localtime(&epoch_seconds);
1599 struct DateInfo date;
1601 date.year = now->tm_year + 1900;
1602 date.month = now->tm_mon + 1;
1603 date.day = now->tm_mday;
1605 date.src = DATE_SRC_CLOCK;
1610 static void resetEventFlags(struct ElementChangeInfo *change)
1614 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1615 change->has_event[i] = FALSE;
1618 static void resetEventBits(void)
1622 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1623 xx_event_bits[i] = 0;
1626 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1630 /* important: only change event flag if corresponding event bit is set
1631 (this is because all xx_event_bits[] values are loaded separately,
1632 and all xx_event_bits[] values are set back to zero before loading
1633 another value xx_event_bits[x] (each value representing 32 flags)) */
1635 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1636 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1637 change->has_event[i] = TRUE;
1640 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1644 /* in contrast to the above function setEventFlagsFromEventBits(), it
1645 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1646 depending on the corresponding change->has_event[i] values here, as
1647 all xx_event_bits[] values are reset in resetEventBits() before */
1649 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1650 if (change->has_event[i])
1651 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1654 static char *getDefaultElementDescription(struct ElementInfo *ei)
1656 static char description[MAX_ELEMENT_NAME_LEN + 1];
1657 char *default_description = (ei->custom_description != NULL ?
1658 ei->custom_description :
1659 ei->editor_description);
1662 // always start with reliable default values
1663 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1664 description[i] = '\0';
1666 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1667 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1669 return &description[0];
1672 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1674 char *default_description = getDefaultElementDescription(ei);
1677 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1678 ei->description[i] = default_description[i];
1681 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1685 for (i = 0; conf[i].data_type != -1; i++)
1687 int default_value = conf[i].default_value;
1688 int data_type = conf[i].data_type;
1689 int conf_type = conf[i].conf_type;
1690 int byte_mask = conf_type & CONF_MASK_BYTES;
1692 if (byte_mask == CONF_MASK_MULTI_BYTES)
1694 int default_num_entities = conf[i].default_num_entities;
1695 int max_num_entities = conf[i].max_num_entities;
1697 *(int *)(conf[i].num_entities) = default_num_entities;
1699 if (data_type == TYPE_STRING)
1701 char *default_string = conf[i].default_string;
1702 char *string = (char *)(conf[i].value);
1704 strncpy(string, default_string, max_num_entities);
1706 else if (data_type == TYPE_ELEMENT_LIST)
1708 int *element_array = (int *)(conf[i].value);
1711 for (j = 0; j < max_num_entities; j++)
1712 element_array[j] = default_value;
1714 else if (data_type == TYPE_CONTENT_LIST)
1716 struct Content *content = (struct Content *)(conf[i].value);
1719 for (c = 0; c < max_num_entities; c++)
1720 for (y = 0; y < 3; y++)
1721 for (x = 0; x < 3; x++)
1722 content[c].e[x][y] = default_value;
1725 else // constant size configuration data (1, 2 or 4 bytes)
1727 if (data_type == TYPE_BOOLEAN)
1728 *(boolean *)(conf[i].value) = default_value;
1730 *(int *) (conf[i].value) = default_value;
1735 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1739 for (i = 0; conf[i].data_type != -1; i++)
1741 int data_type = conf[i].data_type;
1742 int conf_type = conf[i].conf_type;
1743 int byte_mask = conf_type & CONF_MASK_BYTES;
1745 if (byte_mask == CONF_MASK_MULTI_BYTES)
1747 int max_num_entities = conf[i].max_num_entities;
1749 if (data_type == TYPE_STRING)
1751 char *string = (char *)(conf[i].value);
1752 char *string_copy = (char *)(conf[i].value_copy);
1754 strncpy(string_copy, string, max_num_entities);
1756 else if (data_type == TYPE_ELEMENT_LIST)
1758 int *element_array = (int *)(conf[i].value);
1759 int *element_array_copy = (int *)(conf[i].value_copy);
1762 for (j = 0; j < max_num_entities; j++)
1763 element_array_copy[j] = element_array[j];
1765 else if (data_type == TYPE_CONTENT_LIST)
1767 struct Content *content = (struct Content *)(conf[i].value);
1768 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1771 for (c = 0; c < max_num_entities; c++)
1772 for (y = 0; y < 3; y++)
1773 for (x = 0; x < 3; x++)
1774 content_copy[c].e[x][y] = content[c].e[x][y];
1777 else // constant size configuration data (1, 2 or 4 bytes)
1779 if (data_type == TYPE_BOOLEAN)
1780 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1782 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1787 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1791 xx_ei = *ei_from; // copy element data into temporary buffer
1792 yy_ei = *ei_to; // copy element data into temporary buffer
1794 copyConfigFromConfigList(chunk_config_CUSX_base);
1799 // ---------- reinitialize and copy change pages ----------
1801 ei_to->num_change_pages = ei_from->num_change_pages;
1802 ei_to->current_change_page = ei_from->current_change_page;
1804 setElementChangePages(ei_to, ei_to->num_change_pages);
1806 for (i = 0; i < ei_to->num_change_pages; i++)
1807 ei_to->change_page[i] = ei_from->change_page[i];
1809 // ---------- copy group element info ----------
1810 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1811 *ei_to->group = *ei_from->group;
1813 // mark this custom element as modified
1814 ei_to->modified_settings = TRUE;
1817 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1819 int change_page_size = sizeof(struct ElementChangeInfo);
1821 ei->num_change_pages = MAX(1, change_pages);
1824 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1826 if (ei->current_change_page >= ei->num_change_pages)
1827 ei->current_change_page = ei->num_change_pages - 1;
1829 ei->change = &ei->change_page[ei->current_change_page];
1832 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1834 xx_change = *change; // copy change data into temporary buffer
1836 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1838 *change = xx_change;
1840 resetEventFlags(change);
1842 change->direct_action = 0;
1843 change->other_action = 0;
1845 change->pre_change_function = NULL;
1846 change->change_function = NULL;
1847 change->post_change_function = NULL;
1850 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1854 li = *level; // copy level data into temporary buffer
1855 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1856 *level = li; // copy temporary buffer back to level data
1858 setLevelInfoToDefaults_BD();
1859 setLevelInfoToDefaults_EM();
1860 setLevelInfoToDefaults_SP();
1861 setLevelInfoToDefaults_MM();
1863 level->native_bd_level = &native_bd_level;
1864 level->native_em_level = &native_em_level;
1865 level->native_sp_level = &native_sp_level;
1866 level->native_mm_level = &native_mm_level;
1868 level->file_version = FILE_VERSION_ACTUAL;
1869 level->game_version = GAME_VERSION_ACTUAL;
1871 level->creation_date = getCurrentDate();
1873 level->encoding_16bit_field = TRUE;
1874 level->encoding_16bit_yamyam = TRUE;
1875 level->encoding_16bit_amoeba = TRUE;
1877 // clear level name and level author string buffers
1878 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1879 level->name[i] = '\0';
1880 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1881 level->author[i] = '\0';
1883 // set level name and level author to default values
1884 strcpy(level->name, NAMELESS_LEVEL_NAME);
1885 strcpy(level->author, ANONYMOUS_NAME);
1887 // set level playfield to playable default level with player and exit
1888 for (x = 0; x < MAX_LEV_FIELDX; x++)
1889 for (y = 0; y < MAX_LEV_FIELDY; y++)
1890 level->field[x][y] = EL_SAND;
1892 level->field[0][0] = EL_PLAYER_1;
1893 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1895 BorderElement = EL_STEELWALL;
1897 // detect custom elements when loading them
1898 level->file_has_custom_elements = FALSE;
1900 // set all bug compatibility flags to "false" => do not emulate this bug
1901 level->use_action_after_change_bug = FALSE;
1903 if (leveldir_current)
1905 // try to determine better author name than 'anonymous'
1906 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1908 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1909 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1913 switch (LEVELCLASS(leveldir_current))
1915 case LEVELCLASS_TUTORIAL:
1916 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1919 case LEVELCLASS_CONTRIB:
1920 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1921 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1924 case LEVELCLASS_PRIVATE:
1925 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1926 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1930 // keep default value
1937 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1939 static boolean clipboard_elements_initialized = FALSE;
1942 InitElementPropertiesStatic();
1944 li = *level; // copy level data into temporary buffer
1945 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1946 *level = li; // copy temporary buffer back to level data
1948 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1951 struct ElementInfo *ei = &element_info[element];
1953 if (element == EL_MM_GRAY_BALL)
1955 struct LevelInfo_MM *level_mm = level->native_mm_level;
1958 for (j = 0; j < level->num_mm_ball_contents; j++)
1959 level->mm_ball_content[j] =
1960 map_element_MM_to_RND(level_mm->ball_content[j]);
1963 // never initialize clipboard elements after the very first time
1964 // (to be able to use clipboard elements between several levels)
1965 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1968 if (IS_ENVELOPE(element))
1970 int envelope_nr = element - EL_ENVELOPE_1;
1972 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1974 level->envelope[envelope_nr] = xx_envelope;
1977 if (IS_CUSTOM_ELEMENT(element) ||
1978 IS_GROUP_ELEMENT(element) ||
1979 IS_INTERNAL_ELEMENT(element))
1981 xx_ei = *ei; // copy element data into temporary buffer
1983 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1988 setElementChangePages(ei, 1);
1989 setElementChangeInfoToDefaults(ei->change);
1991 if (IS_CUSTOM_ELEMENT(element) ||
1992 IS_GROUP_ELEMENT(element))
1994 setElementDescriptionToDefault(ei);
1996 ei->modified_settings = FALSE;
1999 if (IS_CUSTOM_ELEMENT(element) ||
2000 IS_INTERNAL_ELEMENT(element))
2002 // internal values used in level editor
2004 ei->access_type = 0;
2005 ei->access_layer = 0;
2006 ei->access_protected = 0;
2007 ei->walk_to_action = 0;
2008 ei->smash_targets = 0;
2011 ei->can_explode_by_fire = FALSE;
2012 ei->can_explode_smashed = FALSE;
2013 ei->can_explode_impact = FALSE;
2015 ei->current_change_page = 0;
2018 if (IS_GROUP_ELEMENT(element) ||
2019 IS_INTERNAL_ELEMENT(element))
2021 struct ElementGroupInfo *group;
2023 // initialize memory for list of elements in group
2024 if (ei->group == NULL)
2025 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2029 xx_group = *group; // copy group data into temporary buffer
2031 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2036 if (IS_EMPTY_ELEMENT(element) ||
2037 IS_INTERNAL_ELEMENT(element))
2039 xx_ei = *ei; // copy element data into temporary buffer
2041 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2047 clipboard_elements_initialized = TRUE;
2050 static void setLevelInfoToDefaults(struct LevelInfo *level,
2051 boolean level_info_only,
2052 boolean reset_file_status)
2054 setLevelInfoToDefaults_Level(level);
2056 if (!level_info_only)
2057 setLevelInfoToDefaults_Elements(level);
2059 if (reset_file_status)
2061 level->no_valid_file = FALSE;
2062 level->no_level_file = FALSE;
2065 level->changed = FALSE;
2068 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2070 level_file_info->nr = 0;
2071 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2072 level_file_info->packed = FALSE;
2074 setString(&level_file_info->basename, NULL);
2075 setString(&level_file_info->filename, NULL);
2078 int getMappedElement_SB(int, boolean);
2080 static void ActivateLevelTemplate(void)
2084 if (check_special_flags("load_xsb_to_ces"))
2086 // fill smaller playfields with padding "beyond border wall" elements
2087 if (level.fieldx < level_template.fieldx ||
2088 level.fieldy < level_template.fieldy)
2090 short field[level.fieldx][level.fieldy];
2091 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2092 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2093 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2094 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2096 // copy old playfield (which is smaller than the visible area)
2097 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2098 field[x][y] = level.field[x][y];
2100 // fill new, larger playfield with "beyond border wall" elements
2101 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2102 level.field[x][y] = getMappedElement_SB('_', TRUE);
2104 // copy the old playfield to the middle of the new playfield
2105 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2106 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2108 level.fieldx = new_fieldx;
2109 level.fieldy = new_fieldy;
2113 // Currently there is no special action needed to activate the template
2114 // data, because 'element_info' property settings overwrite the original
2115 // level data, while all other variables do not change.
2117 // Exception: 'from_level_template' elements in the original level playfield
2118 // are overwritten with the corresponding elements at the same position in
2119 // playfield from the level template.
2121 for (x = 0; x < level.fieldx; x++)
2122 for (y = 0; y < level.fieldy; y++)
2123 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2124 level.field[x][y] = level_template.field[x][y];
2126 if (check_special_flags("load_xsb_to_ces"))
2128 struct LevelInfo level_backup = level;
2130 // overwrite all individual level settings from template level settings
2131 level = level_template;
2133 // restore level file info
2134 level.file_info = level_backup.file_info;
2136 // restore playfield size
2137 level.fieldx = level_backup.fieldx;
2138 level.fieldy = level_backup.fieldy;
2140 // restore playfield content
2141 for (x = 0; x < level.fieldx; x++)
2142 for (y = 0; y < level.fieldy; y++)
2143 level.field[x][y] = level_backup.field[x][y];
2145 // restore name and author from individual level
2146 strcpy(level.name, level_backup.name);
2147 strcpy(level.author, level_backup.author);
2149 // restore flag "use_custom_template"
2150 level.use_custom_template = level_backup.use_custom_template;
2154 static boolean checkForPackageFromBasename_BD(char *basename)
2156 // check for native BD level file extensions
2157 if (!strSuffixLower(basename, ".bd") &&
2158 !strSuffixLower(basename, ".bdr") &&
2159 !strSuffixLower(basename, ".brc") &&
2160 !strSuffixLower(basename, ".gds"))
2163 // check for standard single-level BD files (like "001.bd")
2164 if (strSuffixLower(basename, ".bd") &&
2165 strlen(basename) == 6 &&
2166 basename[0] >= '0' && basename[0] <= '9' &&
2167 basename[1] >= '0' && basename[1] <= '9' &&
2168 basename[2] >= '0' && basename[2] <= '9')
2171 // this is a level package in native BD file format
2175 static char *getLevelFilenameFromBasename(char *basename)
2177 static char *filename = NULL;
2179 checked_free(filename);
2181 filename = getPath2(getCurrentLevelDir(), basename);
2186 static int getFileTypeFromBasename(char *basename)
2188 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2190 static char *filename = NULL;
2191 struct stat file_status;
2193 // ---------- try to determine file type from filename ----------
2195 // check for typical filename of a Supaplex level package file
2196 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2197 return LEVEL_FILE_TYPE_SP;
2199 // check for typical filename of a Diamond Caves II level package file
2200 if (strSuffixLower(basename, ".dc") ||
2201 strSuffixLower(basename, ".dc2"))
2202 return LEVEL_FILE_TYPE_DC;
2204 // check for typical filename of a Sokoban level package file
2205 if (strSuffixLower(basename, ".xsb") &&
2206 strchr(basename, '%') == NULL)
2207 return LEVEL_FILE_TYPE_SB;
2209 // check for typical filename of a Boulder Dash (GDash) level package file
2210 if (checkForPackageFromBasename_BD(basename))
2211 return LEVEL_FILE_TYPE_BD;
2213 // ---------- try to determine file type from filesize ----------
2215 checked_free(filename);
2216 filename = getPath2(getCurrentLevelDir(), basename);
2218 if (stat(filename, &file_status) == 0)
2220 // check for typical filesize of a Supaplex level package file
2221 if (file_status.st_size == 170496)
2222 return LEVEL_FILE_TYPE_SP;
2225 return LEVEL_FILE_TYPE_UNKNOWN;
2228 static int getFileTypeFromMagicBytes(char *filename, int type)
2232 if ((file = openFile(filename, MODE_READ)))
2234 char chunk_name[CHUNK_ID_LEN + 1];
2236 getFileChunkBE(file, chunk_name, NULL);
2238 if (strEqual(chunk_name, "MMII") ||
2239 strEqual(chunk_name, "MIRR"))
2240 type = LEVEL_FILE_TYPE_MM;
2248 static boolean checkForPackageFromBasename(char *basename)
2250 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2251 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2253 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2256 static char *getSingleLevelBasenameExt(int nr, char *extension)
2258 static char basename[MAX_FILENAME_LEN];
2261 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2263 sprintf(basename, "%03d.%s", nr, extension);
2268 static char *getSingleLevelBasename(int nr)
2270 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2273 static char *getPackedLevelBasename(int type)
2275 static char basename[MAX_FILENAME_LEN];
2276 char *directory = getCurrentLevelDir();
2278 DirectoryEntry *dir_entry;
2280 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2282 if ((dir = openDirectory(directory)) == NULL)
2284 Warn("cannot read current level directory '%s'", directory);
2289 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2291 char *entry_basename = dir_entry->basename;
2292 int entry_type = getFileTypeFromBasename(entry_basename);
2294 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2296 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2299 strcpy(basename, entry_basename);
2306 closeDirectory(dir);
2311 static char *getSingleLevelFilename(int nr)
2313 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2316 #if ENABLE_UNUSED_CODE
2317 static char *getPackedLevelFilename(int type)
2319 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2323 char *getDefaultLevelFilename(int nr)
2325 return getSingleLevelFilename(nr);
2328 #if ENABLE_UNUSED_CODE
2329 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2333 lfi->packed = FALSE;
2335 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2336 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2340 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2341 int type, char *format, ...)
2343 static char basename[MAX_FILENAME_LEN];
2346 va_start(ap, format);
2347 vsprintf(basename, format, ap);
2351 lfi->packed = FALSE;
2353 setString(&lfi->basename, basename);
2354 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2357 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2363 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2364 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2367 static int getFiletypeFromID(char *filetype_id)
2369 char *filetype_id_lower;
2370 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2373 if (filetype_id == NULL)
2374 return LEVEL_FILE_TYPE_UNKNOWN;
2376 filetype_id_lower = getStringToLower(filetype_id);
2378 for (i = 0; filetype_id_list[i].id != NULL; i++)
2380 char *id_lower = getStringToLower(filetype_id_list[i].id);
2382 if (strEqual(filetype_id_lower, id_lower))
2383 filetype = filetype_id_list[i].filetype;
2387 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2391 free(filetype_id_lower);
2396 char *getLocalLevelTemplateFilename(void)
2398 return getDefaultLevelFilename(-1);
2401 char *getGlobalLevelTemplateFilename(void)
2403 // global variable "leveldir_current" must be modified in the loop below
2404 LevelDirTree *leveldir_current_last = leveldir_current;
2405 char *filename = NULL;
2407 // check for template level in path from current to topmost tree node
2409 while (leveldir_current != NULL)
2411 filename = getDefaultLevelFilename(-1);
2413 if (fileExists(filename))
2416 leveldir_current = leveldir_current->node_parent;
2419 // restore global variable "leveldir_current" modified in above loop
2420 leveldir_current = leveldir_current_last;
2425 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2429 // special case: level number is negative => check for level template file
2432 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2433 getSingleLevelBasename(-1));
2435 // replace local level template filename with global template filename
2436 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2438 // no fallback if template file not existing
2442 // special case: check for file name/pattern specified in "levelinfo.conf"
2443 if (leveldir_current->level_filename != NULL)
2445 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2447 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2448 leveldir_current->level_filename, nr);
2450 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2452 if (fileExists(lfi->filename))
2455 else if (leveldir_current->level_filetype != NULL)
2457 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2459 // check for specified native level file with standard file name
2460 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2461 "%03d.%s", nr, LEVELFILE_EXTENSION);
2462 if (fileExists(lfi->filename))
2466 // check for native Rocks'n'Diamonds level file
2467 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2468 "%03d.%s", nr, LEVELFILE_EXTENSION);
2469 if (fileExists(lfi->filename))
2472 // check for native Boulder Dash level file
2473 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2474 if (fileExists(lfi->filename))
2477 // check for Emerald Mine level file (V1)
2478 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2479 'a' + (nr / 10) % 26, '0' + nr % 10);
2480 if (fileExists(lfi->filename))
2482 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2483 'A' + (nr / 10) % 26, '0' + nr % 10);
2484 if (fileExists(lfi->filename))
2487 // check for Emerald Mine level file (V2 to V5)
2488 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2489 if (fileExists(lfi->filename))
2492 // check for Emerald Mine level file (V6 / single mode)
2493 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2494 if (fileExists(lfi->filename))
2496 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2497 if (fileExists(lfi->filename))
2500 // check for Emerald Mine level file (V6 / teamwork mode)
2501 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2502 if (fileExists(lfi->filename))
2504 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2505 if (fileExists(lfi->filename))
2508 // check for various packed level file formats
2509 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2510 if (fileExists(lfi->filename))
2513 // no known level file found -- use default values (and fail later)
2514 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2515 "%03d.%s", nr, LEVELFILE_EXTENSION);
2518 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2520 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2521 lfi->type = getFileTypeFromBasename(lfi->basename);
2523 if (lfi->type == LEVEL_FILE_TYPE_RND)
2524 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2527 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2529 // always start with reliable default values
2530 setFileInfoToDefaults(level_file_info);
2532 level_file_info->nr = nr; // set requested level number
2534 determineLevelFileInfo_Filename(level_file_info);
2535 determineLevelFileInfo_Filetype(level_file_info);
2538 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2539 struct LevelFileInfo *lfi_to)
2541 lfi_to->nr = lfi_from->nr;
2542 lfi_to->type = lfi_from->type;
2543 lfi_to->packed = lfi_from->packed;
2545 setString(&lfi_to->basename, lfi_from->basename);
2546 setString(&lfi_to->filename, lfi_from->filename);
2549 // ----------------------------------------------------------------------------
2550 // functions for loading R'n'D level
2551 // ----------------------------------------------------------------------------
2553 int getMappedElement(int element)
2555 // remap some (historic, now obsolete) elements
2559 case EL_PLAYER_OBSOLETE:
2560 element = EL_PLAYER_1;
2563 case EL_KEY_OBSOLETE:
2567 case EL_EM_KEY_1_FILE_OBSOLETE:
2568 element = EL_EM_KEY_1;
2571 case EL_EM_KEY_2_FILE_OBSOLETE:
2572 element = EL_EM_KEY_2;
2575 case EL_EM_KEY_3_FILE_OBSOLETE:
2576 element = EL_EM_KEY_3;
2579 case EL_EM_KEY_4_FILE_OBSOLETE:
2580 element = EL_EM_KEY_4;
2583 case EL_ENVELOPE_OBSOLETE:
2584 element = EL_ENVELOPE_1;
2592 if (element >= NUM_FILE_ELEMENTS)
2594 Warn("invalid level element %d", element);
2596 element = EL_UNKNOWN;
2604 static int getMappedElementByVersion(int element, int game_version)
2606 // remap some elements due to certain game version
2608 if (game_version <= VERSION_IDENT(2,2,0,0))
2610 // map game font elements
2611 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2612 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2613 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2614 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2617 if (game_version < VERSION_IDENT(3,0,0,0))
2619 // map Supaplex gravity tube elements
2620 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2621 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2622 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2623 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2630 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2632 level->file_version = getFileVersion(file);
2633 level->game_version = getFileVersion(file);
2638 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2640 level->creation_date.year = getFile16BitBE(file);
2641 level->creation_date.month = getFile8Bit(file);
2642 level->creation_date.day = getFile8Bit(file);
2644 level->creation_date.src = DATE_SRC_LEVELFILE;
2649 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2651 int initial_player_stepsize;
2652 int initial_player_gravity;
2655 level->fieldx = getFile8Bit(file);
2656 level->fieldy = getFile8Bit(file);
2658 level->time = getFile16BitBE(file);
2659 level->gems_needed = getFile16BitBE(file);
2661 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2662 level->name[i] = getFile8Bit(file);
2663 level->name[MAX_LEVEL_NAME_LEN] = 0;
2665 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2666 level->score[i] = getFile8Bit(file);
2668 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2669 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2670 for (y = 0; y < 3; y++)
2671 for (x = 0; x < 3; x++)
2672 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2674 level->amoeba_speed = getFile8Bit(file);
2675 level->time_magic_wall = getFile8Bit(file);
2676 level->time_wheel = getFile8Bit(file);
2677 level->amoeba_content = getMappedElement(getFile8Bit(file));
2679 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2682 for (i = 0; i < MAX_PLAYERS; i++)
2683 level->initial_player_stepsize[i] = initial_player_stepsize;
2685 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2687 for (i = 0; i < MAX_PLAYERS; i++)
2688 level->initial_player_gravity[i] = initial_player_gravity;
2690 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2691 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2693 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2695 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2696 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2697 level->can_move_into_acid_bits = getFile32BitBE(file);
2698 level->dont_collide_with_bits = getFile8Bit(file);
2700 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2701 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2703 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2704 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2705 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2707 level->game_engine_type = getFile8Bit(file);
2709 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2714 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2718 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2719 level->name[i] = getFile8Bit(file);
2720 level->name[MAX_LEVEL_NAME_LEN] = 0;
2725 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2729 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2730 level->author[i] = getFile8Bit(file);
2731 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2736 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2739 int chunk_size_expected = level->fieldx * level->fieldy;
2741 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2742 stored with 16-bit encoding (and should be twice as big then).
2743 Even worse, playfield data was stored 16-bit when only yamyam content
2744 contained 16-bit elements and vice versa. */
2746 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2747 chunk_size_expected *= 2;
2749 if (chunk_size_expected != chunk_size)
2751 ReadUnusedBytesFromFile(file, chunk_size);
2752 return chunk_size_expected;
2755 for (y = 0; y < level->fieldy; y++)
2756 for (x = 0; x < level->fieldx; x++)
2757 level->field[x][y] =
2758 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2763 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2766 int header_size = 4;
2767 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2768 int chunk_size_expected = header_size + content_size;
2770 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2771 stored with 16-bit encoding (and should be twice as big then).
2772 Even worse, playfield data was stored 16-bit when only yamyam content
2773 contained 16-bit elements and vice versa. */
2775 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2776 chunk_size_expected += content_size;
2778 if (chunk_size_expected != chunk_size)
2780 ReadUnusedBytesFromFile(file, chunk_size);
2781 return chunk_size_expected;
2785 level->num_yamyam_contents = getFile8Bit(file);
2789 // correct invalid number of content fields -- should never happen
2790 if (level->num_yamyam_contents < 1 ||
2791 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2792 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2794 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2795 for (y = 0; y < 3; y++)
2796 for (x = 0; x < 3; x++)
2797 level->yamyam_content[i].e[x][y] =
2798 getMappedElement(level->encoding_16bit_field ?
2799 getFile16BitBE(file) : getFile8Bit(file));
2803 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2808 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2810 element = getMappedElement(getFile16BitBE(file));
2811 num_contents = getFile8Bit(file);
2813 getFile8Bit(file); // content x size (unused)
2814 getFile8Bit(file); // content y size (unused)
2816 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2818 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2819 for (y = 0; y < 3; y++)
2820 for (x = 0; x < 3; x++)
2821 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2823 // correct invalid number of content fields -- should never happen
2824 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2825 num_contents = STD_ELEMENT_CONTENTS;
2827 if (element == EL_YAMYAM)
2829 level->num_yamyam_contents = num_contents;
2831 for (i = 0; i < num_contents; i++)
2832 for (y = 0; y < 3; y++)
2833 for (x = 0; x < 3; x++)
2834 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2836 else if (element == EL_BD_AMOEBA)
2838 level->amoeba_content = content_array[0][0][0];
2842 Warn("cannot load content for element '%d'", element);
2848 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2854 int chunk_size_expected;
2856 element = getMappedElement(getFile16BitBE(file));
2857 if (!IS_ENVELOPE(element))
2858 element = EL_ENVELOPE_1;
2860 envelope_nr = element - EL_ENVELOPE_1;
2862 envelope_len = getFile16BitBE(file);
2864 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2865 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2867 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2869 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2870 if (chunk_size_expected != chunk_size)
2872 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2873 return chunk_size_expected;
2876 for (i = 0; i < envelope_len; i++)
2877 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2882 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2884 int num_changed_custom_elements = getFile16BitBE(file);
2885 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2888 if (chunk_size_expected != chunk_size)
2890 ReadUnusedBytesFromFile(file, chunk_size - 2);
2891 return chunk_size_expected;
2894 for (i = 0; i < num_changed_custom_elements; i++)
2896 int element = getMappedElement(getFile16BitBE(file));
2897 int properties = getFile32BitBE(file);
2899 if (IS_CUSTOM_ELEMENT(element))
2900 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2902 Warn("invalid custom element number %d", element);
2904 // older game versions that wrote level files with CUS1 chunks used
2905 // different default push delay values (not yet stored in level file)
2906 element_info[element].push_delay_fixed = 2;
2907 element_info[element].push_delay_random = 8;
2910 level->file_has_custom_elements = TRUE;
2915 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2917 int num_changed_custom_elements = getFile16BitBE(file);
2918 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2921 if (chunk_size_expected != chunk_size)
2923 ReadUnusedBytesFromFile(file, chunk_size - 2);
2924 return chunk_size_expected;
2927 for (i = 0; i < num_changed_custom_elements; i++)
2929 int element = getMappedElement(getFile16BitBE(file));
2930 int custom_target_element = getMappedElement(getFile16BitBE(file));
2932 if (IS_CUSTOM_ELEMENT(element))
2933 element_info[element].change->target_element = custom_target_element;
2935 Warn("invalid custom element number %d", element);
2938 level->file_has_custom_elements = TRUE;
2943 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2945 int num_changed_custom_elements = getFile16BitBE(file);
2946 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2949 if (chunk_size_expected != chunk_size)
2951 ReadUnusedBytesFromFile(file, chunk_size - 2);
2952 return chunk_size_expected;
2955 for (i = 0; i < num_changed_custom_elements; i++)
2957 int element = getMappedElement(getFile16BitBE(file));
2958 struct ElementInfo *ei = &element_info[element];
2959 unsigned int event_bits;
2961 if (!IS_CUSTOM_ELEMENT(element))
2963 Warn("invalid custom element number %d", element);
2965 element = EL_INTERNAL_DUMMY;
2968 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2969 ei->description[j] = getFile8Bit(file);
2970 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2972 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2974 // some free bytes for future properties and padding
2975 ReadUnusedBytesFromFile(file, 7);
2977 ei->use_gfx_element = getFile8Bit(file);
2978 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2980 ei->collect_score_initial = getFile8Bit(file);
2981 ei->collect_count_initial = getFile8Bit(file);
2983 ei->push_delay_fixed = getFile16BitBE(file);
2984 ei->push_delay_random = getFile16BitBE(file);
2985 ei->move_delay_fixed = getFile16BitBE(file);
2986 ei->move_delay_random = getFile16BitBE(file);
2988 ei->move_pattern = getFile16BitBE(file);
2989 ei->move_direction_initial = getFile8Bit(file);
2990 ei->move_stepsize = getFile8Bit(file);
2992 for (y = 0; y < 3; y++)
2993 for (x = 0; x < 3; x++)
2994 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2996 // bits 0 - 31 of "has_event[]"
2997 event_bits = getFile32BitBE(file);
2998 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2999 if (event_bits & (1u << j))
3000 ei->change->has_event[j] = TRUE;
3002 ei->change->target_element = getMappedElement(getFile16BitBE(file));
3004 ei->change->delay_fixed = getFile16BitBE(file);
3005 ei->change->delay_random = getFile16BitBE(file);
3006 ei->change->delay_frames = getFile16BitBE(file);
3008 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3010 ei->change->explode = getFile8Bit(file);
3011 ei->change->use_target_content = getFile8Bit(file);
3012 ei->change->only_if_complete = getFile8Bit(file);
3013 ei->change->use_random_replace = getFile8Bit(file);
3015 ei->change->random_percentage = getFile8Bit(file);
3016 ei->change->replace_when = getFile8Bit(file);
3018 for (y = 0; y < 3; y++)
3019 for (x = 0; x < 3; x++)
3020 ei->change->target_content.e[x][y] =
3021 getMappedElement(getFile16BitBE(file));
3023 ei->slippery_type = getFile8Bit(file);
3025 // some free bytes for future properties and padding
3026 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3028 // mark that this custom element has been modified
3029 ei->modified_settings = TRUE;
3032 level->file_has_custom_elements = TRUE;
3037 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3039 struct ElementInfo *ei;
3040 int chunk_size_expected;
3044 // ---------- custom element base property values (96 bytes) ----------------
3046 element = getMappedElement(getFile16BitBE(file));
3048 if (!IS_CUSTOM_ELEMENT(element))
3050 Warn("invalid custom element number %d", element);
3052 ReadUnusedBytesFromFile(file, chunk_size - 2);
3057 ei = &element_info[element];
3059 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3060 ei->description[i] = getFile8Bit(file);
3061 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3063 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3065 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3067 ei->num_change_pages = getFile8Bit(file);
3069 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3070 if (chunk_size_expected != chunk_size)
3072 ReadUnusedBytesFromFile(file, chunk_size - 43);
3073 return chunk_size_expected;
3076 ei->ce_value_fixed_initial = getFile16BitBE(file);
3077 ei->ce_value_random_initial = getFile16BitBE(file);
3078 ei->use_last_ce_value = getFile8Bit(file);
3080 ei->use_gfx_element = getFile8Bit(file);
3081 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3083 ei->collect_score_initial = getFile8Bit(file);
3084 ei->collect_count_initial = getFile8Bit(file);
3086 ei->drop_delay_fixed = getFile8Bit(file);
3087 ei->push_delay_fixed = getFile8Bit(file);
3088 ei->drop_delay_random = getFile8Bit(file);
3089 ei->push_delay_random = getFile8Bit(file);
3090 ei->move_delay_fixed = getFile16BitBE(file);
3091 ei->move_delay_random = getFile16BitBE(file);
3093 // bits 0 - 15 of "move_pattern" ...
3094 ei->move_pattern = getFile16BitBE(file);
3095 ei->move_direction_initial = getFile8Bit(file);
3096 ei->move_stepsize = getFile8Bit(file);
3098 ei->slippery_type = getFile8Bit(file);
3100 for (y = 0; y < 3; y++)
3101 for (x = 0; x < 3; x++)
3102 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3104 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3105 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3106 ei->move_leave_type = getFile8Bit(file);
3108 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3109 ei->move_pattern |= (getFile16BitBE(file) << 16);
3111 ei->access_direction = getFile8Bit(file);
3113 ei->explosion_delay = getFile8Bit(file);
3114 ei->ignition_delay = getFile8Bit(file);
3115 ei->explosion_type = getFile8Bit(file);
3117 // some free bytes for future custom property values and padding
3118 ReadUnusedBytesFromFile(file, 1);
3120 // ---------- change page property values (48 bytes) ------------------------
3122 setElementChangePages(ei, ei->num_change_pages);
3124 for (i = 0; i < ei->num_change_pages; i++)
3126 struct ElementChangeInfo *change = &ei->change_page[i];
3127 unsigned int event_bits;
3129 // always start with reliable default values
3130 setElementChangeInfoToDefaults(change);
3132 // bits 0 - 31 of "has_event[]" ...
3133 event_bits = getFile32BitBE(file);
3134 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3135 if (event_bits & (1u << j))
3136 change->has_event[j] = TRUE;
3138 change->target_element = getMappedElement(getFile16BitBE(file));
3140 change->delay_fixed = getFile16BitBE(file);
3141 change->delay_random = getFile16BitBE(file);
3142 change->delay_frames = getFile16BitBE(file);
3144 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3146 change->explode = getFile8Bit(file);
3147 change->use_target_content = getFile8Bit(file);
3148 change->only_if_complete = getFile8Bit(file);
3149 change->use_random_replace = getFile8Bit(file);
3151 change->random_percentage = getFile8Bit(file);
3152 change->replace_when = getFile8Bit(file);
3154 for (y = 0; y < 3; y++)
3155 for (x = 0; x < 3; x++)
3156 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3158 change->can_change = getFile8Bit(file);
3160 change->trigger_side = getFile8Bit(file);
3162 change->trigger_player = getFile8Bit(file);
3163 change->trigger_page = getFile8Bit(file);
3165 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3166 CH_PAGE_ANY : (1 << change->trigger_page));
3168 change->has_action = getFile8Bit(file);
3169 change->action_type = getFile8Bit(file);
3170 change->action_mode = getFile8Bit(file);
3171 change->action_arg = getFile16BitBE(file);
3173 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3174 event_bits = getFile8Bit(file);
3175 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3176 if (event_bits & (1u << (j - 32)))
3177 change->has_event[j] = TRUE;
3180 // mark this custom element as modified
3181 ei->modified_settings = TRUE;
3183 level->file_has_custom_elements = TRUE;
3188 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3190 struct ElementInfo *ei;
3191 struct ElementGroupInfo *group;
3195 element = getMappedElement(getFile16BitBE(file));
3197 if (!IS_GROUP_ELEMENT(element))
3199 Warn("invalid group element number %d", element);
3201 ReadUnusedBytesFromFile(file, chunk_size - 2);
3206 ei = &element_info[element];
3208 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3209 ei->description[i] = getFile8Bit(file);
3210 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3212 group = element_info[element].group;
3214 group->num_elements = getFile8Bit(file);
3216 ei->use_gfx_element = getFile8Bit(file);
3217 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3219 group->choice_mode = getFile8Bit(file);
3221 // some free bytes for future values and padding
3222 ReadUnusedBytesFromFile(file, 3);
3224 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3225 group->element[i] = getMappedElement(getFile16BitBE(file));
3227 // mark this group element as modified
3228 element_info[element].modified_settings = TRUE;
3230 level->file_has_custom_elements = TRUE;
3235 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3236 int element, int real_element)
3238 int micro_chunk_size = 0;
3239 int conf_type = getFile8Bit(file);
3240 int byte_mask = conf_type & CONF_MASK_BYTES;
3241 boolean element_found = FALSE;
3244 micro_chunk_size += 1;
3246 if (byte_mask == CONF_MASK_MULTI_BYTES)
3248 int num_bytes = getFile16BitBE(file);
3249 byte *buffer = checked_malloc(num_bytes);
3251 ReadBytesFromFile(file, buffer, num_bytes);
3253 for (i = 0; conf[i].data_type != -1; i++)
3255 if (conf[i].element == element &&
3256 conf[i].conf_type == conf_type)
3258 int data_type = conf[i].data_type;
3259 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3260 int max_num_entities = conf[i].max_num_entities;
3262 if (num_entities > max_num_entities)
3264 Warn("truncating number of entities for element %d from %d to %d",
3265 element, num_entities, max_num_entities);
3267 num_entities = max_num_entities;
3270 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3271 data_type == TYPE_CONTENT_LIST))
3273 // for element and content lists, zero entities are not allowed
3274 Warn("found empty list of entities for element %d", element);
3276 // do not set "num_entities" here to prevent reading behind buffer
3278 *(int *)(conf[i].num_entities) = 1; // at least one is required
3282 *(int *)(conf[i].num_entities) = num_entities;
3285 element_found = TRUE;
3287 if (data_type == TYPE_STRING)
3289 char *string = (char *)(conf[i].value);
3292 for (j = 0; j < max_num_entities; j++)
3293 string[j] = (j < num_entities ? buffer[j] : '\0');
3295 else if (data_type == TYPE_ELEMENT_LIST)
3297 int *element_array = (int *)(conf[i].value);
3300 for (j = 0; j < num_entities; j++)
3302 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3304 else if (data_type == TYPE_CONTENT_LIST)
3306 struct Content *content= (struct Content *)(conf[i].value);
3309 for (c = 0; c < num_entities; c++)
3310 for (y = 0; y < 3; y++)
3311 for (x = 0; x < 3; x++)
3312 content[c].e[x][y] =
3313 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3316 element_found = FALSE;
3322 checked_free(buffer);
3324 micro_chunk_size += 2 + num_bytes;
3326 else // constant size configuration data (1, 2 or 4 bytes)
3328 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3329 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3330 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3332 for (i = 0; conf[i].data_type != -1; i++)
3334 if (conf[i].element == element &&
3335 conf[i].conf_type == conf_type)
3337 int data_type = conf[i].data_type;
3339 if (data_type == TYPE_ELEMENT)
3340 value = getMappedElement(value);
3342 if (data_type == TYPE_BOOLEAN)
3343 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3345 *(int *) (conf[i].value) = value;
3347 element_found = TRUE;
3353 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3358 char *error_conf_chunk_bytes =
3359 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3360 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3361 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3362 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3363 int error_element = real_element;
3365 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3366 error_conf_chunk_bytes, error_conf_chunk_token,
3367 error_element, EL_NAME(error_element));
3370 return micro_chunk_size;
3373 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3375 int real_chunk_size = 0;
3377 li = *level; // copy level data into temporary buffer
3379 while (!checkEndOfFile(file))
3381 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3383 if (real_chunk_size >= chunk_size)
3387 *level = li; // copy temporary buffer back to level data
3389 return real_chunk_size;
3392 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3394 int real_chunk_size = 0;
3396 li = *level; // copy level data into temporary buffer
3398 while (!checkEndOfFile(file))
3400 int element = getMappedElement(getFile16BitBE(file));
3402 real_chunk_size += 2;
3403 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3405 if (real_chunk_size >= chunk_size)
3409 *level = li; // copy temporary buffer back to level data
3411 return real_chunk_size;
3414 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3416 int real_chunk_size = 0;
3418 li = *level; // copy level data into temporary buffer
3420 while (!checkEndOfFile(file))
3422 int element = getMappedElement(getFile16BitBE(file));
3424 real_chunk_size += 2;
3425 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3427 if (real_chunk_size >= chunk_size)
3431 *level = li; // copy temporary buffer back to level data
3433 return real_chunk_size;
3436 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3438 int element = getMappedElement(getFile16BitBE(file));
3439 int envelope_nr = element - EL_ENVELOPE_1;
3440 int real_chunk_size = 2;
3442 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3444 while (!checkEndOfFile(file))
3446 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3449 if (real_chunk_size >= chunk_size)
3453 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3455 return real_chunk_size;
3458 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3460 int element = getMappedElement(getFile16BitBE(file));
3461 int real_chunk_size = 2;
3462 struct ElementInfo *ei = &element_info[element];
3465 xx_ei = *ei; // copy element data into temporary buffer
3467 xx_ei.num_change_pages = -1;
3469 while (!checkEndOfFile(file))
3471 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3473 if (xx_ei.num_change_pages != -1)
3476 if (real_chunk_size >= chunk_size)
3482 if (ei->num_change_pages == -1)
3484 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3487 ei->num_change_pages = 1;
3489 setElementChangePages(ei, 1);
3490 setElementChangeInfoToDefaults(ei->change);
3492 return real_chunk_size;
3495 // initialize number of change pages stored for this custom element
3496 setElementChangePages(ei, ei->num_change_pages);
3497 for (i = 0; i < ei->num_change_pages; i++)
3498 setElementChangeInfoToDefaults(&ei->change_page[i]);
3500 // start with reading properties for the first change page
3501 xx_current_change_page = 0;
3503 while (!checkEndOfFile(file))
3505 // level file might contain invalid change page number
3506 if (xx_current_change_page >= ei->num_change_pages)
3509 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3511 xx_change = *change; // copy change data into temporary buffer
3513 resetEventBits(); // reset bits; change page might have changed
3515 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3518 *change = xx_change;
3520 setEventFlagsFromEventBits(change);
3522 if (real_chunk_size >= chunk_size)
3526 level->file_has_custom_elements = TRUE;
3528 return real_chunk_size;
3531 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3533 int element = getMappedElement(getFile16BitBE(file));
3534 int real_chunk_size = 2;
3535 struct ElementInfo *ei = &element_info[element];
3536 struct ElementGroupInfo *group = ei->group;
3541 xx_ei = *ei; // copy element data into temporary buffer
3542 xx_group = *group; // copy group data into temporary buffer
3544 while (!checkEndOfFile(file))
3546 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3549 if (real_chunk_size >= chunk_size)
3556 level->file_has_custom_elements = TRUE;
3558 return real_chunk_size;
3561 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3563 int element = getMappedElement(getFile16BitBE(file));
3564 int real_chunk_size = 2;
3565 struct ElementInfo *ei = &element_info[element];
3567 xx_ei = *ei; // copy element data into temporary buffer
3569 while (!checkEndOfFile(file))
3571 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3574 if (real_chunk_size >= chunk_size)
3580 level->file_has_custom_elements = TRUE;
3582 return real_chunk_size;
3585 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3586 struct LevelFileInfo *level_file_info,
3587 boolean level_info_only)
3589 char *filename = level_file_info->filename;
3590 char cookie[MAX_LINE_LEN];
3591 char chunk_name[CHUNK_ID_LEN + 1];
3595 if (!(file = openFile(filename, MODE_READ)))
3597 level->no_valid_file = TRUE;
3598 level->no_level_file = TRUE;
3600 if (level_info_only)
3603 Warn("cannot read level '%s' -- using empty level", filename);
3605 if (!setup.editor.use_template_for_new_levels)
3608 // if level file not found, try to initialize level data from template
3609 filename = getGlobalLevelTemplateFilename();
3611 if (!(file = openFile(filename, MODE_READ)))
3614 // default: for empty levels, use level template for custom elements
3615 level->use_custom_template = TRUE;
3617 level->no_valid_file = FALSE;
3620 getFileChunkBE(file, chunk_name, NULL);
3621 if (strEqual(chunk_name, "RND1"))
3623 getFile32BitBE(file); // not used
3625 getFileChunkBE(file, chunk_name, NULL);
3626 if (!strEqual(chunk_name, "CAVE"))
3628 level->no_valid_file = TRUE;
3630 Warn("unknown format of level file '%s'", filename);
3637 else // check for pre-2.0 file format with cookie string
3639 strcpy(cookie, chunk_name);
3640 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3642 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3643 cookie[strlen(cookie) - 1] = '\0';
3645 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3647 level->no_valid_file = TRUE;
3649 Warn("unknown format of level file '%s'", filename);
3656 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3658 level->no_valid_file = TRUE;
3660 Warn("unsupported version of level file '%s'", filename);
3667 // pre-2.0 level files have no game version, so use file version here
3668 level->game_version = level->file_version;
3671 if (level->file_version < FILE_VERSION_1_2)
3673 // level files from versions before 1.2.0 without chunk structure
3674 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3675 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3683 int (*loader)(File *, int, struct LevelInfo *);
3687 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3688 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3689 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3690 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3691 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3692 { "INFO", -1, LoadLevel_INFO },
3693 { "BODY", -1, LoadLevel_BODY },
3694 { "CONT", -1, LoadLevel_CONT },
3695 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3696 { "CNT3", -1, LoadLevel_CNT3 },
3697 { "CUS1", -1, LoadLevel_CUS1 },
3698 { "CUS2", -1, LoadLevel_CUS2 },
3699 { "CUS3", -1, LoadLevel_CUS3 },
3700 { "CUS4", -1, LoadLevel_CUS4 },
3701 { "GRP1", -1, LoadLevel_GRP1 },
3702 { "CONF", -1, LoadLevel_CONF },
3703 { "ELEM", -1, LoadLevel_ELEM },
3704 { "NOTE", -1, LoadLevel_NOTE },
3705 { "CUSX", -1, LoadLevel_CUSX },
3706 { "GRPX", -1, LoadLevel_GRPX },
3707 { "EMPX", -1, LoadLevel_EMPX },
3712 while (getFileChunkBE(file, chunk_name, &chunk_size))
3716 while (chunk_info[i].name != NULL &&
3717 !strEqual(chunk_name, chunk_info[i].name))
3720 if (chunk_info[i].name == NULL)
3722 Warn("unknown chunk '%s' in level file '%s'",
3723 chunk_name, filename);
3725 ReadUnusedBytesFromFile(file, chunk_size);
3727 else if (chunk_info[i].size != -1 &&
3728 chunk_info[i].size != chunk_size)
3730 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3731 chunk_size, chunk_name, filename);
3733 ReadUnusedBytesFromFile(file, chunk_size);
3737 // call function to load this level chunk
3738 int chunk_size_expected =
3739 (chunk_info[i].loader)(file, chunk_size, level);
3741 if (chunk_size_expected < 0)
3743 Warn("error reading chunk '%s' in level file '%s'",
3744 chunk_name, filename);
3749 // the size of some chunks cannot be checked before reading other
3750 // chunks first (like "HEAD" and "BODY") that contain some header
3751 // information, so check them here
3752 if (chunk_size_expected != chunk_size)
3754 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3755 chunk_size, chunk_name, filename);
3767 // ----------------------------------------------------------------------------
3768 // functions for loading BD level
3769 // ----------------------------------------------------------------------------
3771 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3773 struct LevelInfo_BD *level_bd = level->native_bd_level;
3774 GdCave *cave = NULL; // will be changed below
3775 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3776 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3779 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3781 // cave and map newly allocated when set to defaults above
3782 cave = level_bd->cave;
3784 for (i = 0; i < 5; i++)
3786 cave->level_time[i] = level->time;
3787 cave->level_diamonds[i] = level->gems_needed;
3788 cave->level_magic_wall_time[i] = level->time_magic_wall;
3790 cave->level_speed[i] = level->bd_cycle_delay_ms;
3791 cave->level_ckdelay[i] = level->bd_cycle_delay_c64;
3792 cave->level_hatching_delay_frame[i] = level->bd_hatching_delay_cycles;
3793 cave->level_hatching_delay_time[i] = level->bd_hatching_delay_seconds;
3795 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3798 cave->diamond_value = level->score[SC_EMERALD];
3799 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3801 cave->scheduling = level->bd_scheduling_type;
3802 cave->pal_timing = level->bd_pal_timing;
3803 cave->intermission = level->bd_intermission;
3804 cave->diagonal_movements = level->bd_diagonal_movements;
3805 cave->active_is_first_found = level->bd_topmost_player_active;
3807 cave->lineshift = level->bd_line_shifting_borders;
3808 cave->wraparound_objects = level->bd_wraparound_objects;
3809 cave->border_scan_first_and_last = level->bd_scan_first_and_last_row;
3810 cave->short_explosions = level->bd_short_explosions;
3811 cave->gravity_affects_all = level->bd_gravity_affects_all;
3813 strncpy(cave->name, level->name, sizeof(GdString));
3814 cave->name[sizeof(GdString) - 1] = '\0';
3816 for (x = 0; x < cave->w; x++)
3817 for (y = 0; y < cave->h; y++)
3818 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3821 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3823 struct LevelInfo_BD *level_bd = level->native_bd_level;
3824 GdCave *cave = level_bd->cave;
3825 int bd_level_nr = level_bd->level_nr;
3828 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3829 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3831 level->time = cave->level_time[bd_level_nr];
3832 level->gems_needed = cave->level_diamonds[bd_level_nr];
3833 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3835 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3836 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3837 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3838 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3840 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3841 level->score[SC_EMERALD] = cave->diamond_value;
3842 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3844 level->bd_scheduling_type = cave->scheduling;
3845 level->bd_pal_timing = cave->pal_timing;
3846 level->bd_intermission = cave->intermission;
3847 level->bd_diagonal_movements = cave->diagonal_movements;
3848 level->bd_topmost_player_active = cave->active_is_first_found;
3850 level->bd_line_shifting_borders = cave->lineshift;
3851 level->bd_wraparound_objects = cave->wraparound_objects;
3852 level->bd_scan_first_and_last_row = cave->border_scan_first_and_last;
3853 level->bd_short_explosions = cave->short_explosions;
3854 level->bd_gravity_affects_all = cave->gravity_affects_all;
3856 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3858 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3859 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3861 for (x = 0; x < level->fieldx; x++)
3862 for (y = 0; y < level->fieldy; y++)
3863 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3865 checked_free(cave_name);
3868 static void setTapeInfoToDefaults(void);
3870 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3872 struct LevelInfo_BD *level_bd = level->native_bd_level;
3873 GdCave *cave = level_bd->cave;
3874 GdReplay *replay = level_bd->replay;
3880 // always start with reliable default values
3881 setTapeInfoToDefaults();
3883 tape.level_nr = level_nr; // (currently not used)
3884 tape.random_seed = replay->seed;
3886 TapeSetDateFromIsoDateString(replay->date);
3889 tape.pos[tape.counter].delay = 0;
3891 tape.bd_replay = TRUE;
3893 // all time calculations only used to display approximate tape time
3894 int cave_speed = cave->speed;
3895 int milliseconds_game = 0;
3896 int milliseconds_elapsed = 20;
3898 for (i = 0; i < replay->movements->len; i++)
3900 int replay_action = replay->movements->data[i];
3901 int tape_action = map_action_BD_to_RND(replay_action);
3902 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3903 boolean success = 0;
3907 success = TapeAddAction(action);
3909 milliseconds_game += milliseconds_elapsed;
3911 if (milliseconds_game >= cave_speed)
3913 milliseconds_game -= cave_speed;
3920 tape.pos[tape.counter].delay = 0;
3921 tape.pos[tape.counter].action[0] = 0;
3925 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3931 TapeHaltRecording();
3935 // ----------------------------------------------------------------------------
3936 // functions for loading EM level
3937 // ----------------------------------------------------------------------------
3939 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3941 static int ball_xy[8][2] =
3952 struct LevelInfo_EM *level_em = level->native_em_level;
3953 struct CAVE *cav = level_em->cav;
3956 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3957 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3959 cav->time_seconds = level->time;
3960 cav->gems_needed = level->gems_needed;
3962 cav->emerald_score = level->score[SC_EMERALD];
3963 cav->diamond_score = level->score[SC_DIAMOND];
3964 cav->alien_score = level->score[SC_ROBOT];
3965 cav->tank_score = level->score[SC_SPACESHIP];
3966 cav->bug_score = level->score[SC_BUG];
3967 cav->eater_score = level->score[SC_YAMYAM];
3968 cav->nut_score = level->score[SC_NUT];
3969 cav->dynamite_score = level->score[SC_DYNAMITE];
3970 cav->key_score = level->score[SC_KEY];
3971 cav->exit_score = level->score[SC_TIME_BONUS];
3973 cav->num_eater_arrays = level->num_yamyam_contents;
3975 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3976 for (y = 0; y < 3; y++)
3977 for (x = 0; x < 3; x++)
3978 cav->eater_array[i][y * 3 + x] =
3979 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3981 cav->amoeba_time = level->amoeba_speed;
3982 cav->wonderwall_time = level->time_magic_wall;
3983 cav->wheel_time = level->time_wheel;
3985 cav->android_move_time = level->android_move_time;
3986 cav->android_clone_time = level->android_clone_time;
3987 cav->ball_random = level->ball_random;
3988 cav->ball_active = level->ball_active_initial;
3989 cav->ball_time = level->ball_time;
3990 cav->num_ball_arrays = level->num_ball_contents;
3992 cav->lenses_score = level->lenses_score;
3993 cav->magnify_score = level->magnify_score;
3994 cav->slurp_score = level->slurp_score;
3996 cav->lenses_time = level->lenses_time;
3997 cav->magnify_time = level->magnify_time;
3999 cav->wind_time = 9999;
4000 cav->wind_direction =
4001 map_direction_RND_to_EM(level->wind_direction_initial);
4003 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4004 for (j = 0; j < 8; j++)
4005 cav->ball_array[i][j] =
4006 map_element_RND_to_EM_cave(level->ball_content[i].
4007 e[ball_xy[j][0]][ball_xy[j][1]]);
4009 map_android_clone_elements_RND_to_EM(level);
4011 // first fill the complete playfield with the empty space element
4012 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4013 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4014 cav->cave[x][y] = Cblank;
4016 // then copy the real level contents from level file into the playfield
4017 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4019 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4021 if (level->field[x][y] == EL_AMOEBA_DEAD)
4022 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4024 cav->cave[x][y] = new_element;
4027 for (i = 0; i < MAX_PLAYERS; i++)
4029 cav->player_x[i] = -1;
4030 cav->player_y[i] = -1;
4033 // initialize player positions and delete players from the playfield
4034 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4036 if (IS_PLAYER_ELEMENT(level->field[x][y]))
4038 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4040 cav->player_x[player_nr] = x;
4041 cav->player_y[player_nr] = y;
4043 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4048 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4050 static int ball_xy[8][2] =
4061 struct LevelInfo_EM *level_em = level->native_em_level;
4062 struct CAVE *cav = level_em->cav;
4065 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4066 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4068 level->time = cav->time_seconds;
4069 level->gems_needed = cav->gems_needed;
4071 sprintf(level->name, "Level %d", level->file_info.nr);
4073 level->score[SC_EMERALD] = cav->emerald_score;
4074 level->score[SC_DIAMOND] = cav->diamond_score;
4075 level->score[SC_ROBOT] = cav->alien_score;
4076 level->score[SC_SPACESHIP] = cav->tank_score;
4077 level->score[SC_BUG] = cav->bug_score;
4078 level->score[SC_YAMYAM] = cav->eater_score;
4079 level->score[SC_NUT] = cav->nut_score;
4080 level->score[SC_DYNAMITE] = cav->dynamite_score;
4081 level->score[SC_KEY] = cav->key_score;
4082 level->score[SC_TIME_BONUS] = cav->exit_score;
4084 level->num_yamyam_contents = cav->num_eater_arrays;
4086 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4087 for (y = 0; y < 3; y++)
4088 for (x = 0; x < 3; x++)
4089 level->yamyam_content[i].e[x][y] =
4090 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4092 level->amoeba_speed = cav->amoeba_time;
4093 level->time_magic_wall = cav->wonderwall_time;
4094 level->time_wheel = cav->wheel_time;
4096 level->android_move_time = cav->android_move_time;
4097 level->android_clone_time = cav->android_clone_time;
4098 level->ball_random = cav->ball_random;
4099 level->ball_active_initial = cav->ball_active;
4100 level->ball_time = cav->ball_time;
4101 level->num_ball_contents = cav->num_ball_arrays;
4103 level->lenses_score = cav->lenses_score;
4104 level->magnify_score = cav->magnify_score;
4105 level->slurp_score = cav->slurp_score;
4107 level->lenses_time = cav->lenses_time;
4108 level->magnify_time = cav->magnify_time;
4110 level->wind_direction_initial =
4111 map_direction_EM_to_RND(cav->wind_direction);
4113 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4114 for (j = 0; j < 8; j++)
4115 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4116 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4118 map_android_clone_elements_EM_to_RND(level);
4120 // convert the playfield (some elements need special treatment)
4121 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4123 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4125 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4126 new_element = EL_AMOEBA_DEAD;
4128 level->field[x][y] = new_element;
4131 for (i = 0; i < MAX_PLAYERS; i++)
4133 // in case of all players set to the same field, use the first player
4134 int nr = MAX_PLAYERS - i - 1;
4135 int jx = cav->player_x[nr];
4136 int jy = cav->player_y[nr];
4138 if (jx != -1 && jy != -1)
4139 level->field[jx][jy] = EL_PLAYER_1 + nr;
4142 // time score is counted for each 10 seconds left in Emerald Mine levels
4143 level->time_score_base = 10;
4147 // ----------------------------------------------------------------------------
4148 // functions for loading SP level
4149 // ----------------------------------------------------------------------------
4151 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4153 struct LevelInfo_SP *level_sp = level->native_sp_level;
4154 LevelInfoType *header = &level_sp->header;
4157 level_sp->width = level->fieldx;
4158 level_sp->height = level->fieldy;
4160 for (x = 0; x < level->fieldx; x++)
4161 for (y = 0; y < level->fieldy; y++)
4162 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4164 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4166 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4167 header->LevelTitle[i] = level->name[i];
4168 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4170 header->InfotronsNeeded = level->gems_needed;
4172 header->SpecialPortCount = 0;
4174 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4176 boolean gravity_port_found = FALSE;
4177 boolean gravity_port_valid = FALSE;
4178 int gravity_port_flag;
4179 int gravity_port_base_element;
4180 int element = level->field[x][y];
4182 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4183 element <= EL_SP_GRAVITY_ON_PORT_UP)
4185 gravity_port_found = TRUE;
4186 gravity_port_valid = TRUE;
4187 gravity_port_flag = 1;
4188 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4190 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4191 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4193 gravity_port_found = TRUE;
4194 gravity_port_valid = TRUE;
4195 gravity_port_flag = 0;
4196 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4198 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4199 element <= EL_SP_GRAVITY_PORT_UP)
4201 // change R'n'D style gravity inverting special port to normal port
4202 // (there are no gravity inverting ports in native Supaplex engine)
4204 gravity_port_found = TRUE;
4205 gravity_port_valid = FALSE;
4206 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4209 if (gravity_port_found)
4211 if (gravity_port_valid &&
4212 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4214 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4216 port->PortLocation = (y * level->fieldx + x) * 2;
4217 port->Gravity = gravity_port_flag;
4219 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4221 header->SpecialPortCount++;
4225 // change special gravity port to normal port
4227 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4230 level_sp->playfield[x][y] = element - EL_SP_START;
4235 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4237 struct LevelInfo_SP *level_sp = level->native_sp_level;
4238 LevelInfoType *header = &level_sp->header;
4239 boolean num_invalid_elements = 0;
4242 level->fieldx = level_sp->width;
4243 level->fieldy = level_sp->height;
4245 for (x = 0; x < level->fieldx; x++)
4247 for (y = 0; y < level->fieldy; y++)
4249 int element_old = level_sp->playfield[x][y];
4250 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4252 if (element_new == EL_UNKNOWN)
4254 num_invalid_elements++;
4256 Debug("level:native:SP", "invalid element %d at position %d, %d",
4260 level->field[x][y] = element_new;
4264 if (num_invalid_elements > 0)
4265 Warn("found %d invalid elements%s", num_invalid_elements,
4266 (!options.debug ? " (use '--debug' for more details)" : ""));
4268 for (i = 0; i < MAX_PLAYERS; i++)
4269 level->initial_player_gravity[i] =
4270 (header->InitialGravity == 1 ? TRUE : FALSE);
4272 // skip leading spaces
4273 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4274 if (header->LevelTitle[i] != ' ')
4278 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4279 level->name[j] = header->LevelTitle[i];
4280 level->name[j] = '\0';
4282 // cut trailing spaces
4284 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4285 level->name[j - 1] = '\0';
4287 level->gems_needed = header->InfotronsNeeded;
4289 for (i = 0; i < header->SpecialPortCount; i++)
4291 SpecialPortType *port = &header->SpecialPort[i];
4292 int port_location = port->PortLocation;
4293 int gravity = port->Gravity;
4294 int port_x, port_y, port_element;
4296 port_x = (port_location / 2) % level->fieldx;
4297 port_y = (port_location / 2) / level->fieldx;
4299 if (port_x < 0 || port_x >= level->fieldx ||
4300 port_y < 0 || port_y >= level->fieldy)
4302 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4307 port_element = level->field[port_x][port_y];
4309 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4310 port_element > EL_SP_GRAVITY_PORT_UP)
4312 Warn("no special port at position (%d, %d)", port_x, port_y);
4317 // change previous (wrong) gravity inverting special port to either
4318 // gravity enabling special port or gravity disabling special port
4319 level->field[port_x][port_y] +=
4320 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4321 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4324 // change special gravity ports without database entries to normal ports
4325 for (x = 0; x < level->fieldx; x++)
4326 for (y = 0; y < level->fieldy; y++)
4327 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4328 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4329 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4331 level->time = 0; // no time limit
4332 level->amoeba_speed = 0;
4333 level->time_magic_wall = 0;
4334 level->time_wheel = 0;
4335 level->amoeba_content = EL_EMPTY;
4337 // original Supaplex does not use score values -- rate by playing time
4338 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4339 level->score[i] = 0;
4341 level->rate_time_over_score = TRUE;
4343 // there are no yamyams in supaplex levels
4344 for (i = 0; i < level->num_yamyam_contents; i++)
4345 for (x = 0; x < 3; x++)
4346 for (y = 0; y < 3; y++)
4347 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4350 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4352 struct LevelInfo_SP *level_sp = level->native_sp_level;
4353 struct DemoInfo_SP *demo = &level_sp->demo;
4356 // always start with reliable default values
4357 demo->is_available = FALSE;
4360 if (TAPE_IS_EMPTY(tape))
4363 demo->level_nr = tape.level_nr; // (currently not used)
4365 level_sp->header.DemoRandomSeed = tape.random_seed;
4369 for (i = 0; i < tape.length; i++)
4371 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4372 int demo_repeat = tape.pos[i].delay;
4373 int demo_entries = (demo_repeat + 15) / 16;
4375 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4377 Warn("tape truncated: size exceeds maximum SP demo size %d",
4383 for (j = 0; j < demo_repeat / 16; j++)
4384 demo->data[demo->length++] = 0xf0 | demo_action;
4386 if (demo_repeat % 16)
4387 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4390 demo->is_available = TRUE;
4393 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4395 struct LevelInfo_SP *level_sp = level->native_sp_level;
4396 struct DemoInfo_SP *demo = &level_sp->demo;
4397 char *filename = level->file_info.filename;
4400 // always start with reliable default values
4401 setTapeInfoToDefaults();
4403 if (!demo->is_available)
4406 tape.level_nr = demo->level_nr; // (currently not used)
4407 tape.random_seed = level_sp->header.DemoRandomSeed;
4409 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4412 tape.pos[tape.counter].delay = 0;
4414 for (i = 0; i < demo->length; i++)
4416 int demo_action = demo->data[i] & 0x0f;
4417 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4418 int tape_action = map_key_SP_to_RND(demo_action);
4419 int tape_repeat = demo_repeat + 1;
4420 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4421 boolean success = 0;
4424 for (j = 0; j < tape_repeat; j++)
4425 success = TapeAddAction(action);
4429 Warn("SP demo truncated: size exceeds maximum tape size %d",
4436 TapeHaltRecording();
4440 // ----------------------------------------------------------------------------
4441 // functions for loading MM level
4442 // ----------------------------------------------------------------------------
4444 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4446 struct LevelInfo_MM *level_mm = level->native_mm_level;
4449 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4450 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4452 level_mm->time = level->time;
4453 level_mm->kettles_needed = level->gems_needed;
4454 level_mm->auto_count_kettles = level->auto_count_gems;
4456 level_mm->mm_laser_red = level->mm_laser_red;
4457 level_mm->mm_laser_green = level->mm_laser_green;
4458 level_mm->mm_laser_blue = level->mm_laser_blue;
4460 level_mm->df_laser_red = level->df_laser_red;
4461 level_mm->df_laser_green = level->df_laser_green;
4462 level_mm->df_laser_blue = level->df_laser_blue;
4464 strcpy(level_mm->name, level->name);
4465 strcpy(level_mm->author, level->author);
4467 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4468 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4469 level_mm->score[SC_KEY] = level->score[SC_KEY];
4470 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4471 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4473 level_mm->amoeba_speed = level->amoeba_speed;
4474 level_mm->time_fuse = level->mm_time_fuse;
4475 level_mm->time_bomb = level->mm_time_bomb;
4476 level_mm->time_ball = level->mm_time_ball;
4477 level_mm->time_block = level->mm_time_block;
4479 level_mm->num_ball_contents = level->num_mm_ball_contents;
4480 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4481 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4482 level_mm->explode_ball = level->explode_mm_ball;
4484 for (i = 0; i < level->num_mm_ball_contents; i++)
4485 level_mm->ball_content[i] =
4486 map_element_RND_to_MM(level->mm_ball_content[i]);
4488 for (x = 0; x < level->fieldx; x++)
4489 for (y = 0; y < level->fieldy; y++)
4491 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4494 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4496 struct LevelInfo_MM *level_mm = level->native_mm_level;
4499 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4500 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4502 level->time = level_mm->time;
4503 level->gems_needed = level_mm->kettles_needed;
4504 level->auto_count_gems = level_mm->auto_count_kettles;
4506 level->mm_laser_red = level_mm->mm_laser_red;
4507 level->mm_laser_green = level_mm->mm_laser_green;
4508 level->mm_laser_blue = level_mm->mm_laser_blue;
4510 level->df_laser_red = level_mm->df_laser_red;
4511 level->df_laser_green = level_mm->df_laser_green;
4512 level->df_laser_blue = level_mm->df_laser_blue;
4514 strcpy(level->name, level_mm->name);
4516 // only overwrite author from 'levelinfo.conf' if author defined in level
4517 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4518 strcpy(level->author, level_mm->author);
4520 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4521 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4522 level->score[SC_KEY] = level_mm->score[SC_KEY];
4523 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4524 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4526 level->amoeba_speed = level_mm->amoeba_speed;
4527 level->mm_time_fuse = level_mm->time_fuse;
4528 level->mm_time_bomb = level_mm->time_bomb;
4529 level->mm_time_ball = level_mm->time_ball;
4530 level->mm_time_block = level_mm->time_block;
4532 level->num_mm_ball_contents = level_mm->num_ball_contents;
4533 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4534 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4535 level->explode_mm_ball = level_mm->explode_ball;
4537 for (i = 0; i < level->num_mm_ball_contents; i++)
4538 level->mm_ball_content[i] =
4539 map_element_MM_to_RND(level_mm->ball_content[i]);
4541 for (x = 0; x < level->fieldx; x++)
4542 for (y = 0; y < level->fieldy; y++)
4543 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4547 // ----------------------------------------------------------------------------
4548 // functions for loading DC level
4549 // ----------------------------------------------------------------------------
4551 #define DC_LEVEL_HEADER_SIZE 344
4553 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4556 static int last_data_encoded;
4560 int diff_hi, diff_lo;
4561 int data_hi, data_lo;
4562 unsigned short data_decoded;
4566 last_data_encoded = 0;
4573 diff = data_encoded - last_data_encoded;
4574 diff_hi = diff & ~0xff;
4575 diff_lo = diff & 0xff;
4579 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4580 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4581 data_hi = data_hi & 0xff00;
4583 data_decoded = data_hi | data_lo;
4585 last_data_encoded = data_encoded;
4587 offset1 = (offset1 + 1) % 31;
4588 offset2 = offset2 & 0xff;
4590 return data_decoded;
4593 static int getMappedElement_DC(int element)
4601 // 0x0117 - 0x036e: (?)
4604 // 0x042d - 0x0684: (?)
4620 element = EL_CRYSTAL;
4623 case 0x0e77: // quicksand (boulder)
4624 element = EL_QUICKSAND_FAST_FULL;
4627 case 0x0e99: // slow quicksand (boulder)
4628 element = EL_QUICKSAND_FULL;
4632 element = EL_EM_EXIT_OPEN;
4636 element = EL_EM_EXIT_CLOSED;
4640 element = EL_EM_STEEL_EXIT_OPEN;
4644 element = EL_EM_STEEL_EXIT_CLOSED;
4647 case 0x0f4f: // dynamite (lit 1)
4648 element = EL_EM_DYNAMITE_ACTIVE;
4651 case 0x0f57: // dynamite (lit 2)
4652 element = EL_EM_DYNAMITE_ACTIVE;
4655 case 0x0f5f: // dynamite (lit 3)
4656 element = EL_EM_DYNAMITE_ACTIVE;
4659 case 0x0f67: // dynamite (lit 4)
4660 element = EL_EM_DYNAMITE_ACTIVE;
4667 element = EL_AMOEBA_WET;
4671 element = EL_AMOEBA_DROP;
4675 element = EL_DC_MAGIC_WALL;
4679 element = EL_SPACESHIP_UP;
4683 element = EL_SPACESHIP_DOWN;
4687 element = EL_SPACESHIP_LEFT;
4691 element = EL_SPACESHIP_RIGHT;
4695 element = EL_BUG_UP;
4699 element = EL_BUG_DOWN;
4703 element = EL_BUG_LEFT;
4707 element = EL_BUG_RIGHT;
4711 element = EL_MOLE_UP;
4715 element = EL_MOLE_DOWN;
4719 element = EL_MOLE_LEFT;
4723 element = EL_MOLE_RIGHT;
4731 element = EL_YAMYAM_UP;
4735 element = EL_SWITCHGATE_OPEN;
4739 element = EL_SWITCHGATE_CLOSED;
4743 element = EL_DC_SWITCHGATE_SWITCH_UP;
4747 element = EL_TIMEGATE_CLOSED;
4750 case 0x144c: // conveyor belt switch (green)
4751 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4754 case 0x144f: // conveyor belt switch (red)
4755 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4758 case 0x1452: // conveyor belt switch (blue)
4759 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4763 element = EL_CONVEYOR_BELT_3_MIDDLE;
4767 element = EL_CONVEYOR_BELT_3_LEFT;
4771 element = EL_CONVEYOR_BELT_3_RIGHT;
4775 element = EL_CONVEYOR_BELT_1_MIDDLE;
4779 element = EL_CONVEYOR_BELT_1_LEFT;
4783 element = EL_CONVEYOR_BELT_1_RIGHT;
4787 element = EL_CONVEYOR_BELT_4_MIDDLE;
4791 element = EL_CONVEYOR_BELT_4_LEFT;
4795 element = EL_CONVEYOR_BELT_4_RIGHT;
4799 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4803 element = EL_EXPANDABLE_WALL_VERTICAL;
4807 element = EL_EXPANDABLE_WALL_ANY;
4810 case 0x14ce: // growing steel wall (left/right)
4811 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4814 case 0x14df: // growing steel wall (up/down)
4815 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4818 case 0x14e8: // growing steel wall (up/down/left/right)
4819 element = EL_EXPANDABLE_STEELWALL_ANY;
4823 element = EL_SHIELD_DEADLY;
4827 element = EL_EXTRA_TIME;
4835 element = EL_EMPTY_SPACE;
4838 case 0x1578: // quicksand (empty)
4839 element = EL_QUICKSAND_FAST_EMPTY;
4842 case 0x1579: // slow quicksand (empty)
4843 element = EL_QUICKSAND_EMPTY;
4853 element = EL_EM_DYNAMITE;
4856 case 0x15a1: // key (red)
4857 element = EL_EM_KEY_1;
4860 case 0x15a2: // key (yellow)
4861 element = EL_EM_KEY_2;
4864 case 0x15a3: // key (blue)
4865 element = EL_EM_KEY_4;
4868 case 0x15a4: // key (green)
4869 element = EL_EM_KEY_3;
4872 case 0x15a5: // key (white)
4873 element = EL_DC_KEY_WHITE;
4877 element = EL_WALL_SLIPPERY;
4884 case 0x15a8: // wall (not round)
4888 case 0x15a9: // (blue)
4889 element = EL_CHAR_A;
4892 case 0x15aa: // (blue)
4893 element = EL_CHAR_B;
4896 case 0x15ab: // (blue)
4897 element = EL_CHAR_C;
4900 case 0x15ac: // (blue)
4901 element = EL_CHAR_D;
4904 case 0x15ad: // (blue)
4905 element = EL_CHAR_E;
4908 case 0x15ae: // (blue)
4909 element = EL_CHAR_F;
4912 case 0x15af: // (blue)
4913 element = EL_CHAR_G;
4916 case 0x15b0: // (blue)
4917 element = EL_CHAR_H;
4920 case 0x15b1: // (blue)
4921 element = EL_CHAR_I;
4924 case 0x15b2: // (blue)
4925 element = EL_CHAR_J;
4928 case 0x15b3: // (blue)
4929 element = EL_CHAR_K;
4932 case 0x15b4: // (blue)
4933 element = EL_CHAR_L;
4936 case 0x15b5: // (blue)
4937 element = EL_CHAR_M;
4940 case 0x15b6: // (blue)
4941 element = EL_CHAR_N;
4944 case 0x15b7: // (blue)
4945 element = EL_CHAR_O;
4948 case 0x15b8: // (blue)
4949 element = EL_CHAR_P;
4952 case 0x15b9: // (blue)
4953 element = EL_CHAR_Q;
4956 case 0x15ba: // (blue)
4957 element = EL_CHAR_R;
4960 case 0x15bb: // (blue)
4961 element = EL_CHAR_S;
4964 case 0x15bc: // (blue)
4965 element = EL_CHAR_T;
4968 case 0x15bd: // (blue)
4969 element = EL_CHAR_U;
4972 case 0x15be: // (blue)
4973 element = EL_CHAR_V;
4976 case 0x15bf: // (blue)
4977 element = EL_CHAR_W;
4980 case 0x15c0: // (blue)
4981 element = EL_CHAR_X;
4984 case 0x15c1: // (blue)
4985 element = EL_CHAR_Y;
4988 case 0x15c2: // (blue)
4989 element = EL_CHAR_Z;
4992 case 0x15c3: // (blue)
4993 element = EL_CHAR_AUMLAUT;
4996 case 0x15c4: // (blue)
4997 element = EL_CHAR_OUMLAUT;
5000 case 0x15c5: // (blue)
5001 element = EL_CHAR_UUMLAUT;
5004 case 0x15c6: // (blue)
5005 element = EL_CHAR_0;
5008 case 0x15c7: // (blue)
5009 element = EL_CHAR_1;
5012 case 0x15c8: // (blue)
5013 element = EL_CHAR_2;
5016 case 0x15c9: // (blue)
5017 element = EL_CHAR_3;
5020 case 0x15ca: // (blue)
5021 element = EL_CHAR_4;
5024 case 0x15cb: // (blue)
5025 element = EL_CHAR_5;
5028 case 0x15cc: // (blue)
5029 element = EL_CHAR_6;
5032 case 0x15cd: // (blue)
5033 element = EL_CHAR_7;
5036 case 0x15ce: // (blue)
5037 element = EL_CHAR_8;
5040 case 0x15cf: // (blue)
5041 element = EL_CHAR_9;
5044 case 0x15d0: // (blue)
5045 element = EL_CHAR_PERIOD;
5048 case 0x15d1: // (blue)
5049 element = EL_CHAR_EXCLAM;
5052 case 0x15d2: // (blue)
5053 element = EL_CHAR_COLON;
5056 case 0x15d3: // (blue)
5057 element = EL_CHAR_LESS;
5060 case 0x15d4: // (blue)
5061 element = EL_CHAR_GREATER;
5064 case 0x15d5: // (blue)
5065 element = EL_CHAR_QUESTION;
5068 case 0x15d6: // (blue)
5069 element = EL_CHAR_COPYRIGHT;
5072 case 0x15d7: // (blue)
5073 element = EL_CHAR_UP;
5076 case 0x15d8: // (blue)
5077 element = EL_CHAR_DOWN;
5080 case 0x15d9: // (blue)
5081 element = EL_CHAR_BUTTON;
5084 case 0x15da: // (blue)
5085 element = EL_CHAR_PLUS;
5088 case 0x15db: // (blue)
5089 element = EL_CHAR_MINUS;
5092 case 0x15dc: // (blue)
5093 element = EL_CHAR_APOSTROPHE;
5096 case 0x15dd: // (blue)
5097 element = EL_CHAR_PARENLEFT;
5100 case 0x15de: // (blue)
5101 element = EL_CHAR_PARENRIGHT;
5104 case 0x15df: // (green)
5105 element = EL_CHAR_A;
5108 case 0x15e0: // (green)
5109 element = EL_CHAR_B;
5112 case 0x15e1: // (green)
5113 element = EL_CHAR_C;
5116 case 0x15e2: // (green)
5117 element = EL_CHAR_D;
5120 case 0x15e3: // (green)
5121 element = EL_CHAR_E;
5124 case 0x15e4: // (green)
5125 element = EL_CHAR_F;
5128 case 0x15e5: // (green)
5129 element = EL_CHAR_G;
5132 case 0x15e6: // (green)
5133 element = EL_CHAR_H;
5136 case 0x15e7: // (green)
5137 element = EL_CHAR_I;
5140 case 0x15e8: // (green)
5141 element = EL_CHAR_J;
5144 case 0x15e9: // (green)
5145 element = EL_CHAR_K;
5148 case 0x15ea: // (green)
5149 element = EL_CHAR_L;
5152 case 0x15eb: // (green)
5153 element = EL_CHAR_M;
5156 case 0x15ec: // (green)
5157 element = EL_CHAR_N;
5160 case 0x15ed: // (green)
5161 element = EL_CHAR_O;
5164 case 0x15ee: // (green)
5165 element = EL_CHAR_P;
5168 case 0x15ef: // (green)
5169 element = EL_CHAR_Q;
5172 case 0x15f0: // (green)
5173 element = EL_CHAR_R;
5176 case 0x15f1: // (green)
5177 element = EL_CHAR_S;
5180 case 0x15f2: // (green)
5181 element = EL_CHAR_T;
5184 case 0x15f3: // (green)
5185 element = EL_CHAR_U;
5188 case 0x15f4: // (green)
5189 element = EL_CHAR_V;
5192 case 0x15f5: // (green)
5193 element = EL_CHAR_W;
5196 case 0x15f6: // (green)
5197 element = EL_CHAR_X;
5200 case 0x15f7: // (green)
5201 element = EL_CHAR_Y;
5204 case 0x15f8: // (green)
5205 element = EL_CHAR_Z;
5208 case 0x15f9: // (green)
5209 element = EL_CHAR_AUMLAUT;
5212 case 0x15fa: // (green)
5213 element = EL_CHAR_OUMLAUT;
5216 case 0x15fb: // (green)
5217 element = EL_CHAR_UUMLAUT;
5220 case 0x15fc: // (green)
5221 element = EL_CHAR_0;
5224 case 0x15fd: // (green)
5225 element = EL_CHAR_1;
5228 case 0x15fe: // (green)
5229 element = EL_CHAR_2;
5232 case 0x15ff: // (green)
5233 element = EL_CHAR_3;
5236 case 0x1600: // (green)
5237 element = EL_CHAR_4;
5240 case 0x1601: // (green)
5241 element = EL_CHAR_5;
5244 case 0x1602: // (green)
5245 element = EL_CHAR_6;
5248 case 0x1603: // (green)
5249 element = EL_CHAR_7;
5252 case 0x1604: // (green)
5253 element = EL_CHAR_8;
5256 case 0x1605: // (green)
5257 element = EL_CHAR_9;
5260 case 0x1606: // (green)
5261 element = EL_CHAR_PERIOD;
5264 case 0x1607: // (green)
5265 element = EL_CHAR_EXCLAM;
5268 case 0x1608: // (green)
5269 element = EL_CHAR_COLON;
5272 case 0x1609: // (green)
5273 element = EL_CHAR_LESS;
5276 case 0x160a: // (green)
5277 element = EL_CHAR_GREATER;
5280 case 0x160b: // (green)
5281 element = EL_CHAR_QUESTION;
5284 case 0x160c: // (green)
5285 element = EL_CHAR_COPYRIGHT;
5288 case 0x160d: // (green)
5289 element = EL_CHAR_UP;
5292 case 0x160e: // (green)
5293 element = EL_CHAR_DOWN;
5296 case 0x160f: // (green)
5297 element = EL_CHAR_BUTTON;
5300 case 0x1610: // (green)
5301 element = EL_CHAR_PLUS;
5304 case 0x1611: // (green)
5305 element = EL_CHAR_MINUS;
5308 case 0x1612: // (green)
5309 element = EL_CHAR_APOSTROPHE;
5312 case 0x1613: // (green)
5313 element = EL_CHAR_PARENLEFT;
5316 case 0x1614: // (green)
5317 element = EL_CHAR_PARENRIGHT;
5320 case 0x1615: // (blue steel)
5321 element = EL_STEEL_CHAR_A;
5324 case 0x1616: // (blue steel)
5325 element = EL_STEEL_CHAR_B;
5328 case 0x1617: // (blue steel)
5329 element = EL_STEEL_CHAR_C;
5332 case 0x1618: // (blue steel)
5333 element = EL_STEEL_CHAR_D;
5336 case 0x1619: // (blue steel)
5337 element = EL_STEEL_CHAR_E;
5340 case 0x161a: // (blue steel)
5341 element = EL_STEEL_CHAR_F;
5344 case 0x161b: // (blue steel)
5345 element = EL_STEEL_CHAR_G;
5348 case 0x161c: // (blue steel)
5349 element = EL_STEEL_CHAR_H;
5352 case 0x161d: // (blue steel)
5353 element = EL_STEEL_CHAR_I;
5356 case 0x161e: // (blue steel)
5357 element = EL_STEEL_CHAR_J;
5360 case 0x161f: // (blue steel)
5361 element = EL_STEEL_CHAR_K;
5364 case 0x1620: // (blue steel)
5365 element = EL_STEEL_CHAR_L;
5368 case 0x1621: // (blue steel)
5369 element = EL_STEEL_CHAR_M;
5372 case 0x1622: // (blue steel)
5373 element = EL_STEEL_CHAR_N;
5376 case 0x1623: // (blue steel)
5377 element = EL_STEEL_CHAR_O;
5380 case 0x1624: // (blue steel)
5381 element = EL_STEEL_CHAR_P;
5384 case 0x1625: // (blue steel)
5385 element = EL_STEEL_CHAR_Q;
5388 case 0x1626: // (blue steel)
5389 element = EL_STEEL_CHAR_R;
5392 case 0x1627: // (blue steel)
5393 element = EL_STEEL_CHAR_S;
5396 case 0x1628: // (blue steel)
5397 element = EL_STEEL_CHAR_T;
5400 case 0x1629: // (blue steel)
5401 element = EL_STEEL_CHAR_U;
5404 case 0x162a: // (blue steel)
5405 element = EL_STEEL_CHAR_V;
5408 case 0x162b: // (blue steel)
5409 element = EL_STEEL_CHAR_W;
5412 case 0x162c: // (blue steel)
5413 element = EL_STEEL_CHAR_X;
5416 case 0x162d: // (blue steel)
5417 element = EL_STEEL_CHAR_Y;
5420 case 0x162e: // (blue steel)
5421 element = EL_STEEL_CHAR_Z;
5424 case 0x162f: // (blue steel)
5425 element = EL_STEEL_CHAR_AUMLAUT;
5428 case 0x1630: // (blue steel)
5429 element = EL_STEEL_CHAR_OUMLAUT;
5432 case 0x1631: // (blue steel)
5433 element = EL_STEEL_CHAR_UUMLAUT;
5436 case 0x1632: // (blue steel)
5437 element = EL_STEEL_CHAR_0;
5440 case 0x1633: // (blue steel)
5441 element = EL_STEEL_CHAR_1;
5444 case 0x1634: // (blue steel)
5445 element = EL_STEEL_CHAR_2;
5448 case 0x1635: // (blue steel)
5449 element = EL_STEEL_CHAR_3;
5452 case 0x1636: // (blue steel)
5453 element = EL_STEEL_CHAR_4;
5456 case 0x1637: // (blue steel)
5457 element = EL_STEEL_CHAR_5;
5460 case 0x1638: // (blue steel)
5461 element = EL_STEEL_CHAR_6;
5464 case 0x1639: // (blue steel)
5465 element = EL_STEEL_CHAR_7;
5468 case 0x163a: // (blue steel)
5469 element = EL_STEEL_CHAR_8;
5472 case 0x163b: // (blue steel)
5473 element = EL_STEEL_CHAR_9;
5476 case 0x163c: // (blue steel)
5477 element = EL_STEEL_CHAR_PERIOD;
5480 case 0x163d: // (blue steel)
5481 element = EL_STEEL_CHAR_EXCLAM;
5484 case 0x163e: // (blue steel)
5485 element = EL_STEEL_CHAR_COLON;
5488 case 0x163f: // (blue steel)
5489 element = EL_STEEL_CHAR_LESS;
5492 case 0x1640: // (blue steel)
5493 element = EL_STEEL_CHAR_GREATER;
5496 case 0x1641: // (blue steel)
5497 element = EL_STEEL_CHAR_QUESTION;
5500 case 0x1642: // (blue steel)
5501 element = EL_STEEL_CHAR_COPYRIGHT;
5504 case 0x1643: // (blue steel)
5505 element = EL_STEEL_CHAR_UP;
5508 case 0x1644: // (blue steel)
5509 element = EL_STEEL_CHAR_DOWN;
5512 case 0x1645: // (blue steel)
5513 element = EL_STEEL_CHAR_BUTTON;
5516 case 0x1646: // (blue steel)
5517 element = EL_STEEL_CHAR_PLUS;
5520 case 0x1647: // (blue steel)
5521 element = EL_STEEL_CHAR_MINUS;
5524 case 0x1648: // (blue steel)
5525 element = EL_STEEL_CHAR_APOSTROPHE;
5528 case 0x1649: // (blue steel)
5529 element = EL_STEEL_CHAR_PARENLEFT;
5532 case 0x164a: // (blue steel)
5533 element = EL_STEEL_CHAR_PARENRIGHT;
5536 case 0x164b: // (green steel)
5537 element = EL_STEEL_CHAR_A;
5540 case 0x164c: // (green steel)
5541 element = EL_STEEL_CHAR_B;
5544 case 0x164d: // (green steel)
5545 element = EL_STEEL_CHAR_C;
5548 case 0x164e: // (green steel)
5549 element = EL_STEEL_CHAR_D;
5552 case 0x164f: // (green steel)
5553 element = EL_STEEL_CHAR_E;
5556 case 0x1650: // (green steel)
5557 element = EL_STEEL_CHAR_F;
5560 case 0x1651: // (green steel)
5561 element = EL_STEEL_CHAR_G;
5564 case 0x1652: // (green steel)
5565 element = EL_STEEL_CHAR_H;
5568 case 0x1653: // (green steel)
5569 element = EL_STEEL_CHAR_I;
5572 case 0x1654: // (green steel)
5573 element = EL_STEEL_CHAR_J;
5576 case 0x1655: // (green steel)
5577 element = EL_STEEL_CHAR_K;
5580 case 0x1656: // (green steel)
5581 element = EL_STEEL_CHAR_L;
5584 case 0x1657: // (green steel)
5585 element = EL_STEEL_CHAR_M;
5588 case 0x1658: // (green steel)
5589 element = EL_STEEL_CHAR_N;
5592 case 0x1659: // (green steel)
5593 element = EL_STEEL_CHAR_O;
5596 case 0x165a: // (green steel)
5597 element = EL_STEEL_CHAR_P;
5600 case 0x165b: // (green steel)
5601 element = EL_STEEL_CHAR_Q;
5604 case 0x165c: // (green steel)
5605 element = EL_STEEL_CHAR_R;
5608 case 0x165d: // (green steel)
5609 element = EL_STEEL_CHAR_S;
5612 case 0x165e: // (green steel)
5613 element = EL_STEEL_CHAR_T;
5616 case 0x165f: // (green steel)
5617 element = EL_STEEL_CHAR_U;
5620 case 0x1660: // (green steel)
5621 element = EL_STEEL_CHAR_V;
5624 case 0x1661: // (green steel)
5625 element = EL_STEEL_CHAR_W;
5628 case 0x1662: // (green steel)
5629 element = EL_STEEL_CHAR_X;
5632 case 0x1663: // (green steel)
5633 element = EL_STEEL_CHAR_Y;
5636 case 0x1664: // (green steel)
5637 element = EL_STEEL_CHAR_Z;
5640 case 0x1665: // (green steel)
5641 element = EL_STEEL_CHAR_AUMLAUT;
5644 case 0x1666: // (green steel)
5645 element = EL_STEEL_CHAR_OUMLAUT;
5648 case 0x1667: // (green steel)
5649 element = EL_STEEL_CHAR_UUMLAUT;
5652 case 0x1668: // (green steel)
5653 element = EL_STEEL_CHAR_0;
5656 case 0x1669: // (green steel)
5657 element = EL_STEEL_CHAR_1;
5660 case 0x166a: // (green steel)
5661 element = EL_STEEL_CHAR_2;
5664 case 0x166b: // (green steel)
5665 element = EL_STEEL_CHAR_3;
5668 case 0x166c: // (green steel)
5669 element = EL_STEEL_CHAR_4;
5672 case 0x166d: // (green steel)
5673 element = EL_STEEL_CHAR_5;
5676 case 0x166e: // (green steel)
5677 element = EL_STEEL_CHAR_6;
5680 case 0x166f: // (green steel)
5681 element = EL_STEEL_CHAR_7;
5684 case 0x1670: // (green steel)
5685 element = EL_STEEL_CHAR_8;
5688 case 0x1671: // (green steel)
5689 element = EL_STEEL_CHAR_9;
5692 case 0x1672: // (green steel)
5693 element = EL_STEEL_CHAR_PERIOD;
5696 case 0x1673: // (green steel)
5697 element = EL_STEEL_CHAR_EXCLAM;
5700 case 0x1674: // (green steel)
5701 element = EL_STEEL_CHAR_COLON;
5704 case 0x1675: // (green steel)
5705 element = EL_STEEL_CHAR_LESS;
5708 case 0x1676: // (green steel)
5709 element = EL_STEEL_CHAR_GREATER;
5712 case 0x1677: // (green steel)
5713 element = EL_STEEL_CHAR_QUESTION;
5716 case 0x1678: // (green steel)
5717 element = EL_STEEL_CHAR_COPYRIGHT;
5720 case 0x1679: // (green steel)
5721 element = EL_STEEL_CHAR_UP;
5724 case 0x167a: // (green steel)
5725 element = EL_STEEL_CHAR_DOWN;
5728 case 0x167b: // (green steel)
5729 element = EL_STEEL_CHAR_BUTTON;
5732 case 0x167c: // (green steel)
5733 element = EL_STEEL_CHAR_PLUS;
5736 case 0x167d: // (green steel)
5737 element = EL_STEEL_CHAR_MINUS;
5740 case 0x167e: // (green steel)
5741 element = EL_STEEL_CHAR_APOSTROPHE;
5744 case 0x167f: // (green steel)
5745 element = EL_STEEL_CHAR_PARENLEFT;
5748 case 0x1680: // (green steel)
5749 element = EL_STEEL_CHAR_PARENRIGHT;
5752 case 0x1681: // gate (red)
5753 element = EL_EM_GATE_1;
5756 case 0x1682: // secret gate (red)
5757 element = EL_EM_GATE_1_GRAY;
5760 case 0x1683: // gate (yellow)
5761 element = EL_EM_GATE_2;
5764 case 0x1684: // secret gate (yellow)
5765 element = EL_EM_GATE_2_GRAY;
5768 case 0x1685: // gate (blue)
5769 element = EL_EM_GATE_4;
5772 case 0x1686: // secret gate (blue)
5773 element = EL_EM_GATE_4_GRAY;
5776 case 0x1687: // gate (green)
5777 element = EL_EM_GATE_3;
5780 case 0x1688: // secret gate (green)
5781 element = EL_EM_GATE_3_GRAY;
5784 case 0x1689: // gate (white)
5785 element = EL_DC_GATE_WHITE;
5788 case 0x168a: // secret gate (white)
5789 element = EL_DC_GATE_WHITE_GRAY;
5792 case 0x168b: // secret gate (no key)
5793 element = EL_DC_GATE_FAKE_GRAY;
5797 element = EL_ROBOT_WHEEL;
5801 element = EL_DC_TIMEGATE_SWITCH;
5805 element = EL_ACID_POOL_BOTTOM;
5809 element = EL_ACID_POOL_TOPLEFT;
5813 element = EL_ACID_POOL_TOPRIGHT;
5817 element = EL_ACID_POOL_BOTTOMLEFT;
5821 element = EL_ACID_POOL_BOTTOMRIGHT;
5825 element = EL_STEELWALL;
5829 element = EL_STEELWALL_SLIPPERY;
5832 case 0x1695: // steel wall (not round)
5833 element = EL_STEELWALL;
5836 case 0x1696: // steel wall (left)
5837 element = EL_DC_STEELWALL_1_LEFT;
5840 case 0x1697: // steel wall (bottom)
5841 element = EL_DC_STEELWALL_1_BOTTOM;
5844 case 0x1698: // steel wall (right)
5845 element = EL_DC_STEELWALL_1_RIGHT;
5848 case 0x1699: // steel wall (top)
5849 element = EL_DC_STEELWALL_1_TOP;
5852 case 0x169a: // steel wall (left/bottom)
5853 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5856 case 0x169b: // steel wall (right/bottom)
5857 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5860 case 0x169c: // steel wall (right/top)
5861 element = EL_DC_STEELWALL_1_TOPRIGHT;
5864 case 0x169d: // steel wall (left/top)
5865 element = EL_DC_STEELWALL_1_TOPLEFT;
5868 case 0x169e: // steel wall (right/bottom small)
5869 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5872 case 0x169f: // steel wall (left/bottom small)
5873 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5876 case 0x16a0: // steel wall (right/top small)
5877 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5880 case 0x16a1: // steel wall (left/top small)
5881 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5884 case 0x16a2: // steel wall (left/right)
5885 element = EL_DC_STEELWALL_1_VERTICAL;
5888 case 0x16a3: // steel wall (top/bottom)
5889 element = EL_DC_STEELWALL_1_HORIZONTAL;
5892 case 0x16a4: // steel wall 2 (left end)
5893 element = EL_DC_STEELWALL_2_LEFT;
5896 case 0x16a5: // steel wall 2 (right end)
5897 element = EL_DC_STEELWALL_2_RIGHT;
5900 case 0x16a6: // steel wall 2 (top end)
5901 element = EL_DC_STEELWALL_2_TOP;
5904 case 0x16a7: // steel wall 2 (bottom end)
5905 element = EL_DC_STEELWALL_2_BOTTOM;
5908 case 0x16a8: // steel wall 2 (left/right)
5909 element = EL_DC_STEELWALL_2_HORIZONTAL;
5912 case 0x16a9: // steel wall 2 (up/down)
5913 element = EL_DC_STEELWALL_2_VERTICAL;
5916 case 0x16aa: // steel wall 2 (mid)
5917 element = EL_DC_STEELWALL_2_MIDDLE;
5921 element = EL_SIGN_EXCLAMATION;
5925 element = EL_SIGN_RADIOACTIVITY;
5929 element = EL_SIGN_STOP;
5933 element = EL_SIGN_WHEELCHAIR;
5937 element = EL_SIGN_PARKING;
5941 element = EL_SIGN_NO_ENTRY;
5945 element = EL_SIGN_HEART;
5949 element = EL_SIGN_GIVE_WAY;
5953 element = EL_SIGN_ENTRY_FORBIDDEN;
5957 element = EL_SIGN_EMERGENCY_EXIT;
5961 element = EL_SIGN_YIN_YANG;
5965 element = EL_WALL_EMERALD;
5969 element = EL_WALL_DIAMOND;
5973 element = EL_WALL_PEARL;
5977 element = EL_WALL_CRYSTAL;
5981 element = EL_INVISIBLE_WALL;
5985 element = EL_INVISIBLE_STEELWALL;
5989 // EL_INVISIBLE_SAND
5992 element = EL_LIGHT_SWITCH;
5996 element = EL_ENVELOPE_1;
6000 if (element >= 0x0117 && element <= 0x036e) // (?)
6001 element = EL_DIAMOND;
6002 else if (element >= 0x042d && element <= 0x0684) // (?)
6003 element = EL_EMERALD;
6004 else if (element >= 0x157c && element <= 0x158b)
6006 else if (element >= 0x1590 && element <= 0x159f)
6007 element = EL_DC_LANDMINE;
6008 else if (element >= 0x16bc && element <= 0x16cb)
6009 element = EL_INVISIBLE_SAND;
6012 Warn("unknown Diamond Caves element 0x%04x", element);
6014 element = EL_UNKNOWN;
6019 return getMappedElement(element);
6022 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6024 byte header[DC_LEVEL_HEADER_SIZE];
6026 int envelope_header_pos = 62;
6027 int envelope_content_pos = 94;
6028 int level_name_pos = 251;
6029 int level_author_pos = 292;
6030 int envelope_header_len;
6031 int envelope_content_len;
6033 int level_author_len;
6035 int num_yamyam_contents;
6038 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6040 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6042 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6044 header[i * 2 + 0] = header_word >> 8;
6045 header[i * 2 + 1] = header_word & 0xff;
6048 // read some values from level header to check level decoding integrity
6049 fieldx = header[6] | (header[7] << 8);
6050 fieldy = header[8] | (header[9] << 8);
6051 num_yamyam_contents = header[60] | (header[61] << 8);
6053 // do some simple sanity checks to ensure that level was correctly decoded
6054 if (fieldx < 1 || fieldx > 256 ||
6055 fieldy < 1 || fieldy > 256 ||
6056 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6058 level->no_valid_file = TRUE;
6060 Warn("cannot decode level from stream -- using empty level");
6065 // maximum envelope header size is 31 bytes
6066 envelope_header_len = header[envelope_header_pos];
6067 // maximum envelope content size is 110 (156?) bytes
6068 envelope_content_len = header[envelope_content_pos];
6070 // maximum level title size is 40 bytes
6071 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6072 // maximum level author size is 30 (51?) bytes
6073 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6077 for (i = 0; i < envelope_header_len; i++)
6078 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6079 level->envelope[0].text[envelope_size++] =
6080 header[envelope_header_pos + 1 + i];
6082 if (envelope_header_len > 0 && envelope_content_len > 0)
6084 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6085 level->envelope[0].text[envelope_size++] = '\n';
6086 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6087 level->envelope[0].text[envelope_size++] = '\n';
6090 for (i = 0; i < envelope_content_len; i++)
6091 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6092 level->envelope[0].text[envelope_size++] =
6093 header[envelope_content_pos + 1 + i];
6095 level->envelope[0].text[envelope_size] = '\0';
6097 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6098 level->envelope[0].ysize = 10;
6099 level->envelope[0].autowrap = TRUE;
6100 level->envelope[0].centered = TRUE;
6102 for (i = 0; i < level_name_len; i++)
6103 level->name[i] = header[level_name_pos + 1 + i];
6104 level->name[level_name_len] = '\0';
6106 for (i = 0; i < level_author_len; i++)
6107 level->author[i] = header[level_author_pos + 1 + i];
6108 level->author[level_author_len] = '\0';
6110 num_yamyam_contents = header[60] | (header[61] << 8);
6111 level->num_yamyam_contents =
6112 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6114 for (i = 0; i < num_yamyam_contents; i++)
6116 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6118 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6119 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6121 if (i < MAX_ELEMENT_CONTENTS)
6122 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6126 fieldx = header[6] | (header[7] << 8);
6127 fieldy = header[8] | (header[9] << 8);
6128 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6129 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6131 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6133 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6134 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6136 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6137 level->field[x][y] = getMappedElement_DC(element_dc);
6140 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6141 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6142 level->field[x][y] = EL_PLAYER_1;
6144 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6145 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6146 level->field[x][y] = EL_PLAYER_2;
6148 level->gems_needed = header[18] | (header[19] << 8);
6150 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6151 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6152 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6153 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6154 level->score[SC_NUT] = header[28] | (header[29] << 8);
6155 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6156 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6157 level->score[SC_BUG] = header[34] | (header[35] << 8);
6158 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6159 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6160 level->score[SC_KEY] = header[40] | (header[41] << 8);
6161 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6163 level->time = header[44] | (header[45] << 8);
6165 level->amoeba_speed = header[46] | (header[47] << 8);
6166 level->time_light = header[48] | (header[49] << 8);
6167 level->time_timegate = header[50] | (header[51] << 8);
6168 level->time_wheel = header[52] | (header[53] << 8);
6169 level->time_magic_wall = header[54] | (header[55] << 8);
6170 level->extra_time = header[56] | (header[57] << 8);
6171 level->shield_normal_time = header[58] | (header[59] << 8);
6173 // shield and extra time elements do not have a score
6174 level->score[SC_SHIELD] = 0;
6175 level->extra_time_score = 0;
6177 // set time for normal and deadly shields to the same value
6178 level->shield_deadly_time = level->shield_normal_time;
6180 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6181 // can slip down from flat walls, like normal walls and steel walls
6182 level->em_slippery_gems = TRUE;
6184 // time score is counted for each 10 seconds left in Diamond Caves levels
6185 level->time_score_base = 10;
6188 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6189 struct LevelFileInfo *level_file_info,
6190 boolean level_info_only)
6192 char *filename = level_file_info->filename;
6194 int num_magic_bytes = 8;
6195 char magic_bytes[num_magic_bytes + 1];
6196 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6198 if (!(file = openFile(filename, MODE_READ)))
6200 level->no_valid_file = TRUE;
6202 if (!level_info_only)
6203 Warn("cannot read level '%s' -- using empty level", filename);
6208 // fseek(file, 0x0000, SEEK_SET);
6210 if (level_file_info->packed)
6212 // read "magic bytes" from start of file
6213 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6214 magic_bytes[0] = '\0';
6216 // check "magic bytes" for correct file format
6217 if (!strPrefix(magic_bytes, "DC2"))
6219 level->no_valid_file = TRUE;
6221 Warn("unknown DC level file '%s' -- using empty level", filename);
6226 if (strPrefix(magic_bytes, "DC2Win95") ||
6227 strPrefix(magic_bytes, "DC2Win98"))
6229 int position_first_level = 0x00fa;
6230 int extra_bytes = 4;
6233 // advance file stream to first level inside the level package
6234 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6236 // each block of level data is followed by block of non-level data
6237 num_levels_to_skip *= 2;
6239 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6240 while (num_levels_to_skip >= 0)
6242 // advance file stream to next level inside the level package
6243 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6245 level->no_valid_file = TRUE;
6247 Warn("cannot fseek in file '%s' -- using empty level", filename);
6252 // skip apparently unused extra bytes following each level
6253 ReadUnusedBytesFromFile(file, extra_bytes);
6255 // read size of next level in level package
6256 skip_bytes = getFile32BitLE(file);
6258 num_levels_to_skip--;
6263 level->no_valid_file = TRUE;
6265 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6271 LoadLevelFromFileStream_DC(file, level);
6277 // ----------------------------------------------------------------------------
6278 // functions for loading SB level
6279 // ----------------------------------------------------------------------------
6281 int getMappedElement_SB(int element_ascii, boolean use_ces)
6289 sb_element_mapping[] =
6291 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6292 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6293 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6294 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6295 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6296 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6297 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6298 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6305 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6306 if (element_ascii == sb_element_mapping[i].ascii)
6307 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6309 return EL_UNDEFINED;
6312 static void SetLevelSettings_SB(struct LevelInfo *level)
6316 level->use_step_counter = TRUE;
6319 level->score[SC_TIME_BONUS] = 0;
6320 level->time_score_base = 1;
6321 level->rate_time_over_score = TRUE;
6324 level->auto_exit_sokoban = TRUE;
6327 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6328 struct LevelFileInfo *level_file_info,
6329 boolean level_info_only)
6331 char *filename = level_file_info->filename;
6332 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6333 char last_comment[MAX_LINE_LEN];
6334 char level_name[MAX_LINE_LEN];
6337 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6338 boolean read_continued_line = FALSE;
6339 boolean reading_playfield = FALSE;
6340 boolean got_valid_playfield_line = FALSE;
6341 boolean invalid_playfield_char = FALSE;
6342 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6343 int file_level_nr = 0;
6344 int x = 0, y = 0; // initialized to make compilers happy
6346 last_comment[0] = '\0';
6347 level_name[0] = '\0';
6349 if (!(file = openFile(filename, MODE_READ)))
6351 level->no_valid_file = TRUE;
6353 if (!level_info_only)
6354 Warn("cannot read level '%s' -- using empty level", filename);
6359 while (!checkEndOfFile(file))
6361 // level successfully read, but next level may follow here
6362 if (!got_valid_playfield_line && reading_playfield)
6364 // read playfield from single level file -- skip remaining file
6365 if (!level_file_info->packed)
6368 if (file_level_nr >= num_levels_to_skip)
6373 last_comment[0] = '\0';
6374 level_name[0] = '\0';
6376 reading_playfield = FALSE;
6379 got_valid_playfield_line = FALSE;
6381 // read next line of input file
6382 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6385 // cut trailing line break (this can be newline and/or carriage return)
6386 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6387 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6390 // copy raw input line for later use (mainly debugging output)
6391 strcpy(line_raw, line);
6393 if (read_continued_line)
6395 // append new line to existing line, if there is enough space
6396 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6397 strcat(previous_line, line_ptr);
6399 strcpy(line, previous_line); // copy storage buffer to line
6401 read_continued_line = FALSE;
6404 // if the last character is '\', continue at next line
6405 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6407 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6408 strcpy(previous_line, line); // copy line to storage buffer
6410 read_continued_line = TRUE;
6416 if (line[0] == '\0')
6419 // extract comment text from comment line
6422 for (line_ptr = line; *line_ptr; line_ptr++)
6423 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6426 strcpy(last_comment, line_ptr);
6431 // extract level title text from line containing level title
6432 if (line[0] == '\'')
6434 strcpy(level_name, &line[1]);
6436 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6437 level_name[strlen(level_name) - 1] = '\0';
6442 // skip lines containing only spaces (or empty lines)
6443 for (line_ptr = line; *line_ptr; line_ptr++)
6444 if (*line_ptr != ' ')
6446 if (*line_ptr == '\0')
6449 // at this point, we have found a line containing part of a playfield
6451 got_valid_playfield_line = TRUE;
6453 if (!reading_playfield)
6455 reading_playfield = TRUE;
6456 invalid_playfield_char = FALSE;
6458 for (x = 0; x < MAX_LEV_FIELDX; x++)
6459 for (y = 0; y < MAX_LEV_FIELDY; y++)
6460 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6465 // start with topmost tile row
6469 // skip playfield line if larger row than allowed
6470 if (y >= MAX_LEV_FIELDY)
6473 // start with leftmost tile column
6476 // read playfield elements from line
6477 for (line_ptr = line; *line_ptr; line_ptr++)
6479 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6481 // stop parsing playfield line if larger column than allowed
6482 if (x >= MAX_LEV_FIELDX)
6485 if (mapped_sb_element == EL_UNDEFINED)
6487 invalid_playfield_char = TRUE;
6492 level->field[x][y] = mapped_sb_element;
6494 // continue with next tile column
6497 level->fieldx = MAX(x, level->fieldx);
6500 if (invalid_playfield_char)
6502 // if first playfield line, treat invalid lines as comment lines
6504 reading_playfield = FALSE;
6509 // continue with next tile row
6517 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6518 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6520 if (!reading_playfield)
6522 level->no_valid_file = TRUE;
6524 Warn("cannot read level '%s' -- using empty level", filename);
6529 if (*level_name != '\0')
6531 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6532 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6534 else if (*last_comment != '\0')
6536 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6537 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6541 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6544 // set all empty fields beyond the border walls to invisible steel wall
6545 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6547 if ((x == 0 || x == level->fieldx - 1 ||
6548 y == 0 || y == level->fieldy - 1) &&
6549 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6550 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6551 level->field, level->fieldx, level->fieldy);
6554 // set special level settings for Sokoban levels
6555 SetLevelSettings_SB(level);
6557 if (load_xsb_to_ces)
6559 // special global settings can now be set in level template
6560 level->use_custom_template = TRUE;
6565 // -------------------------------------------------------------------------
6566 // functions for handling native levels
6567 // -------------------------------------------------------------------------
6569 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6570 struct LevelFileInfo *level_file_info,
6571 boolean level_info_only)
6575 // determine position of requested level inside level package
6576 if (level_file_info->packed)
6577 pos = level_file_info->nr - leveldir_current->first_level;
6579 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6580 level->no_valid_file = TRUE;
6583 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6584 struct LevelFileInfo *level_file_info,
6585 boolean level_info_only)
6587 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6588 level->no_valid_file = TRUE;
6591 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6592 struct LevelFileInfo *level_file_info,
6593 boolean level_info_only)
6597 // determine position of requested level inside level package
6598 if (level_file_info->packed)
6599 pos = level_file_info->nr - leveldir_current->first_level;
6601 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6602 level->no_valid_file = TRUE;
6605 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6606 struct LevelFileInfo *level_file_info,
6607 boolean level_info_only)
6609 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6610 level->no_valid_file = TRUE;
6613 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6615 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6616 CopyNativeLevel_RND_to_BD(level);
6617 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6618 CopyNativeLevel_RND_to_EM(level);
6619 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6620 CopyNativeLevel_RND_to_SP(level);
6621 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6622 CopyNativeLevel_RND_to_MM(level);
6625 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6627 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6628 CopyNativeLevel_BD_to_RND(level);
6629 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6630 CopyNativeLevel_EM_to_RND(level);
6631 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6632 CopyNativeLevel_SP_to_RND(level);
6633 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6634 CopyNativeLevel_MM_to_RND(level);
6637 void SaveNativeLevel(struct LevelInfo *level)
6639 // saving native level files only supported for some game engines
6640 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6641 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6644 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6645 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6646 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6647 char *filename = getLevelFilenameFromBasename(basename);
6649 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6652 boolean success = FALSE;
6654 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6656 CopyNativeLevel_RND_to_BD(level);
6657 // CopyNativeTape_RND_to_BD(level);
6659 success = SaveNativeLevel_BD(filename);
6661 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6663 CopyNativeLevel_RND_to_SP(level);
6664 CopyNativeTape_RND_to_SP(level);
6666 success = SaveNativeLevel_SP(filename);
6670 Request("Native level file saved!", REQ_CONFIRM);
6672 Request("Failed to save native level file!", REQ_CONFIRM);
6676 // ----------------------------------------------------------------------------
6677 // functions for loading generic level
6678 // ----------------------------------------------------------------------------
6680 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6681 struct LevelFileInfo *level_file_info,
6682 boolean level_info_only)
6684 // always start with reliable default values
6685 setLevelInfoToDefaults(level, level_info_only, TRUE);
6687 switch (level_file_info->type)
6689 case LEVEL_FILE_TYPE_RND:
6690 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6693 case LEVEL_FILE_TYPE_BD:
6694 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6695 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6698 case LEVEL_FILE_TYPE_EM:
6699 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6700 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6703 case LEVEL_FILE_TYPE_SP:
6704 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6705 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6708 case LEVEL_FILE_TYPE_MM:
6709 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6710 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6713 case LEVEL_FILE_TYPE_DC:
6714 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6717 case LEVEL_FILE_TYPE_SB:
6718 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6722 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6726 // if level file is invalid, restore level structure to default values
6727 if (level->no_valid_file)
6728 setLevelInfoToDefaults(level, level_info_only, FALSE);
6730 if (check_special_flags("use_native_bd_game_engine"))
6731 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6733 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6734 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6736 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6737 CopyNativeLevel_Native_to_RND(level);
6740 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6742 static struct LevelFileInfo level_file_info;
6744 // always start with reliable default values
6745 setFileInfoToDefaults(&level_file_info);
6747 level_file_info.nr = 0; // unknown level number
6748 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6750 setString(&level_file_info.filename, filename);
6752 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6755 static void LoadLevel_InitVersion(struct LevelInfo *level)
6759 if (leveldir_current == NULL) // only when dumping level
6762 // all engine modifications also valid for levels which use latest engine
6763 if (level->game_version < VERSION_IDENT(3,2,0,5))
6765 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6766 level->time_score_base = 10;
6769 if (leveldir_current->latest_engine)
6771 // ---------- use latest game engine --------------------------------------
6773 /* For all levels which are forced to use the latest game engine version
6774 (normally all but user contributed, private and undefined levels), set
6775 the game engine version to the actual version; this allows for actual
6776 corrections in the game engine to take effect for existing, converted
6777 levels (from "classic" or other existing games) to make the emulation
6778 of the corresponding game more accurate, while (hopefully) not breaking
6779 existing levels created from other players. */
6781 level->game_version = GAME_VERSION_ACTUAL;
6783 /* Set special EM style gems behaviour: EM style gems slip down from
6784 normal, steel and growing wall. As this is a more fundamental change,
6785 it seems better to set the default behaviour to "off" (as it is more
6786 natural) and make it configurable in the level editor (as a property
6787 of gem style elements). Already existing converted levels (neither
6788 private nor contributed levels) are changed to the new behaviour. */
6790 if (level->file_version < FILE_VERSION_2_0)
6791 level->em_slippery_gems = TRUE;
6796 // ---------- use game engine the level was created with --------------------
6798 /* For all levels which are not forced to use the latest game engine
6799 version (normally user contributed, private and undefined levels),
6800 use the version of the game engine the levels were created for.
6802 Since 2.0.1, the game engine version is now directly stored
6803 in the level file (chunk "VERS"), so there is no need anymore
6804 to set the game version from the file version (except for old,
6805 pre-2.0 levels, where the game version is still taken from the
6806 file format version used to store the level -- see above). */
6808 // player was faster than enemies in 1.0.0 and before
6809 if (level->file_version == FILE_VERSION_1_0)
6810 for (i = 0; i < MAX_PLAYERS; i++)
6811 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6813 // default behaviour for EM style gems was "slippery" only in 2.0.1
6814 if (level->game_version == VERSION_IDENT(2,0,1,0))
6815 level->em_slippery_gems = TRUE;
6817 // springs could be pushed over pits before (pre-release version) 2.2.0
6818 if (level->game_version < VERSION_IDENT(2,2,0,0))
6819 level->use_spring_bug = TRUE;
6821 if (level->game_version < VERSION_IDENT(3,2,0,5))
6823 // time orb caused limited time in endless time levels before 3.2.0-5
6824 level->use_time_orb_bug = TRUE;
6826 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6827 level->block_snap_field = FALSE;
6829 // extra time score was same value as time left score before 3.2.0-5
6830 level->extra_time_score = level->score[SC_TIME_BONUS];
6833 if (level->game_version < VERSION_IDENT(3,2,0,7))
6835 // default behaviour for snapping was "not continuous" before 3.2.0-7
6836 level->continuous_snapping = FALSE;
6839 // only few elements were able to actively move into acid before 3.1.0
6840 // trigger settings did not exist before 3.1.0; set to default "any"
6841 if (level->game_version < VERSION_IDENT(3,1,0,0))
6843 // correct "can move into acid" settings (all zero in old levels)
6845 level->can_move_into_acid_bits = 0; // nothing can move into acid
6846 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6848 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6849 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6850 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6851 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6853 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6854 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6856 // correct trigger settings (stored as zero == "none" in old levels)
6858 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6860 int element = EL_CUSTOM_START + i;
6861 struct ElementInfo *ei = &element_info[element];
6863 for (j = 0; j < ei->num_change_pages; j++)
6865 struct ElementChangeInfo *change = &ei->change_page[j];
6867 change->trigger_player = CH_PLAYER_ANY;
6868 change->trigger_page = CH_PAGE_ANY;
6873 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6875 int element = EL_CUSTOM_256;
6876 struct ElementInfo *ei = &element_info[element];
6877 struct ElementChangeInfo *change = &ei->change_page[0];
6879 /* This is needed to fix a problem that was caused by a bugfix in function
6880 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6881 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6882 not replace walkable elements, but instead just placed the player on it,
6883 without placing the Sokoban field under the player). Unfortunately, this
6884 breaks "Snake Bite" style levels when the snake is halfway through a door
6885 that just closes (the snake head is still alive and can be moved in this
6886 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6887 player (without Sokoban element) which then gets killed as designed). */
6889 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6890 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6891 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6892 change->target_element = EL_PLAYER_1;
6895 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6896 if (level->game_version < VERSION_IDENT(3,2,5,0))
6898 /* This is needed to fix a problem that was caused by a bugfix in function
6899 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6900 corrects the behaviour when a custom element changes to another custom
6901 element with a higher element number that has change actions defined.
6902 Normally, only one change per frame is allowed for custom elements.
6903 Therefore, it is checked if a custom element already changed in the
6904 current frame; if it did, subsequent changes are suppressed.
6905 Unfortunately, this is only checked for element changes, but not for
6906 change actions, which are still executed. As the function above loops
6907 through all custom elements from lower to higher, an element change
6908 resulting in a lower CE number won't be checked again, while a target
6909 element with a higher number will also be checked, and potential change
6910 actions will get executed for this CE, too (which is wrong), while
6911 further changes are ignored (which is correct). As this bugfix breaks
6912 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6913 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6914 behaviour for existing levels and tapes that make use of this bug */
6916 level->use_action_after_change_bug = TRUE;
6919 // not centering level after relocating player was default only in 3.2.3
6920 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6921 level->shifted_relocation = TRUE;
6923 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6924 if (level->game_version < VERSION_IDENT(3,2,6,0))
6925 level->em_explodes_by_fire = TRUE;
6927 // levels were solved by the first player entering an exit up to 4.1.0.0
6928 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6929 level->solved_by_one_player = TRUE;
6931 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6932 if (level->game_version < VERSION_IDENT(4,1,1,1))
6933 level->use_life_bugs = TRUE;
6935 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6936 if (level->game_version < VERSION_IDENT(4,1,1,1))
6937 level->sb_objects_needed = FALSE;
6939 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6940 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6941 level->finish_dig_collect = FALSE;
6943 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6944 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6945 level->keep_walkable_ce = TRUE;
6948 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6950 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6953 // check if this level is (not) a Sokoban level
6954 for (y = 0; y < level->fieldy; y++)
6955 for (x = 0; x < level->fieldx; x++)
6956 if (!IS_SB_ELEMENT(Tile[x][y]))
6957 is_sokoban_level = FALSE;
6959 if (is_sokoban_level)
6961 // set special level settings for Sokoban levels
6962 SetLevelSettings_SB(level);
6966 static void LoadLevel_InitSettings(struct LevelInfo *level)
6968 // adjust level settings for (non-native) Sokoban-style levels
6969 LoadLevel_InitSettings_SB(level);
6971 // rename levels with title "nameless level" or if renaming is forced
6972 if (leveldir_current->empty_level_name != NULL &&
6973 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6974 leveldir_current->force_level_name))
6975 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6976 leveldir_current->empty_level_name, level_nr);
6979 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6983 // map elements that have changed in newer versions
6984 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6985 level->game_version);
6986 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6987 for (x = 0; x < 3; x++)
6988 for (y = 0; y < 3; y++)
6989 level->yamyam_content[i].e[x][y] =
6990 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6991 level->game_version);
6995 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6999 // map custom element change events that have changed in newer versions
7000 // (these following values were accidentally changed in version 3.0.1)
7001 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7002 if (level->game_version <= VERSION_IDENT(3,0,0,0))
7004 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7006 int element = EL_CUSTOM_START + i;
7008 // order of checking and copying events to be mapped is important
7009 // (do not change the start and end value -- they are constant)
7010 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7012 if (HAS_CHANGE_EVENT(element, j - 2))
7014 SET_CHANGE_EVENT(element, j - 2, FALSE);
7015 SET_CHANGE_EVENT(element, j, TRUE);
7019 // order of checking and copying events to be mapped is important
7020 // (do not change the start and end value -- they are constant)
7021 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7023 if (HAS_CHANGE_EVENT(element, j - 1))
7025 SET_CHANGE_EVENT(element, j - 1, FALSE);
7026 SET_CHANGE_EVENT(element, j, TRUE);
7032 // initialize "can_change" field for old levels with only one change page
7033 if (level->game_version <= VERSION_IDENT(3,0,2,0))
7035 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7037 int element = EL_CUSTOM_START + i;
7039 if (CAN_CHANGE(element))
7040 element_info[element].change->can_change = TRUE;
7044 // correct custom element values (for old levels without these options)
7045 if (level->game_version < VERSION_IDENT(3,1,1,0))
7047 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7049 int element = EL_CUSTOM_START + i;
7050 struct ElementInfo *ei = &element_info[element];
7052 if (ei->access_direction == MV_NO_DIRECTION)
7053 ei->access_direction = MV_ALL_DIRECTIONS;
7057 // correct custom element values (fix invalid values for all versions)
7060 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7062 int element = EL_CUSTOM_START + i;
7063 struct ElementInfo *ei = &element_info[element];
7065 for (j = 0; j < ei->num_change_pages; j++)
7067 struct ElementChangeInfo *change = &ei->change_page[j];
7069 if (change->trigger_player == CH_PLAYER_NONE)
7070 change->trigger_player = CH_PLAYER_ANY;
7072 if (change->trigger_side == CH_SIDE_NONE)
7073 change->trigger_side = CH_SIDE_ANY;
7078 // initialize "can_explode" field for old levels which did not store this
7079 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7080 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7082 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7084 int element = EL_CUSTOM_START + i;
7086 if (EXPLODES_1X1_OLD(element))
7087 element_info[element].explosion_type = EXPLODES_1X1;
7089 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7090 EXPLODES_SMASHED(element) ||
7091 EXPLODES_IMPACT(element)));
7095 // correct previously hard-coded move delay values for maze runner style
7096 if (level->game_version < VERSION_IDENT(3,1,1,0))
7098 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7100 int element = EL_CUSTOM_START + i;
7102 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7104 // previously hard-coded and therefore ignored
7105 element_info[element].move_delay_fixed = 9;
7106 element_info[element].move_delay_random = 0;
7111 // set some other uninitialized values of custom elements in older levels
7112 if (level->game_version < VERSION_IDENT(3,1,0,0))
7114 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7116 int element = EL_CUSTOM_START + i;
7118 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7120 element_info[element].explosion_delay = 17;
7121 element_info[element].ignition_delay = 8;
7125 // set mouse click change events to work for left/middle/right mouse button
7126 if (level->game_version < VERSION_IDENT(4,2,3,0))
7128 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7130 int element = EL_CUSTOM_START + i;
7131 struct ElementInfo *ei = &element_info[element];
7133 for (j = 0; j < ei->num_change_pages; j++)
7135 struct ElementChangeInfo *change = &ei->change_page[j];
7137 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7138 change->has_event[CE_PRESSED_BY_MOUSE] ||
7139 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7140 change->has_event[CE_MOUSE_PRESSED_ON_X])
7141 change->trigger_side = CH_SIDE_ANY;
7147 static void LoadLevel_InitElements(struct LevelInfo *level)
7149 LoadLevel_InitStandardElements(level);
7151 if (level->file_has_custom_elements)
7152 LoadLevel_InitCustomElements(level);
7154 // initialize element properties for level editor etc.
7155 InitElementPropertiesEngine(level->game_version);
7156 InitElementPropertiesGfxElement();
7159 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7163 // map elements that have changed in newer versions
7164 for (y = 0; y < level->fieldy; y++)
7165 for (x = 0; x < level->fieldx; x++)
7166 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7167 level->game_version);
7169 // clear unused playfield data (nicer if level gets resized in editor)
7170 for (x = 0; x < MAX_LEV_FIELDX; x++)
7171 for (y = 0; y < MAX_LEV_FIELDY; y++)
7172 if (x >= level->fieldx || y >= level->fieldy)
7173 level->field[x][y] = EL_EMPTY;
7175 // copy elements to runtime playfield array
7176 for (x = 0; x < MAX_LEV_FIELDX; x++)
7177 for (y = 0; y < MAX_LEV_FIELDY; y++)
7178 Tile[x][y] = level->field[x][y];
7180 // initialize level size variables for faster access
7181 lev_fieldx = level->fieldx;
7182 lev_fieldy = level->fieldy;
7184 // determine border element for this level
7185 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7186 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7191 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7193 struct LevelFileInfo *level_file_info = &level->file_info;
7195 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7196 CopyNativeLevel_RND_to_Native(level);
7199 static void LoadLevelTemplate_LoadAndInit(void)
7201 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7203 LoadLevel_InitVersion(&level_template);
7204 LoadLevel_InitElements(&level_template);
7205 LoadLevel_InitSettings(&level_template);
7207 ActivateLevelTemplate();
7210 void LoadLevelTemplate(int nr)
7212 if (!fileExists(getGlobalLevelTemplateFilename()))
7214 Warn("no level template found for this level");
7219 setLevelFileInfo(&level_template.file_info, nr);
7221 LoadLevelTemplate_LoadAndInit();
7224 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7226 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7228 LoadLevelTemplate_LoadAndInit();
7231 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7233 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7235 if (level.use_custom_template)
7237 if (network_level != NULL)
7238 LoadNetworkLevelTemplate(network_level);
7240 LoadLevelTemplate(-1);
7243 LoadLevel_InitVersion(&level);
7244 LoadLevel_InitElements(&level);
7245 LoadLevel_InitPlayfield(&level);
7246 LoadLevel_InitSettings(&level);
7248 LoadLevel_InitNativeEngines(&level);
7251 void LoadLevel(int nr)
7253 SetLevelSetInfo(leveldir_current->identifier, nr);
7255 setLevelFileInfo(&level.file_info, nr);
7257 LoadLevel_LoadAndInit(NULL);
7260 void LoadLevelInfoOnly(int nr)
7262 setLevelFileInfo(&level.file_info, nr);
7264 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7267 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7269 SetLevelSetInfo(network_level->leveldir_identifier,
7270 network_level->file_info.nr);
7272 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7274 LoadLevel_LoadAndInit(network_level);
7277 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7281 chunk_size += putFileVersion(file, level->file_version);
7282 chunk_size += putFileVersion(file, level->game_version);
7287 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7291 chunk_size += putFile16BitBE(file, level->creation_date.year);
7292 chunk_size += putFile8Bit(file, level->creation_date.month);
7293 chunk_size += putFile8Bit(file, level->creation_date.day);
7298 #if ENABLE_HISTORIC_CHUNKS
7299 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7303 putFile8Bit(file, level->fieldx);
7304 putFile8Bit(file, level->fieldy);
7306 putFile16BitBE(file, level->time);
7307 putFile16BitBE(file, level->gems_needed);
7309 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7310 putFile8Bit(file, level->name[i]);
7312 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7313 putFile8Bit(file, level->score[i]);
7315 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7316 for (y = 0; y < 3; y++)
7317 for (x = 0; x < 3; x++)
7318 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7319 level->yamyam_content[i].e[x][y]));
7320 putFile8Bit(file, level->amoeba_speed);
7321 putFile8Bit(file, level->time_magic_wall);
7322 putFile8Bit(file, level->time_wheel);
7323 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7324 level->amoeba_content));
7325 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7326 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7327 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7328 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7330 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7332 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7333 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7334 putFile32BitBE(file, level->can_move_into_acid_bits);
7335 putFile8Bit(file, level->dont_collide_with_bits);
7337 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7338 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7340 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7341 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7342 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7344 putFile8Bit(file, level->game_engine_type);
7346 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7350 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7355 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7356 chunk_size += putFile8Bit(file, level->name[i]);
7361 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7366 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7367 chunk_size += putFile8Bit(file, level->author[i]);
7372 #if ENABLE_HISTORIC_CHUNKS
7373 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7378 for (y = 0; y < level->fieldy; y++)
7379 for (x = 0; x < level->fieldx; x++)
7380 if (level->encoding_16bit_field)
7381 chunk_size += putFile16BitBE(file, level->field[x][y]);
7383 chunk_size += putFile8Bit(file, level->field[x][y]);
7389 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7394 for (y = 0; y < level->fieldy; y++)
7395 for (x = 0; x < level->fieldx; x++)
7396 chunk_size += putFile16BitBE(file, level->field[x][y]);
7401 #if ENABLE_HISTORIC_CHUNKS
7402 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7406 putFile8Bit(file, EL_YAMYAM);
7407 putFile8Bit(file, level->num_yamyam_contents);
7408 putFile8Bit(file, 0);
7409 putFile8Bit(file, 0);
7411 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7412 for (y = 0; y < 3; y++)
7413 for (x = 0; x < 3; x++)
7414 if (level->encoding_16bit_field)
7415 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7417 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7421 #if ENABLE_HISTORIC_CHUNKS
7422 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7425 int num_contents, content_xsize, content_ysize;
7426 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7428 if (element == EL_YAMYAM)
7430 num_contents = level->num_yamyam_contents;
7434 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7435 for (y = 0; y < 3; y++)
7436 for (x = 0; x < 3; x++)
7437 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7439 else if (element == EL_BD_AMOEBA)
7445 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7446 for (y = 0; y < 3; y++)
7447 for (x = 0; x < 3; x++)
7448 content_array[i][x][y] = EL_EMPTY;
7449 content_array[0][0][0] = level->amoeba_content;
7453 // chunk header already written -- write empty chunk data
7454 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7456 Warn("cannot save content for element '%d'", element);
7461 putFile16BitBE(file, element);
7462 putFile8Bit(file, num_contents);
7463 putFile8Bit(file, content_xsize);
7464 putFile8Bit(file, content_ysize);
7466 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7468 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7469 for (y = 0; y < 3; y++)
7470 for (x = 0; x < 3; x++)
7471 putFile16BitBE(file, content_array[i][x][y]);
7475 #if ENABLE_HISTORIC_CHUNKS
7476 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7478 int envelope_nr = element - EL_ENVELOPE_1;
7479 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7483 chunk_size += putFile16BitBE(file, element);
7484 chunk_size += putFile16BitBE(file, envelope_len);
7485 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7486 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7488 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7489 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7491 for (i = 0; i < envelope_len; i++)
7492 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7498 #if ENABLE_HISTORIC_CHUNKS
7499 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7500 int num_changed_custom_elements)
7504 putFile16BitBE(file, num_changed_custom_elements);
7506 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7508 int element = EL_CUSTOM_START + i;
7510 struct ElementInfo *ei = &element_info[element];
7512 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7514 if (check < num_changed_custom_elements)
7516 putFile16BitBE(file, element);
7517 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7524 if (check != num_changed_custom_elements) // should not happen
7525 Warn("inconsistent number of custom element properties");
7529 #if ENABLE_HISTORIC_CHUNKS
7530 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7531 int num_changed_custom_elements)
7535 putFile16BitBE(file, num_changed_custom_elements);
7537 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7539 int element = EL_CUSTOM_START + i;
7541 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7543 if (check < num_changed_custom_elements)
7545 putFile16BitBE(file, element);
7546 putFile16BitBE(file, element_info[element].change->target_element);
7553 if (check != num_changed_custom_elements) // should not happen
7554 Warn("inconsistent number of custom target elements");
7558 #if ENABLE_HISTORIC_CHUNKS
7559 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7560 int num_changed_custom_elements)
7562 int i, j, x, y, check = 0;
7564 putFile16BitBE(file, num_changed_custom_elements);
7566 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7568 int element = EL_CUSTOM_START + i;
7569 struct ElementInfo *ei = &element_info[element];
7571 if (ei->modified_settings)
7573 if (check < num_changed_custom_elements)
7575 putFile16BitBE(file, element);
7577 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7578 putFile8Bit(file, ei->description[j]);
7580 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7582 // some free bytes for future properties and padding
7583 WriteUnusedBytesToFile(file, 7);
7585 putFile8Bit(file, ei->use_gfx_element);
7586 putFile16BitBE(file, ei->gfx_element_initial);
7588 putFile8Bit(file, ei->collect_score_initial);
7589 putFile8Bit(file, ei->collect_count_initial);
7591 putFile16BitBE(file, ei->push_delay_fixed);
7592 putFile16BitBE(file, ei->push_delay_random);
7593 putFile16BitBE(file, ei->move_delay_fixed);
7594 putFile16BitBE(file, ei->move_delay_random);
7596 putFile16BitBE(file, ei->move_pattern);
7597 putFile8Bit(file, ei->move_direction_initial);
7598 putFile8Bit(file, ei->move_stepsize);
7600 for (y = 0; y < 3; y++)
7601 for (x = 0; x < 3; x++)
7602 putFile16BitBE(file, ei->content.e[x][y]);
7604 putFile32BitBE(file, ei->change->events);
7606 putFile16BitBE(file, ei->change->target_element);
7608 putFile16BitBE(file, ei->change->delay_fixed);
7609 putFile16BitBE(file, ei->change->delay_random);
7610 putFile16BitBE(file, ei->change->delay_frames);
7612 putFile16BitBE(file, ei->change->initial_trigger_element);
7614 putFile8Bit(file, ei->change->explode);
7615 putFile8Bit(file, ei->change->use_target_content);
7616 putFile8Bit(file, ei->change->only_if_complete);
7617 putFile8Bit(file, ei->change->use_random_replace);
7619 putFile8Bit(file, ei->change->random_percentage);
7620 putFile8Bit(file, ei->change->replace_when);
7622 for (y = 0; y < 3; y++)
7623 for (x = 0; x < 3; x++)
7624 putFile16BitBE(file, ei->change->content.e[x][y]);
7626 putFile8Bit(file, ei->slippery_type);
7628 // some free bytes for future properties and padding
7629 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7636 if (check != num_changed_custom_elements) // should not happen
7637 Warn("inconsistent number of custom element properties");
7641 #if ENABLE_HISTORIC_CHUNKS
7642 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7644 struct ElementInfo *ei = &element_info[element];
7647 // ---------- custom element base property values (96 bytes) ----------------
7649 putFile16BitBE(file, element);
7651 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7652 putFile8Bit(file, ei->description[i]);
7654 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7656 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7658 putFile8Bit(file, ei->num_change_pages);
7660 putFile16BitBE(file, ei->ce_value_fixed_initial);
7661 putFile16BitBE(file, ei->ce_value_random_initial);
7662 putFile8Bit(file, ei->use_last_ce_value);
7664 putFile8Bit(file, ei->use_gfx_element);
7665 putFile16BitBE(file, ei->gfx_element_initial);
7667 putFile8Bit(file, ei->collect_score_initial);
7668 putFile8Bit(file, ei->collect_count_initial);
7670 putFile8Bit(file, ei->drop_delay_fixed);
7671 putFile8Bit(file, ei->push_delay_fixed);
7672 putFile8Bit(file, ei->drop_delay_random);
7673 putFile8Bit(file, ei->push_delay_random);
7674 putFile16BitBE(file, ei->move_delay_fixed);
7675 putFile16BitBE(file, ei->move_delay_random);
7677 // bits 0 - 15 of "move_pattern" ...
7678 putFile16BitBE(file, ei->move_pattern & 0xffff);
7679 putFile8Bit(file, ei->move_direction_initial);
7680 putFile8Bit(file, ei->move_stepsize);
7682 putFile8Bit(file, ei->slippery_type);
7684 for (y = 0; y < 3; y++)
7685 for (x = 0; x < 3; x++)
7686 putFile16BitBE(file, ei->content.e[x][y]);
7688 putFile16BitBE(file, ei->move_enter_element);
7689 putFile16BitBE(file, ei->move_leave_element);
7690 putFile8Bit(file, ei->move_leave_type);
7692 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7693 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7695 putFile8Bit(file, ei->access_direction);
7697 putFile8Bit(file, ei->explosion_delay);
7698 putFile8Bit(file, ei->ignition_delay);
7699 putFile8Bit(file, ei->explosion_type);
7701 // some free bytes for future custom property values and padding
7702 WriteUnusedBytesToFile(file, 1);
7704 // ---------- change page property values (48 bytes) ------------------------
7706 for (i = 0; i < ei->num_change_pages; i++)
7708 struct ElementChangeInfo *change = &ei->change_page[i];
7709 unsigned int event_bits;
7711 // bits 0 - 31 of "has_event[]" ...
7713 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7714 if (change->has_event[j])
7715 event_bits |= (1u << j);
7716 putFile32BitBE(file, event_bits);
7718 putFile16BitBE(file, change->target_element);
7720 putFile16BitBE(file, change->delay_fixed);
7721 putFile16BitBE(file, change->delay_random);
7722 putFile16BitBE(file, change->delay_frames);
7724 putFile16BitBE(file, change->initial_trigger_element);
7726 putFile8Bit(file, change->explode);
7727 putFile8Bit(file, change->use_target_content);
7728 putFile8Bit(file, change->only_if_complete);
7729 putFile8Bit(file, change->use_random_replace);
7731 putFile8Bit(file, change->random_percentage);
7732 putFile8Bit(file, change->replace_when);
7734 for (y = 0; y < 3; y++)
7735 for (x = 0; x < 3; x++)
7736 putFile16BitBE(file, change->target_content.e[x][y]);
7738 putFile8Bit(file, change->can_change);
7740 putFile8Bit(file, change->trigger_side);
7742 putFile8Bit(file, change->trigger_player);
7743 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7744 log_2(change->trigger_page)));
7746 putFile8Bit(file, change->has_action);
7747 putFile8Bit(file, change->action_type);
7748 putFile8Bit(file, change->action_mode);
7749 putFile16BitBE(file, change->action_arg);
7751 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7753 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7754 if (change->has_event[j])
7755 event_bits |= (1u << (j - 32));
7756 putFile8Bit(file, event_bits);
7761 #if ENABLE_HISTORIC_CHUNKS
7762 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7764 struct ElementInfo *ei = &element_info[element];
7765 struct ElementGroupInfo *group = ei->group;
7768 putFile16BitBE(file, element);
7770 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7771 putFile8Bit(file, ei->description[i]);
7773 putFile8Bit(file, group->num_elements);
7775 putFile8Bit(file, ei->use_gfx_element);
7776 putFile16BitBE(file, ei->gfx_element_initial);
7778 putFile8Bit(file, group->choice_mode);
7780 // some free bytes for future values and padding
7781 WriteUnusedBytesToFile(file, 3);
7783 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7784 putFile16BitBE(file, group->element[i]);
7788 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7789 boolean write_element)
7791 int save_type = entry->save_type;
7792 int data_type = entry->data_type;
7793 int conf_type = entry->conf_type;
7794 int byte_mask = conf_type & CONF_MASK_BYTES;
7795 int element = entry->element;
7796 int default_value = entry->default_value;
7798 boolean modified = FALSE;
7800 if (byte_mask != CONF_MASK_MULTI_BYTES)
7802 void *value_ptr = entry->value;
7803 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7806 // check if any settings have been modified before saving them
7807 if (value != default_value)
7810 // do not save if explicitly told or if unmodified default settings
7811 if ((save_type == SAVE_CONF_NEVER) ||
7812 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7816 num_bytes += putFile16BitBE(file, element);
7818 num_bytes += putFile8Bit(file, conf_type);
7819 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7820 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7821 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7824 else if (data_type == TYPE_STRING)
7826 char *default_string = entry->default_string;
7827 char *string = (char *)(entry->value);
7828 int string_length = strlen(string);
7831 // check if any settings have been modified before saving them
7832 if (!strEqual(string, default_string))
7835 // do not save if explicitly told or if unmodified default settings
7836 if ((save_type == SAVE_CONF_NEVER) ||
7837 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7841 num_bytes += putFile16BitBE(file, element);
7843 num_bytes += putFile8Bit(file, conf_type);
7844 num_bytes += putFile16BitBE(file, string_length);
7846 for (i = 0; i < string_length; i++)
7847 num_bytes += putFile8Bit(file, string[i]);
7849 else if (data_type == TYPE_ELEMENT_LIST)
7851 int *element_array = (int *)(entry->value);
7852 int num_elements = *(int *)(entry->num_entities);
7855 // check if any settings have been modified before saving them
7856 for (i = 0; i < num_elements; i++)
7857 if (element_array[i] != default_value)
7860 // do not save if explicitly told or if unmodified default settings
7861 if ((save_type == SAVE_CONF_NEVER) ||
7862 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7866 num_bytes += putFile16BitBE(file, element);
7868 num_bytes += putFile8Bit(file, conf_type);
7869 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7871 for (i = 0; i < num_elements; i++)
7872 num_bytes += putFile16BitBE(file, element_array[i]);
7874 else if (data_type == TYPE_CONTENT_LIST)
7876 struct Content *content = (struct Content *)(entry->value);
7877 int num_contents = *(int *)(entry->num_entities);
7880 // check if any settings have been modified before saving them
7881 for (i = 0; i < num_contents; i++)
7882 for (y = 0; y < 3; y++)
7883 for (x = 0; x < 3; x++)
7884 if (content[i].e[x][y] != default_value)
7887 // do not save if explicitly told or if unmodified default settings
7888 if ((save_type == SAVE_CONF_NEVER) ||
7889 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7893 num_bytes += putFile16BitBE(file, element);
7895 num_bytes += putFile8Bit(file, conf_type);
7896 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7898 for (i = 0; i < num_contents; i++)
7899 for (y = 0; y < 3; y++)
7900 for (x = 0; x < 3; x++)
7901 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7907 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7912 li = *level; // copy level data into temporary buffer
7914 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7915 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7920 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7925 li = *level; // copy level data into temporary buffer
7927 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7928 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7933 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7935 int envelope_nr = element - EL_ENVELOPE_1;
7939 chunk_size += putFile16BitBE(file, element);
7941 // copy envelope data into temporary buffer
7942 xx_envelope = level->envelope[envelope_nr];
7944 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7945 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7950 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7952 struct ElementInfo *ei = &element_info[element];
7956 chunk_size += putFile16BitBE(file, element);
7958 xx_ei = *ei; // copy element data into temporary buffer
7960 // set default description string for this specific element
7961 strcpy(xx_default_description, getDefaultElementDescription(ei));
7963 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7964 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7966 for (i = 0; i < ei->num_change_pages; i++)
7968 struct ElementChangeInfo *change = &ei->change_page[i];
7970 xx_current_change_page = i;
7972 xx_change = *change; // copy change data into temporary buffer
7975 setEventBitsFromEventFlags(change);
7977 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7978 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7985 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7987 struct ElementInfo *ei = &element_info[element];
7988 struct ElementGroupInfo *group = ei->group;
7992 chunk_size += putFile16BitBE(file, element);
7994 xx_ei = *ei; // copy element data into temporary buffer
7995 xx_group = *group; // copy group data into temporary buffer
7997 // set default description string for this specific element
7998 strcpy(xx_default_description, getDefaultElementDescription(ei));
8000 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8001 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8006 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8008 struct ElementInfo *ei = &element_info[element];
8012 chunk_size += putFile16BitBE(file, element);
8014 xx_ei = *ei; // copy element data into temporary buffer
8016 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8017 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8022 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8023 boolean save_as_template)
8029 if (!(file = fopen(filename, MODE_WRITE)))
8031 Warn("cannot save level file '%s'", filename);
8036 level->file_version = FILE_VERSION_ACTUAL;
8037 level->game_version = GAME_VERSION_ACTUAL;
8039 level->creation_date = getCurrentDate();
8041 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8042 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8044 chunk_size = SaveLevel_VERS(NULL, level);
8045 putFileChunkBE(file, "VERS", chunk_size);
8046 SaveLevel_VERS(file, level);
8048 chunk_size = SaveLevel_DATE(NULL, level);
8049 putFileChunkBE(file, "DATE", chunk_size);
8050 SaveLevel_DATE(file, level);
8052 chunk_size = SaveLevel_NAME(NULL, level);
8053 putFileChunkBE(file, "NAME", chunk_size);
8054 SaveLevel_NAME(file, level);
8056 chunk_size = SaveLevel_AUTH(NULL, level);
8057 putFileChunkBE(file, "AUTH", chunk_size);
8058 SaveLevel_AUTH(file, level);
8060 chunk_size = SaveLevel_INFO(NULL, level);
8061 putFileChunkBE(file, "INFO", chunk_size);
8062 SaveLevel_INFO(file, level);
8064 chunk_size = SaveLevel_BODY(NULL, level);
8065 putFileChunkBE(file, "BODY", chunk_size);
8066 SaveLevel_BODY(file, level);
8068 chunk_size = SaveLevel_ELEM(NULL, level);
8069 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8071 putFileChunkBE(file, "ELEM", chunk_size);
8072 SaveLevel_ELEM(file, level);
8075 for (i = 0; i < NUM_ENVELOPES; i++)
8077 int element = EL_ENVELOPE_1 + i;
8079 chunk_size = SaveLevel_NOTE(NULL, level, element);
8080 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8082 putFileChunkBE(file, "NOTE", chunk_size);
8083 SaveLevel_NOTE(file, level, element);
8087 // if not using template level, check for non-default custom/group elements
8088 if (!level->use_custom_template || save_as_template)
8090 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8092 int element = EL_CUSTOM_START + i;
8094 chunk_size = SaveLevel_CUSX(NULL, level, element);
8095 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8097 putFileChunkBE(file, "CUSX", chunk_size);
8098 SaveLevel_CUSX(file, level, element);
8102 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8104 int element = EL_GROUP_START + i;
8106 chunk_size = SaveLevel_GRPX(NULL, level, element);
8107 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8109 putFileChunkBE(file, "GRPX", chunk_size);
8110 SaveLevel_GRPX(file, level, element);
8114 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8116 int element = GET_EMPTY_ELEMENT(i);
8118 chunk_size = SaveLevel_EMPX(NULL, level, element);
8119 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8121 putFileChunkBE(file, "EMPX", chunk_size);
8122 SaveLevel_EMPX(file, level, element);
8129 SetFilePermissions(filename, PERMS_PRIVATE);
8132 void SaveLevel(int nr)
8134 char *filename = getDefaultLevelFilename(nr);
8136 SaveLevelFromFilename(&level, filename, FALSE);
8139 void SaveLevelTemplate(void)
8141 char *filename = getLocalLevelTemplateFilename();
8143 SaveLevelFromFilename(&level, filename, TRUE);
8146 boolean SaveLevelChecked(int nr)
8148 char *filename = getDefaultLevelFilename(nr);
8149 boolean new_level = !fileExists(filename);
8150 boolean level_saved = FALSE;
8152 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8157 Request("Level saved!", REQ_CONFIRM);
8165 void DumpLevel(struct LevelInfo *level)
8167 if (level->no_level_file || level->no_valid_file)
8169 Warn("cannot dump -- no valid level file found");
8175 Print("Level xxx (file version %08d, game version %08d)\n",
8176 level->file_version, level->game_version);
8179 Print("Level author: '%s'\n", level->author);
8180 Print("Level title: '%s'\n", level->name);
8182 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8184 Print("Level time: %d seconds\n", level->time);
8185 Print("Gems needed: %d\n", level->gems_needed);
8187 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8188 Print("Time for wheel: %d seconds\n", level->time_wheel);
8189 Print("Time for light: %d seconds\n", level->time_light);
8190 Print("Time for timegate: %d seconds\n", level->time_timegate);
8192 Print("Amoeba speed: %d\n", level->amoeba_speed);
8195 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8196 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8197 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8198 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8199 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8200 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8206 for (i = 0; i < NUM_ENVELOPES; i++)
8208 char *text = level->envelope[i].text;
8209 int text_len = strlen(text);
8210 boolean has_text = FALSE;
8212 for (j = 0; j < text_len; j++)
8213 if (text[j] != ' ' && text[j] != '\n')
8219 Print("Envelope %d:\n'%s'\n", i + 1, text);
8227 void DumpLevels(void)
8229 static LevelDirTree *dumplevel_leveldir = NULL;
8231 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8232 global.dumplevel_leveldir);
8234 if (dumplevel_leveldir == NULL)
8235 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8237 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8238 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8239 Fail("no such level number: %d", global.dumplevel_level_nr);
8241 leveldir_current = dumplevel_leveldir;
8243 LoadLevel(global.dumplevel_level_nr);
8250 // ============================================================================
8251 // tape file functions
8252 // ============================================================================
8254 static void setTapeInfoToDefaults(void)
8258 // always start with reliable default values (empty tape)
8261 // default values (also for pre-1.2 tapes) with only the first player
8262 tape.player_participates[0] = TRUE;
8263 for (i = 1; i < MAX_PLAYERS; i++)
8264 tape.player_participates[i] = FALSE;
8266 // at least one (default: the first) player participates in every tape
8267 tape.num_participating_players = 1;
8269 tape.property_bits = TAPE_PROPERTY_NONE;
8271 tape.level_nr = level_nr;
8273 tape.changed = FALSE;
8274 tape.solved = FALSE;
8276 tape.recording = FALSE;
8277 tape.playing = FALSE;
8278 tape.pausing = FALSE;
8280 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8281 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8283 tape.no_info_chunk = TRUE;
8284 tape.no_valid_file = FALSE;
8287 static int getTapePosSize(struct TapeInfo *tape)
8289 int tape_pos_size = 0;
8291 if (tape->use_key_actions)
8292 tape_pos_size += tape->num_participating_players;
8294 if (tape->use_mouse_actions)
8295 tape_pos_size += 3; // x and y position and mouse button mask
8297 tape_pos_size += 1; // tape action delay value
8299 return tape_pos_size;
8302 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8304 tape->use_key_actions = FALSE;
8305 tape->use_mouse_actions = FALSE;
8307 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8308 tape->use_key_actions = TRUE;
8310 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8311 tape->use_mouse_actions = TRUE;
8314 static int getTapeActionValue(struct TapeInfo *tape)
8316 return (tape->use_key_actions &&
8317 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8318 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8319 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8320 TAPE_ACTIONS_DEFAULT);
8323 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8325 tape->file_version = getFileVersion(file);
8326 tape->game_version = getFileVersion(file);
8331 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8335 tape->random_seed = getFile32BitBE(file);
8336 tape->date = getFile32BitBE(file);
8337 tape->length = getFile32BitBE(file);
8339 // read header fields that are new since version 1.2
8340 if (tape->file_version >= FILE_VERSION_1_2)
8342 byte store_participating_players = getFile8Bit(file);
8345 // since version 1.2, tapes store which players participate in the tape
8346 tape->num_participating_players = 0;
8347 for (i = 0; i < MAX_PLAYERS; i++)
8349 tape->player_participates[i] = FALSE;
8351 if (store_participating_players & (1 << i))
8353 tape->player_participates[i] = TRUE;
8354 tape->num_participating_players++;
8358 setTapeActionFlags(tape, getFile8Bit(file));
8360 tape->property_bits = getFile8Bit(file);
8361 tape->solved = getFile8Bit(file);
8363 engine_version = getFileVersion(file);
8364 if (engine_version > 0)
8365 tape->engine_version = engine_version;
8367 tape->engine_version = tape->game_version;
8373 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8375 tape->scr_fieldx = getFile8Bit(file);
8376 tape->scr_fieldy = getFile8Bit(file);
8381 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8383 char *level_identifier = NULL;
8384 int level_identifier_size;
8387 tape->no_info_chunk = FALSE;
8389 level_identifier_size = getFile16BitBE(file);
8391 level_identifier = checked_malloc(level_identifier_size);
8393 for (i = 0; i < level_identifier_size; i++)
8394 level_identifier[i] = getFile8Bit(file);
8396 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8397 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8399 checked_free(level_identifier);
8401 tape->level_nr = getFile16BitBE(file);
8403 chunk_size = 2 + level_identifier_size + 2;
8408 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8411 int tape_pos_size = getTapePosSize(tape);
8412 int chunk_size_expected = tape_pos_size * tape->length;
8414 if (chunk_size_expected != chunk_size)
8416 ReadUnusedBytesFromFile(file, chunk_size);
8417 return chunk_size_expected;
8420 for (i = 0; i < tape->length; i++)
8422 if (i >= MAX_TAPE_LEN)
8424 Warn("tape truncated -- size exceeds maximum tape size %d",
8427 // tape too large; read and ignore remaining tape data from this chunk
8428 for (;i < tape->length; i++)
8429 ReadUnusedBytesFromFile(file, tape_pos_size);
8434 if (tape->use_key_actions)
8436 for (j = 0; j < MAX_PLAYERS; j++)
8438 tape->pos[i].action[j] = MV_NONE;
8440 if (tape->player_participates[j])
8441 tape->pos[i].action[j] = getFile8Bit(file);
8445 if (tape->use_mouse_actions)
8447 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8448 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8449 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8452 tape->pos[i].delay = getFile8Bit(file);
8454 if (tape->file_version == FILE_VERSION_1_0)
8456 // eliminate possible diagonal moves in old tapes
8457 // this is only for backward compatibility
8459 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8460 byte action = tape->pos[i].action[0];
8461 int k, num_moves = 0;
8463 for (k = 0; k < 4; k++)
8465 if (action & joy_dir[k])
8467 tape->pos[i + num_moves].action[0] = joy_dir[k];
8469 tape->pos[i + num_moves].delay = 0;
8478 tape->length += num_moves;
8481 else if (tape->file_version < FILE_VERSION_2_0)
8483 // convert pre-2.0 tapes to new tape format
8485 if (tape->pos[i].delay > 1)
8488 tape->pos[i + 1] = tape->pos[i];
8489 tape->pos[i + 1].delay = 1;
8492 for (j = 0; j < MAX_PLAYERS; j++)
8493 tape->pos[i].action[j] = MV_NONE;
8494 tape->pos[i].delay--;
8501 if (checkEndOfFile(file))
8505 if (i != tape->length)
8506 chunk_size = tape_pos_size * i;
8511 static void LoadTape_SokobanSolution(char *filename)
8514 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8516 if (!(file = openFile(filename, MODE_READ)))
8518 tape.no_valid_file = TRUE;
8523 while (!checkEndOfFile(file))
8525 unsigned char c = getByteFromFile(file);
8527 if (checkEndOfFile(file))
8534 tape.pos[tape.length].action[0] = MV_UP;
8535 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8541 tape.pos[tape.length].action[0] = MV_DOWN;
8542 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8548 tape.pos[tape.length].action[0] = MV_LEFT;
8549 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8555 tape.pos[tape.length].action[0] = MV_RIGHT;
8556 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8564 // ignore white-space characters
8568 tape.no_valid_file = TRUE;
8570 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8578 if (tape.no_valid_file)
8581 tape.length_frames = GetTapeLengthFrames();
8582 tape.length_seconds = GetTapeLengthSeconds();
8585 void LoadTapeFromFilename(char *filename)
8587 char cookie[MAX_LINE_LEN];
8588 char chunk_name[CHUNK_ID_LEN + 1];
8592 // always start with reliable default values
8593 setTapeInfoToDefaults();
8595 if (strSuffix(filename, ".sln"))
8597 LoadTape_SokobanSolution(filename);
8602 if (!(file = openFile(filename, MODE_READ)))
8604 tape.no_valid_file = TRUE;
8609 getFileChunkBE(file, chunk_name, NULL);
8610 if (strEqual(chunk_name, "RND1"))
8612 getFile32BitBE(file); // not used
8614 getFileChunkBE(file, chunk_name, NULL);
8615 if (!strEqual(chunk_name, "TAPE"))
8617 tape.no_valid_file = TRUE;
8619 Warn("unknown format of tape file '%s'", filename);
8626 else // check for pre-2.0 file format with cookie string
8628 strcpy(cookie, chunk_name);
8629 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8631 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8632 cookie[strlen(cookie) - 1] = '\0';
8634 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8636 tape.no_valid_file = TRUE;
8638 Warn("unknown format of tape file '%s'", filename);
8645 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8647 tape.no_valid_file = TRUE;
8649 Warn("unsupported version of tape file '%s'", filename);
8656 // pre-2.0 tape files have no game version, so use file version here
8657 tape.game_version = tape.file_version;
8660 if (tape.file_version < FILE_VERSION_1_2)
8662 // tape files from versions before 1.2.0 without chunk structure
8663 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8664 LoadTape_BODY(file, 2 * tape.length, &tape);
8672 int (*loader)(File *, int, struct TapeInfo *);
8676 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8677 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8678 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8679 { "INFO", -1, LoadTape_INFO },
8680 { "BODY", -1, LoadTape_BODY },
8684 while (getFileChunkBE(file, chunk_name, &chunk_size))
8688 while (chunk_info[i].name != NULL &&
8689 !strEqual(chunk_name, chunk_info[i].name))
8692 if (chunk_info[i].name == NULL)
8694 Warn("unknown chunk '%s' in tape file '%s'",
8695 chunk_name, filename);
8697 ReadUnusedBytesFromFile(file, chunk_size);
8699 else if (chunk_info[i].size != -1 &&
8700 chunk_info[i].size != chunk_size)
8702 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8703 chunk_size, chunk_name, filename);
8705 ReadUnusedBytesFromFile(file, chunk_size);
8709 // call function to load this tape chunk
8710 int chunk_size_expected =
8711 (chunk_info[i].loader)(file, chunk_size, &tape);
8713 // the size of some chunks cannot be checked before reading other
8714 // chunks first (like "HEAD" and "BODY") that contain some header
8715 // information, so check them here
8716 if (chunk_size_expected != chunk_size)
8718 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8719 chunk_size, chunk_name, filename);
8727 tape.length_frames = GetTapeLengthFrames();
8728 tape.length_seconds = GetTapeLengthSeconds();
8731 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8733 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8735 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8736 tape.engine_version);
8740 void LoadTape(int nr)
8742 char *filename = getTapeFilename(nr);
8744 LoadTapeFromFilename(filename);
8747 void LoadSolutionTape(int nr)
8749 char *filename = getSolutionTapeFilename(nr);
8751 LoadTapeFromFilename(filename);
8753 if (TAPE_IS_EMPTY(tape))
8755 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8756 level.native_bd_level->replay != NULL)
8757 CopyNativeTape_BD_to_RND(&level);
8758 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8759 level.native_sp_level->demo.is_available)
8760 CopyNativeTape_SP_to_RND(&level);
8764 void LoadScoreTape(char *score_tape_basename, int nr)
8766 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8768 LoadTapeFromFilename(filename);
8771 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8773 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8775 LoadTapeFromFilename(filename);
8778 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8780 // chunk required for team mode tapes with non-default screen size
8781 return (tape->num_participating_players > 1 &&
8782 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8783 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8786 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8788 putFileVersion(file, tape->file_version);
8789 putFileVersion(file, tape->game_version);
8792 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8795 byte store_participating_players = 0;
8797 // set bits for participating players for compact storage
8798 for (i = 0; i < MAX_PLAYERS; i++)
8799 if (tape->player_participates[i])
8800 store_participating_players |= (1 << i);
8802 putFile32BitBE(file, tape->random_seed);
8803 putFile32BitBE(file, tape->date);
8804 putFile32BitBE(file, tape->length);
8806 putFile8Bit(file, store_participating_players);
8808 putFile8Bit(file, getTapeActionValue(tape));
8810 putFile8Bit(file, tape->property_bits);
8811 putFile8Bit(file, tape->solved);
8813 putFileVersion(file, tape->engine_version);
8816 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8818 putFile8Bit(file, tape->scr_fieldx);
8819 putFile8Bit(file, tape->scr_fieldy);
8822 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8824 int level_identifier_size = strlen(tape->level_identifier) + 1;
8827 putFile16BitBE(file, level_identifier_size);
8829 for (i = 0; i < level_identifier_size; i++)
8830 putFile8Bit(file, tape->level_identifier[i]);
8832 putFile16BitBE(file, tape->level_nr);
8835 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8839 for (i = 0; i < tape->length; i++)
8841 if (tape->use_key_actions)
8843 for (j = 0; j < MAX_PLAYERS; j++)
8844 if (tape->player_participates[j])
8845 putFile8Bit(file, tape->pos[i].action[j]);
8848 if (tape->use_mouse_actions)
8850 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8851 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8852 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8855 putFile8Bit(file, tape->pos[i].delay);
8859 void SaveTapeToFilename(char *filename)
8863 int info_chunk_size;
8864 int body_chunk_size;
8866 if (!(file = fopen(filename, MODE_WRITE)))
8868 Warn("cannot save level recording file '%s'", filename);
8873 tape_pos_size = getTapePosSize(&tape);
8875 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8876 body_chunk_size = tape_pos_size * tape.length;
8878 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8879 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8881 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8882 SaveTape_VERS(file, &tape);
8884 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8885 SaveTape_HEAD(file, &tape);
8887 if (checkSaveTape_SCRN(&tape))
8889 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8890 SaveTape_SCRN(file, &tape);
8893 putFileChunkBE(file, "INFO", info_chunk_size);
8894 SaveTape_INFO(file, &tape);
8896 putFileChunkBE(file, "BODY", body_chunk_size);
8897 SaveTape_BODY(file, &tape);
8901 SetFilePermissions(filename, PERMS_PRIVATE);
8904 static void SaveTapeExt(char *filename)
8908 tape.file_version = FILE_VERSION_ACTUAL;
8909 tape.game_version = GAME_VERSION_ACTUAL;
8911 tape.num_participating_players = 0;
8913 // count number of participating players
8914 for (i = 0; i < MAX_PLAYERS; i++)
8915 if (tape.player_participates[i])
8916 tape.num_participating_players++;
8918 SaveTapeToFilename(filename);
8920 tape.changed = FALSE;
8923 void SaveTape(int nr)
8925 char *filename = getTapeFilename(nr);
8927 InitTapeDirectory(leveldir_current->subdir);
8929 SaveTapeExt(filename);
8932 void SaveScoreTape(int nr)
8934 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8936 // used instead of "leveldir_current->subdir" (for network games)
8937 InitScoreTapeDirectory(levelset.identifier, nr);
8939 SaveTapeExt(filename);
8942 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8943 unsigned int req_state_added)
8945 char *filename = getTapeFilename(nr);
8946 boolean new_tape = !fileExists(filename);
8947 boolean tape_saved = FALSE;
8949 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8954 Request(msg_saved, REQ_CONFIRM | req_state_added);
8962 boolean SaveTapeChecked(int nr)
8964 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8967 boolean SaveTapeChecked_LevelSolved(int nr)
8969 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8970 "Level solved! Tape saved!", REQ_STAY_OPEN);
8973 void DumpTape(struct TapeInfo *tape)
8975 int tape_frame_counter;
8978 if (tape->no_valid_file)
8980 Warn("cannot dump -- no valid tape file found");
8987 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8988 tape->level_nr, tape->file_version, tape->game_version);
8989 Print(" (effective engine version %08d)\n",
8990 tape->engine_version);
8991 Print("Level series identifier: '%s'\n", tape->level_identifier);
8993 Print("Solution tape: %s\n",
8994 tape->solved ? "yes" :
8995 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8997 Print("Special tape properties: ");
8998 if (tape->property_bits == TAPE_PROPERTY_NONE)
9000 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9001 Print("[em_random_bug]");
9002 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9003 Print("[game_speed]");
9004 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9006 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9007 Print("[single_step]");
9008 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9009 Print("[snapshot]");
9010 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9011 Print("[replayed]");
9012 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9013 Print("[tas_keys]");
9014 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9015 Print("[small_graphics]");
9018 int year2 = tape->date / 10000;
9019 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9020 int month_index_raw = (tape->date / 100) % 100;
9021 int month_index = month_index_raw % 12; // prevent invalid index
9022 int month = month_index + 1;
9023 int day = tape->date % 100;
9025 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9029 tape_frame_counter = 0;
9031 for (i = 0; i < tape->length; i++)
9033 if (i >= MAX_TAPE_LEN)
9038 for (j = 0; j < MAX_PLAYERS; j++)
9040 if (tape->player_participates[j])
9042 int action = tape->pos[i].action[j];
9044 Print("%d:%02x ", j, action);
9045 Print("[%c%c%c%c|%c%c] - ",
9046 (action & JOY_LEFT ? '<' : ' '),
9047 (action & JOY_RIGHT ? '>' : ' '),
9048 (action & JOY_UP ? '^' : ' '),
9049 (action & JOY_DOWN ? 'v' : ' '),
9050 (action & JOY_BUTTON_1 ? '1' : ' '),
9051 (action & JOY_BUTTON_2 ? '2' : ' '));
9055 Print("(%03d) ", tape->pos[i].delay);
9056 Print("[%05d]\n", tape_frame_counter);
9058 tape_frame_counter += tape->pos[i].delay;
9064 void DumpTapes(void)
9066 static LevelDirTree *dumptape_leveldir = NULL;
9068 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9069 global.dumptape_leveldir);
9071 if (dumptape_leveldir == NULL)
9072 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9074 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9075 global.dumptape_level_nr > dumptape_leveldir->last_level)
9076 Fail("no such level number: %d", global.dumptape_level_nr);
9078 leveldir_current = dumptape_leveldir;
9080 if (options.mytapes)
9081 LoadTape(global.dumptape_level_nr);
9083 LoadSolutionTape(global.dumptape_level_nr);
9091 // ============================================================================
9092 // score file functions
9093 // ============================================================================
9095 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9099 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9101 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9102 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9103 scores->entry[i].score = 0;
9104 scores->entry[i].time = 0;
9106 scores->entry[i].id = -1;
9107 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9108 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9109 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9110 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9111 strcpy(scores->entry[i].country_code, "??");
9114 scores->num_entries = 0;
9115 scores->last_added = -1;
9116 scores->last_added_local = -1;
9118 scores->updated = FALSE;
9119 scores->uploaded = FALSE;
9120 scores->tape_downloaded = FALSE;
9121 scores->force_last_added = FALSE;
9123 // The following values are intentionally not reset here:
9127 // - continue_playing
9128 // - continue_on_return
9131 static void setScoreInfoToDefaults(void)
9133 setScoreInfoToDefaultsExt(&scores);
9136 static void setServerScoreInfoToDefaults(void)
9138 setScoreInfoToDefaultsExt(&server_scores);
9141 static void LoadScore_OLD(int nr)
9144 char *filename = getScoreFilename(nr);
9145 char cookie[MAX_LINE_LEN];
9146 char line[MAX_LINE_LEN];
9150 if (!(file = fopen(filename, MODE_READ)))
9153 // check file identifier
9154 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9156 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9157 cookie[strlen(cookie) - 1] = '\0';
9159 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9161 Warn("unknown format of score file '%s'", filename);
9168 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9170 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9171 Warn("fscanf() failed; %s", strerror(errno));
9173 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9176 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9177 line[strlen(line) - 1] = '\0';
9179 for (line_ptr = line; *line_ptr; line_ptr++)
9181 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9183 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9184 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9193 static void ConvertScore_OLD(void)
9195 // only convert score to time for levels that rate playing time over score
9196 if (!level.rate_time_over_score)
9199 // convert old score to playing time for score-less levels (like Supaplex)
9200 int time_final_max = 999;
9203 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9205 int score = scores.entry[i].score;
9207 if (score > 0 && score < time_final_max)
9208 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9212 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9214 scores->file_version = getFileVersion(file);
9215 scores->game_version = getFileVersion(file);
9220 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9222 char *level_identifier = NULL;
9223 int level_identifier_size;
9226 level_identifier_size = getFile16BitBE(file);
9228 level_identifier = checked_malloc(level_identifier_size);
9230 for (i = 0; i < level_identifier_size; i++)
9231 level_identifier[i] = getFile8Bit(file);
9233 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9234 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9236 checked_free(level_identifier);
9238 scores->level_nr = getFile16BitBE(file);
9239 scores->num_entries = getFile16BitBE(file);
9241 chunk_size = 2 + level_identifier_size + 2 + 2;
9246 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9250 for (i = 0; i < scores->num_entries; i++)
9252 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9253 scores->entry[i].name[j] = getFile8Bit(file);
9255 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9258 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9263 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9267 for (i = 0; i < scores->num_entries; i++)
9268 scores->entry[i].score = getFile16BitBE(file);
9270 chunk_size = scores->num_entries * 2;
9275 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9279 for (i = 0; i < scores->num_entries; i++)
9280 scores->entry[i].score = getFile32BitBE(file);
9282 chunk_size = scores->num_entries * 4;
9287 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9291 for (i = 0; i < scores->num_entries; i++)
9292 scores->entry[i].time = getFile32BitBE(file);
9294 chunk_size = scores->num_entries * 4;
9299 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9303 for (i = 0; i < scores->num_entries; i++)
9305 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9306 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9308 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9311 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9316 void LoadScore(int nr)
9318 char *filename = getScoreFilename(nr);
9319 char cookie[MAX_LINE_LEN];
9320 char chunk_name[CHUNK_ID_LEN + 1];
9322 boolean old_score_file_format = FALSE;
9325 // always start with reliable default values
9326 setScoreInfoToDefaults();
9328 if (!(file = openFile(filename, MODE_READ)))
9331 getFileChunkBE(file, chunk_name, NULL);
9332 if (strEqual(chunk_name, "RND1"))
9334 getFile32BitBE(file); // not used
9336 getFileChunkBE(file, chunk_name, NULL);
9337 if (!strEqual(chunk_name, "SCOR"))
9339 Warn("unknown format of score file '%s'", filename);
9346 else // check for old file format with cookie string
9348 strcpy(cookie, chunk_name);
9349 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9351 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9352 cookie[strlen(cookie) - 1] = '\0';
9354 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9356 Warn("unknown format of score file '%s'", filename);
9363 old_score_file_format = TRUE;
9366 if (old_score_file_format)
9368 // score files from versions before 4.2.4.0 without chunk structure
9371 // convert score to time, if possible (mainly for Supaplex levels)
9380 int (*loader)(File *, int, struct ScoreInfo *);
9384 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9385 { "INFO", -1, LoadScore_INFO },
9386 { "NAME", -1, LoadScore_NAME },
9387 { "SCOR", -1, LoadScore_SCOR },
9388 { "SC4R", -1, LoadScore_SC4R },
9389 { "TIME", -1, LoadScore_TIME },
9390 { "TAPE", -1, LoadScore_TAPE },
9395 while (getFileChunkBE(file, chunk_name, &chunk_size))
9399 while (chunk_info[i].name != NULL &&
9400 !strEqual(chunk_name, chunk_info[i].name))
9403 if (chunk_info[i].name == NULL)
9405 Warn("unknown chunk '%s' in score file '%s'",
9406 chunk_name, filename);
9408 ReadUnusedBytesFromFile(file, chunk_size);
9410 else if (chunk_info[i].size != -1 &&
9411 chunk_info[i].size != chunk_size)
9413 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9414 chunk_size, chunk_name, filename);
9416 ReadUnusedBytesFromFile(file, chunk_size);
9420 // call function to load this score chunk
9421 int chunk_size_expected =
9422 (chunk_info[i].loader)(file, chunk_size, &scores);
9424 // the size of some chunks cannot be checked before reading other
9425 // chunks first (like "HEAD" and "BODY") that contain some header
9426 // information, so check them here
9427 if (chunk_size_expected != chunk_size)
9429 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9430 chunk_size, chunk_name, filename);
9439 #if ENABLE_HISTORIC_CHUNKS
9440 void SaveScore_OLD(int nr)
9443 char *filename = getScoreFilename(nr);
9446 // used instead of "leveldir_current->subdir" (for network games)
9447 InitScoreDirectory(levelset.identifier);
9449 if (!(file = fopen(filename, MODE_WRITE)))
9451 Warn("cannot save score for level %d", nr);
9456 fprintf(file, "%s\n\n", SCORE_COOKIE);
9458 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9459 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9463 SetFilePermissions(filename, PERMS_PRIVATE);
9467 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9469 putFileVersion(file, scores->file_version);
9470 putFileVersion(file, scores->game_version);
9473 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9475 int level_identifier_size = strlen(scores->level_identifier) + 1;
9478 putFile16BitBE(file, level_identifier_size);
9480 for (i = 0; i < level_identifier_size; i++)
9481 putFile8Bit(file, scores->level_identifier[i]);
9483 putFile16BitBE(file, scores->level_nr);
9484 putFile16BitBE(file, scores->num_entries);
9487 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9491 for (i = 0; i < scores->num_entries; i++)
9493 int name_size = strlen(scores->entry[i].name);
9495 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9496 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9500 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9504 for (i = 0; i < scores->num_entries; i++)
9505 putFile16BitBE(file, scores->entry[i].score);
9508 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9512 for (i = 0; i < scores->num_entries; i++)
9513 putFile32BitBE(file, scores->entry[i].score);
9516 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9520 for (i = 0; i < scores->num_entries; i++)
9521 putFile32BitBE(file, scores->entry[i].time);
9524 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9528 for (i = 0; i < scores->num_entries; i++)
9530 int size = strlen(scores->entry[i].tape_basename);
9532 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9533 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9537 static void SaveScoreToFilename(char *filename)
9540 int info_chunk_size;
9541 int name_chunk_size;
9542 int scor_chunk_size;
9543 int sc4r_chunk_size;
9544 int time_chunk_size;
9545 int tape_chunk_size;
9546 boolean has_large_score_values;
9549 if (!(file = fopen(filename, MODE_WRITE)))
9551 Warn("cannot save score file '%s'", filename);
9556 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9557 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9558 scor_chunk_size = scores.num_entries * 2;
9559 sc4r_chunk_size = scores.num_entries * 4;
9560 time_chunk_size = scores.num_entries * 4;
9561 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9563 has_large_score_values = FALSE;
9564 for (i = 0; i < scores.num_entries; i++)
9565 if (scores.entry[i].score > 0xffff)
9566 has_large_score_values = TRUE;
9568 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9569 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9571 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9572 SaveScore_VERS(file, &scores);
9574 putFileChunkBE(file, "INFO", info_chunk_size);
9575 SaveScore_INFO(file, &scores);
9577 putFileChunkBE(file, "NAME", name_chunk_size);
9578 SaveScore_NAME(file, &scores);
9580 if (has_large_score_values)
9582 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9583 SaveScore_SC4R(file, &scores);
9587 putFileChunkBE(file, "SCOR", scor_chunk_size);
9588 SaveScore_SCOR(file, &scores);
9591 putFileChunkBE(file, "TIME", time_chunk_size);
9592 SaveScore_TIME(file, &scores);
9594 putFileChunkBE(file, "TAPE", tape_chunk_size);
9595 SaveScore_TAPE(file, &scores);
9599 SetFilePermissions(filename, PERMS_PRIVATE);
9602 void SaveScore(int nr)
9604 char *filename = getScoreFilename(nr);
9607 // used instead of "leveldir_current->subdir" (for network games)
9608 InitScoreDirectory(levelset.identifier);
9610 scores.file_version = FILE_VERSION_ACTUAL;
9611 scores.game_version = GAME_VERSION_ACTUAL;
9613 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9614 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9615 scores.level_nr = level_nr;
9617 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9618 if (scores.entry[i].score == 0 &&
9619 scores.entry[i].time == 0 &&
9620 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9623 scores.num_entries = i;
9625 if (scores.num_entries == 0)
9628 SaveScoreToFilename(filename);
9631 static void LoadServerScoreFromCache(int nr)
9633 struct ScoreEntry score_entry;
9642 { &score_entry.score, FALSE, 0 },
9643 { &score_entry.time, FALSE, 0 },
9644 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9645 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9646 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9647 { &score_entry.id, FALSE, 0 },
9648 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9649 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9650 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9651 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9655 char *filename = getScoreCacheFilename(nr);
9656 SetupFileHash *score_hash = loadSetupFileHash(filename);
9659 server_scores.num_entries = 0;
9661 if (score_hash == NULL)
9664 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9666 score_entry = server_scores.entry[i];
9668 for (j = 0; score_mapping[j].value != NULL; j++)
9672 sprintf(token, "%02d.%d", i, j);
9674 char *value = getHashEntry(score_hash, token);
9679 if (score_mapping[j].is_string)
9681 char *score_value = (char *)score_mapping[j].value;
9682 int value_size = score_mapping[j].string_size;
9684 strncpy(score_value, value, value_size);
9685 score_value[value_size] = '\0';
9689 int *score_value = (int *)score_mapping[j].value;
9691 *score_value = atoi(value);
9694 server_scores.num_entries = i + 1;
9697 server_scores.entry[i] = score_entry;
9700 freeSetupFileHash(score_hash);
9703 void LoadServerScore(int nr, boolean download_score)
9705 if (!setup.use_api_server)
9708 // always start with reliable default values
9709 setServerScoreInfoToDefaults();
9711 // 1st step: load server scores from cache file (which may not exist)
9712 // (this should prevent reading it while the thread is writing to it)
9713 LoadServerScoreFromCache(nr);
9715 if (download_score && runtime.use_api_server)
9717 // 2nd step: download server scores from score server to cache file
9718 // (as thread, as it might time out if the server is not reachable)
9719 ApiGetScoreAsThread(nr);
9723 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9725 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9727 // if score tape not uploaded, ask for uploading missing tapes later
9728 if (!setup.has_remaining_tapes)
9729 setup.ask_for_remaining_tapes = TRUE;
9731 setup.provide_uploading_tapes = TRUE;
9732 setup.has_remaining_tapes = TRUE;
9734 SaveSetup_ServerSetup();
9737 void SaveServerScore(int nr, boolean tape_saved)
9739 if (!runtime.use_api_server)
9741 PrepareScoreTapesForUpload(leveldir_current->subdir);
9746 ApiAddScoreAsThread(nr, tape_saved, NULL);
9749 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9750 char *score_tape_filename)
9752 if (!runtime.use_api_server)
9755 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9758 void LoadLocalAndServerScore(int nr, boolean download_score)
9760 int last_added_local = scores.last_added_local;
9761 boolean force_last_added = scores.force_last_added;
9763 // needed if only showing server scores
9764 setScoreInfoToDefaults();
9766 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9769 // restore last added local score entry (before merging server scores)
9770 scores.last_added = scores.last_added_local = last_added_local;
9772 if (setup.use_api_server &&
9773 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9775 // load server scores from cache file and trigger update from server
9776 LoadServerScore(nr, download_score);
9778 // merge local scores with scores from server
9782 if (force_last_added)
9783 scores.force_last_added = force_last_added;
9787 // ============================================================================
9788 // setup file functions
9789 // ============================================================================
9791 #define TOKEN_STR_PLAYER_PREFIX "player_"
9794 static struct TokenInfo global_setup_tokens[] =
9798 &setup.player_name, "player_name"
9802 &setup.multiple_users, "multiple_users"
9806 &setup.sound, "sound"
9810 &setup.sound_loops, "repeating_sound_loops"
9814 &setup.sound_music, "background_music"
9818 &setup.sound_simple, "simple_sound_effects"
9822 &setup.toons, "toons"
9826 &setup.global_animations, "global_animations"
9830 &setup.scroll_delay, "scroll_delay"
9834 &setup.forced_scroll_delay, "forced_scroll_delay"
9838 &setup.scroll_delay_value, "scroll_delay_value"
9842 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9846 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9850 &setup.fade_screens, "fade_screens"
9854 &setup.autorecord, "automatic_tape_recording"
9858 &setup.autorecord_after_replay, "autorecord_after_replay"
9862 &setup.auto_pause_on_start, "auto_pause_on_start"
9866 &setup.show_titlescreen, "show_titlescreen"
9870 &setup.quick_doors, "quick_doors"
9874 &setup.team_mode, "team_mode"
9878 &setup.handicap, "handicap"
9882 &setup.skip_levels, "skip_levels"
9886 &setup.increment_levels, "increment_levels"
9890 &setup.auto_play_next_level, "auto_play_next_level"
9894 &setup.count_score_after_game, "count_score_after_game"
9898 &setup.show_scores_after_game, "show_scores_after_game"
9902 &setup.time_limit, "time_limit"
9906 &setup.fullscreen, "fullscreen"
9910 &setup.window_scaling_percent, "window_scaling_percent"
9914 &setup.window_scaling_quality, "window_scaling_quality"
9918 &setup.screen_rendering_mode, "screen_rendering_mode"
9922 &setup.vsync_mode, "vsync_mode"
9926 &setup.ask_on_escape, "ask_on_escape"
9930 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9934 &setup.ask_on_game_over, "ask_on_game_over"
9938 &setup.ask_on_quit_game, "ask_on_quit_game"
9942 &setup.ask_on_quit_program, "ask_on_quit_program"
9946 &setup.quick_switch, "quick_player_switch"
9950 &setup.input_on_focus, "input_on_focus"
9954 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9958 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9962 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9966 &setup.game_speed_extended, "game_speed_extended"
9970 &setup.game_frame_delay, "game_frame_delay"
9974 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9978 &setup.bd_skip_hatching, "bd_skip_hatching"
9982 &setup.bd_scroll_delay, "bd_scroll_delay"
9986 &setup.bd_smooth_movements, "bd_smooth_movements"
9990 &setup.sp_show_border_elements, "sp_show_border_elements"
9994 &setup.small_game_graphics, "small_game_graphics"
9998 &setup.show_load_save_buttons, "show_load_save_buttons"
10002 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
10006 &setup.scores_in_highscore_list, "scores_in_highscore_list"
10010 &setup.graphics_set, "graphics_set"
10014 &setup.sounds_set, "sounds_set"
10018 &setup.music_set, "music_set"
10022 &setup.override_level_graphics, "override_level_graphics"
10026 &setup.override_level_sounds, "override_level_sounds"
10030 &setup.override_level_music, "override_level_music"
10034 &setup.volume_simple, "volume_simple"
10038 &setup.volume_loops, "volume_loops"
10042 &setup.volume_music, "volume_music"
10046 &setup.network_mode, "network_mode"
10050 &setup.network_player_nr, "network_player"
10054 &setup.network_server_hostname, "network_server_hostname"
10058 &setup.touch.control_type, "touch.control_type"
10062 &setup.touch.move_distance, "touch.move_distance"
10066 &setup.touch.drop_distance, "touch.drop_distance"
10070 &setup.touch.transparency, "touch.transparency"
10074 &setup.touch.draw_outlined, "touch.draw_outlined"
10078 &setup.touch.draw_pressed, "touch.draw_pressed"
10082 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10086 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10090 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10094 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10098 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10102 static struct TokenInfo auto_setup_tokens[] =
10106 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10110 static struct TokenInfo server_setup_tokens[] =
10114 &setup.player_uuid, "player_uuid"
10118 &setup.player_version, "player_version"
10122 &setup.use_api_server, TEST_PREFIX "use_api_server"
10126 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10130 &setup.api_server_password, TEST_PREFIX "api_server_password"
10134 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10138 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10142 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10146 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10150 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10154 static struct TokenInfo editor_setup_tokens[] =
10158 &setup.editor.el_classic, "editor.el_classic"
10162 &setup.editor.el_custom, "editor.el_custom"
10166 &setup.editor.el_user_defined, "editor.el_user_defined"
10170 &setup.editor.el_dynamic, "editor.el_dynamic"
10174 &setup.editor.el_headlines, "editor.el_headlines"
10178 &setup.editor.show_element_token, "editor.show_element_token"
10182 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10186 static struct TokenInfo editor_cascade_setup_tokens[] =
10190 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10194 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10198 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10202 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10206 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10210 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10214 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10218 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10222 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10226 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10230 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10234 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10238 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10242 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10246 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10250 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10254 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10258 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10262 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10266 static struct TokenInfo shortcut_setup_tokens[] =
10270 &setup.shortcut.save_game, "shortcut.save_game"
10274 &setup.shortcut.load_game, "shortcut.load_game"
10278 &setup.shortcut.restart_game, "shortcut.restart_game"
10282 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10286 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10290 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10294 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10298 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10302 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10306 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10310 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10314 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10318 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10322 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10326 &setup.shortcut.tape_record, "shortcut.tape_record"
10330 &setup.shortcut.tape_play, "shortcut.tape_play"
10334 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10338 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10342 &setup.shortcut.sound_music, "shortcut.sound_music"
10346 &setup.shortcut.snap_left, "shortcut.snap_left"
10350 &setup.shortcut.snap_right, "shortcut.snap_right"
10354 &setup.shortcut.snap_up, "shortcut.snap_up"
10358 &setup.shortcut.snap_down, "shortcut.snap_down"
10362 static struct SetupInputInfo setup_input;
10363 static struct TokenInfo player_setup_tokens[] =
10367 &setup_input.use_joystick, ".use_joystick"
10371 &setup_input.joy.device_name, ".joy.device_name"
10375 &setup_input.joy.xleft, ".joy.xleft"
10379 &setup_input.joy.xmiddle, ".joy.xmiddle"
10383 &setup_input.joy.xright, ".joy.xright"
10387 &setup_input.joy.yupper, ".joy.yupper"
10391 &setup_input.joy.ymiddle, ".joy.ymiddle"
10395 &setup_input.joy.ylower, ".joy.ylower"
10399 &setup_input.joy.snap, ".joy.snap_field"
10403 &setup_input.joy.drop, ".joy.place_bomb"
10407 &setup_input.key.left, ".key.move_left"
10411 &setup_input.key.right, ".key.move_right"
10415 &setup_input.key.up, ".key.move_up"
10419 &setup_input.key.down, ".key.move_down"
10423 &setup_input.key.snap, ".key.snap_field"
10427 &setup_input.key.drop, ".key.place_bomb"
10431 static struct TokenInfo system_setup_tokens[] =
10435 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10439 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10443 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10447 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10451 static struct TokenInfo internal_setup_tokens[] =
10455 &setup.internal.program_title, "program_title"
10459 &setup.internal.program_version, "program_version"
10463 &setup.internal.program_author, "program_author"
10467 &setup.internal.program_email, "program_email"
10471 &setup.internal.program_website, "program_website"
10475 &setup.internal.program_copyright, "program_copyright"
10479 &setup.internal.program_company, "program_company"
10483 &setup.internal.program_icon_file, "program_icon_file"
10487 &setup.internal.default_graphics_set, "default_graphics_set"
10491 &setup.internal.default_sounds_set, "default_sounds_set"
10495 &setup.internal.default_music_set, "default_music_set"
10499 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10503 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10507 &setup.internal.fallback_music_file, "fallback_music_file"
10511 &setup.internal.default_level_series, "default_level_series"
10515 &setup.internal.default_window_width, "default_window_width"
10519 &setup.internal.default_window_height, "default_window_height"
10523 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10527 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10531 &setup.internal.create_user_levelset, "create_user_levelset"
10535 &setup.internal.info_screens_from_main, "info_screens_from_main"
10539 &setup.internal.menu_game, "menu_game"
10543 &setup.internal.menu_engines, "menu_engines"
10547 &setup.internal.menu_editor, "menu_editor"
10551 &setup.internal.menu_graphics, "menu_graphics"
10555 &setup.internal.menu_sound, "menu_sound"
10559 &setup.internal.menu_artwork, "menu_artwork"
10563 &setup.internal.menu_input, "menu_input"
10567 &setup.internal.menu_touch, "menu_touch"
10571 &setup.internal.menu_shortcuts, "menu_shortcuts"
10575 &setup.internal.menu_exit, "menu_exit"
10579 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10583 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10587 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10591 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10595 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10599 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10603 &setup.internal.info_title, "info_title"
10607 &setup.internal.info_elements, "info_elements"
10611 &setup.internal.info_music, "info_music"
10615 &setup.internal.info_credits, "info_credits"
10619 &setup.internal.info_program, "info_program"
10623 &setup.internal.info_version, "info_version"
10627 &setup.internal.info_levelset, "info_levelset"
10631 &setup.internal.info_exit, "info_exit"
10635 static struct TokenInfo debug_setup_tokens[] =
10639 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10643 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10647 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10651 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10655 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10659 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10663 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10667 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10671 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10675 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10679 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10683 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10687 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10691 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10695 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10699 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10703 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10707 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10711 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10715 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10719 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10722 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10726 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10730 &setup.debug.xsn_mode, "debug.xsn_mode"
10734 &setup.debug.xsn_percent, "debug.xsn_percent"
10738 static struct TokenInfo options_setup_tokens[] =
10742 &setup.options.verbose, "options.verbose"
10746 &setup.options.debug, "options.debug"
10750 &setup.options.debug_mode, "options.debug_mode"
10754 static void setSetupInfoToDefaults(struct SetupInfo *si)
10758 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10760 si->multiple_users = TRUE;
10763 si->sound_loops = TRUE;
10764 si->sound_music = TRUE;
10765 si->sound_simple = TRUE;
10767 si->global_animations = TRUE;
10768 si->scroll_delay = TRUE;
10769 si->forced_scroll_delay = FALSE;
10770 si->scroll_delay_value = STD_SCROLL_DELAY;
10771 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10772 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10773 si->fade_screens = TRUE;
10774 si->autorecord = TRUE;
10775 si->autorecord_after_replay = TRUE;
10776 si->auto_pause_on_start = FALSE;
10777 si->show_titlescreen = TRUE;
10778 si->quick_doors = FALSE;
10779 si->team_mode = FALSE;
10780 si->handicap = TRUE;
10781 si->skip_levels = TRUE;
10782 si->increment_levels = TRUE;
10783 si->auto_play_next_level = TRUE;
10784 si->count_score_after_game = TRUE;
10785 si->show_scores_after_game = TRUE;
10786 si->time_limit = TRUE;
10787 si->fullscreen = FALSE;
10788 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10789 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10790 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10791 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10792 si->ask_on_escape = TRUE;
10793 si->ask_on_escape_editor = TRUE;
10794 si->ask_on_game_over = TRUE;
10795 si->ask_on_quit_game = TRUE;
10796 si->ask_on_quit_program = TRUE;
10797 si->quick_switch = FALSE;
10798 si->input_on_focus = FALSE;
10799 si->prefer_aga_graphics = TRUE;
10800 si->prefer_lowpass_sounds = FALSE;
10801 si->prefer_extra_panel_items = TRUE;
10802 si->game_speed_extended = FALSE;
10803 si->game_frame_delay = GAME_FRAME_DELAY;
10804 si->bd_skip_uncovering = FALSE;
10805 si->bd_skip_hatching = FALSE;
10806 si->bd_scroll_delay = TRUE;
10807 si->bd_smooth_movements = AUTO;
10808 si->sp_show_border_elements = FALSE;
10809 si->small_game_graphics = FALSE;
10810 si->show_load_save_buttons = FALSE;
10811 si->show_undo_redo_buttons = FALSE;
10812 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10814 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10815 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10816 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10818 si->override_level_graphics = FALSE;
10819 si->override_level_sounds = FALSE;
10820 si->override_level_music = FALSE;
10822 si->volume_simple = 100; // percent
10823 si->volume_loops = 100; // percent
10824 si->volume_music = 100; // percent
10826 si->network_mode = FALSE;
10827 si->network_player_nr = 0; // first player
10828 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10830 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10831 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10832 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10833 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10834 si->touch.draw_outlined = TRUE;
10835 si->touch.draw_pressed = TRUE;
10837 for (i = 0; i < 2; i++)
10839 char *default_grid_button[6][2] =
10845 { "111222", " vv " },
10846 { "111222", " vv " }
10848 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10849 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10850 int min_xsize = MIN(6, grid_xsize);
10851 int min_ysize = MIN(6, grid_ysize);
10852 int startx = grid_xsize - min_xsize;
10853 int starty = grid_ysize - min_ysize;
10856 // virtual buttons grid can only be set to defaults if video is initialized
10857 // (this will be repeated if virtual buttons are not loaded from setup file)
10858 if (video.initialized)
10860 si->touch.grid_xsize[i] = grid_xsize;
10861 si->touch.grid_ysize[i] = grid_ysize;
10865 si->touch.grid_xsize[i] = -1;
10866 si->touch.grid_ysize[i] = -1;
10869 for (x = 0; x < MAX_GRID_XSIZE; x++)
10870 for (y = 0; y < MAX_GRID_YSIZE; y++)
10871 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10873 for (x = 0; x < min_xsize; x++)
10874 for (y = 0; y < min_ysize; y++)
10875 si->touch.grid_button[i][x][starty + y] =
10876 default_grid_button[y][0][x];
10878 for (x = 0; x < min_xsize; x++)
10879 for (y = 0; y < min_ysize; y++)
10880 si->touch.grid_button[i][startx + x][starty + y] =
10881 default_grid_button[y][1][x];
10884 si->touch.grid_initialized = video.initialized;
10886 si->touch.overlay_buttons = FALSE;
10888 si->editor.el_boulderdash = TRUE;
10889 si->editor.el_boulderdash_native = TRUE;
10890 si->editor.el_emerald_mine = TRUE;
10891 si->editor.el_emerald_mine_club = TRUE;
10892 si->editor.el_more = TRUE;
10893 si->editor.el_sokoban = TRUE;
10894 si->editor.el_supaplex = TRUE;
10895 si->editor.el_diamond_caves = TRUE;
10896 si->editor.el_dx_boulderdash = TRUE;
10898 si->editor.el_mirror_magic = TRUE;
10899 si->editor.el_deflektor = TRUE;
10901 si->editor.el_chars = TRUE;
10902 si->editor.el_steel_chars = TRUE;
10904 si->editor.el_classic = TRUE;
10905 si->editor.el_custom = TRUE;
10907 si->editor.el_user_defined = FALSE;
10908 si->editor.el_dynamic = TRUE;
10910 si->editor.el_headlines = TRUE;
10912 si->editor.show_element_token = FALSE;
10914 si->editor.show_read_only_warning = TRUE;
10916 si->editor.use_template_for_new_levels = TRUE;
10918 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10919 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10920 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10921 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10922 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10924 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10925 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10926 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10927 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10928 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10930 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10931 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10932 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10933 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10934 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10935 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10937 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10938 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10939 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10941 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10942 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10943 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10944 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10946 for (i = 0; i < MAX_PLAYERS; i++)
10948 si->input[i].use_joystick = FALSE;
10949 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10950 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10951 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10952 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10953 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10954 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10955 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10956 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10957 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10958 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10959 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10960 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10961 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10962 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10963 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10966 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10967 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10968 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10969 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10971 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10972 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10973 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10974 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10975 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10976 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10977 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10979 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10981 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10982 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10983 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10985 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10986 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10987 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10989 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10990 si->internal.choose_from_top_leveldir = FALSE;
10991 si->internal.show_scaling_in_title = TRUE;
10992 si->internal.create_user_levelset = TRUE;
10993 si->internal.info_screens_from_main = FALSE;
10995 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10996 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10998 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10999 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11000 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11001 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11002 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11003 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11004 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11005 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11006 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11007 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11009 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11010 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11011 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11012 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11013 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11014 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11015 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11016 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11017 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11018 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11020 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11021 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
11023 si->debug.show_frames_per_second = FALSE;
11025 si->debug.xsn_mode = AUTO;
11026 si->debug.xsn_percent = 0;
11028 si->options.verbose = FALSE;
11029 si->options.debug = FALSE;
11030 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11032 #if defined(PLATFORM_ANDROID)
11033 si->fullscreen = TRUE;
11034 si->touch.overlay_buttons = TRUE;
11037 setHideSetupEntry(&setup.debug.xsn_mode);
11040 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11042 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11045 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11047 si->player_uuid = NULL; // (will be set later)
11048 si->player_version = 1; // (will be set later)
11050 si->use_api_server = TRUE;
11051 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11052 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11053 si->ask_for_uploading_tapes = TRUE;
11054 si->ask_for_remaining_tapes = FALSE;
11055 si->provide_uploading_tapes = TRUE;
11056 si->ask_for_using_api_server = TRUE;
11057 si->has_remaining_tapes = FALSE;
11060 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11062 si->editor_cascade.el_bd = TRUE;
11063 si->editor_cascade.el_bd_native = TRUE;
11064 si->editor_cascade.el_em = TRUE;
11065 si->editor_cascade.el_emc = TRUE;
11066 si->editor_cascade.el_rnd = TRUE;
11067 si->editor_cascade.el_sb = TRUE;
11068 si->editor_cascade.el_sp = TRUE;
11069 si->editor_cascade.el_dc = TRUE;
11070 si->editor_cascade.el_dx = TRUE;
11072 si->editor_cascade.el_mm = TRUE;
11073 si->editor_cascade.el_df = TRUE;
11075 si->editor_cascade.el_chars = FALSE;
11076 si->editor_cascade.el_steel_chars = FALSE;
11077 si->editor_cascade.el_ce = FALSE;
11078 si->editor_cascade.el_ge = FALSE;
11079 si->editor_cascade.el_es = FALSE;
11080 si->editor_cascade.el_ref = FALSE;
11081 si->editor_cascade.el_user = FALSE;
11082 si->editor_cascade.el_dynamic = FALSE;
11085 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11087 static char *getHideSetupToken(void *setup_value)
11089 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11091 if (setup_value != NULL)
11092 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11094 return hide_setup_token;
11097 void setHideSetupEntry(void *setup_value)
11099 char *hide_setup_token = getHideSetupToken(setup_value);
11101 if (hide_setup_hash == NULL)
11102 hide_setup_hash = newSetupFileHash();
11104 if (setup_value != NULL)
11105 setHashEntry(hide_setup_hash, hide_setup_token, "");
11108 void removeHideSetupEntry(void *setup_value)
11110 char *hide_setup_token = getHideSetupToken(setup_value);
11112 if (setup_value != NULL)
11113 removeHashEntry(hide_setup_hash, hide_setup_token);
11116 boolean hideSetupEntry(void *setup_value)
11118 char *hide_setup_token = getHideSetupToken(setup_value);
11120 return (setup_value != NULL &&
11121 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11124 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11125 struct TokenInfo *token_info,
11126 int token_nr, char *token_text)
11128 char *token_hide_text = getStringCat2(token_text, ".hide");
11129 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11131 // set the value of this setup option in the setup option structure
11132 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11134 // check if this setup option should be hidden in the setup menu
11135 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11136 setHideSetupEntry(token_info[token_nr].value);
11138 free(token_hide_text);
11141 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11142 struct TokenInfo *token_info,
11145 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11146 token_info[token_nr].text);
11149 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11153 if (!setup_file_hash)
11156 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11157 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11159 setup.touch.grid_initialized = TRUE;
11160 for (i = 0; i < 2; i++)
11162 int grid_xsize = setup.touch.grid_xsize[i];
11163 int grid_ysize = setup.touch.grid_ysize[i];
11166 // if virtual buttons are not loaded from setup file, repeat initializing
11167 // virtual buttons grid with default values later when video is initialized
11168 if (grid_xsize == -1 ||
11171 setup.touch.grid_initialized = FALSE;
11176 for (y = 0; y < grid_ysize; y++)
11178 char token_string[MAX_LINE_LEN];
11180 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11182 char *value_string = getHashEntry(setup_file_hash, token_string);
11184 if (value_string == NULL)
11187 for (x = 0; x < grid_xsize; x++)
11189 char c = value_string[x];
11191 setup.touch.grid_button[i][x][y] =
11192 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11197 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11198 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11200 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11201 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11203 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11207 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11209 setup_input = setup.input[pnr];
11210 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11212 char full_token[100];
11214 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11215 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11218 setup.input[pnr] = setup_input;
11221 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11222 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11224 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11225 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11227 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11228 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11230 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11231 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11233 setHideRelatedSetupEntries();
11236 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11240 if (!setup_file_hash)
11243 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11244 setSetupInfo(auto_setup_tokens, i,
11245 getHashEntry(setup_file_hash,
11246 auto_setup_tokens[i].text));
11249 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11253 if (!setup_file_hash)
11256 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11257 setSetupInfo(server_setup_tokens, i,
11258 getHashEntry(setup_file_hash,
11259 server_setup_tokens[i].text));
11262 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11266 if (!setup_file_hash)
11269 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11270 setSetupInfo(editor_cascade_setup_tokens, i,
11271 getHashEntry(setup_file_hash,
11272 editor_cascade_setup_tokens[i].text));
11275 void LoadUserNames(void)
11277 int last_user_nr = user.nr;
11280 if (global.user_names != NULL)
11282 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11283 checked_free(global.user_names[i]);
11285 checked_free(global.user_names);
11288 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11290 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11294 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11296 if (setup_file_hash)
11298 char *player_name = getHashEntry(setup_file_hash, "player_name");
11300 global.user_names[i] = getFixedUserName(player_name);
11302 freeSetupFileHash(setup_file_hash);
11305 if (global.user_names[i] == NULL)
11306 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11309 user.nr = last_user_nr;
11312 void LoadSetupFromFilename(char *filename)
11314 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11316 if (setup_file_hash)
11318 decodeSetupFileHash_Default(setup_file_hash);
11320 freeSetupFileHash(setup_file_hash);
11324 Debug("setup", "using default setup values");
11328 static void LoadSetup_SpecialPostProcessing(void)
11330 char *player_name_new;
11332 // needed to work around problems with fixed length strings
11333 player_name_new = getFixedUserName(setup.player_name);
11334 free(setup.player_name);
11335 setup.player_name = player_name_new;
11337 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11338 if (setup.scroll_delay == FALSE)
11340 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11341 setup.scroll_delay = TRUE; // now always "on"
11344 // make sure that scroll delay value stays inside valid range
11345 setup.scroll_delay_value =
11346 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11349 void LoadSetup_Default(void)
11353 // always start with reliable default values
11354 setSetupInfoToDefaults(&setup);
11356 // try to load setup values from default setup file
11357 filename = getDefaultSetupFilename();
11359 if (fileExists(filename))
11360 LoadSetupFromFilename(filename);
11362 // try to load setup values from platform setup file
11363 filename = getPlatformSetupFilename();
11365 if (fileExists(filename))
11366 LoadSetupFromFilename(filename);
11368 // try to load setup values from user setup file
11369 filename = getSetupFilename();
11371 LoadSetupFromFilename(filename);
11373 LoadSetup_SpecialPostProcessing();
11376 void LoadSetup_AutoSetup(void)
11378 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11379 SetupFileHash *setup_file_hash = NULL;
11381 // always start with reliable default values
11382 setSetupInfoToDefaults_AutoSetup(&setup);
11384 setup_file_hash = loadSetupFileHash(filename);
11386 if (setup_file_hash)
11388 decodeSetupFileHash_AutoSetup(setup_file_hash);
11390 freeSetupFileHash(setup_file_hash);
11396 void LoadSetup_ServerSetup(void)
11398 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11399 SetupFileHash *setup_file_hash = NULL;
11401 // always start with reliable default values
11402 setSetupInfoToDefaults_ServerSetup(&setup);
11404 setup_file_hash = loadSetupFileHash(filename);
11406 if (setup_file_hash)
11408 decodeSetupFileHash_ServerSetup(setup_file_hash);
11410 freeSetupFileHash(setup_file_hash);
11415 if (setup.player_uuid == NULL)
11417 // player UUID does not yet exist in setup file
11418 setup.player_uuid = getStringCopy(getUUID());
11419 setup.player_version = 2;
11421 SaveSetup_ServerSetup();
11425 void LoadSetup_EditorCascade(void)
11427 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11428 SetupFileHash *setup_file_hash = NULL;
11430 // always start with reliable default values
11431 setSetupInfoToDefaults_EditorCascade(&setup);
11433 setup_file_hash = loadSetupFileHash(filename);
11435 if (setup_file_hash)
11437 decodeSetupFileHash_EditorCascade(setup_file_hash);
11439 freeSetupFileHash(setup_file_hash);
11445 void LoadSetup(void)
11447 LoadSetup_Default();
11448 LoadSetup_AutoSetup();
11449 LoadSetup_ServerSetup();
11450 LoadSetup_EditorCascade();
11453 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11454 char *mapping_line)
11456 char mapping_guid[MAX_LINE_LEN];
11457 char *mapping_start, *mapping_end;
11459 // get GUID from game controller mapping line: copy complete line
11460 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11461 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11463 // get GUID from game controller mapping line: cut after GUID part
11464 mapping_start = strchr(mapping_guid, ',');
11465 if (mapping_start != NULL)
11466 *mapping_start = '\0';
11468 // cut newline from game controller mapping line
11469 mapping_end = strchr(mapping_line, '\n');
11470 if (mapping_end != NULL)
11471 *mapping_end = '\0';
11473 // add mapping entry to game controller mappings hash
11474 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11477 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11482 if (!(file = fopen(filename, MODE_READ)))
11484 Warn("cannot read game controller mappings file '%s'", filename);
11489 while (!feof(file))
11491 char line[MAX_LINE_LEN];
11493 if (!fgets(line, MAX_LINE_LEN, file))
11496 addGameControllerMappingToHash(mappings_hash, line);
11502 void SaveSetup_Default(void)
11504 char *filename = getSetupFilename();
11508 InitUserDataDirectory();
11510 if (!(file = fopen(filename, MODE_WRITE)))
11512 Warn("cannot write setup file '%s'", filename);
11517 fprintFileHeader(file, SETUP_FILENAME);
11519 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11521 // just to make things nicer :)
11522 if (global_setup_tokens[i].value == &setup.multiple_users ||
11523 global_setup_tokens[i].value == &setup.sound ||
11524 global_setup_tokens[i].value == &setup.graphics_set ||
11525 global_setup_tokens[i].value == &setup.volume_simple ||
11526 global_setup_tokens[i].value == &setup.network_mode ||
11527 global_setup_tokens[i].value == &setup.touch.control_type ||
11528 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11529 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11530 fprintf(file, "\n");
11532 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11535 for (i = 0; i < 2; i++)
11537 int grid_xsize = setup.touch.grid_xsize[i];
11538 int grid_ysize = setup.touch.grid_ysize[i];
11541 fprintf(file, "\n");
11543 for (y = 0; y < grid_ysize; y++)
11545 char token_string[MAX_LINE_LEN];
11546 char value_string[MAX_LINE_LEN];
11548 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11550 for (x = 0; x < grid_xsize; x++)
11552 char c = setup.touch.grid_button[i][x][y];
11554 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11557 value_string[grid_xsize] = '\0';
11559 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11563 fprintf(file, "\n");
11564 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11565 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11567 fprintf(file, "\n");
11568 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11569 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11571 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11575 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11576 fprintf(file, "\n");
11578 setup_input = setup.input[pnr];
11579 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11580 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11583 fprintf(file, "\n");
11584 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11585 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11587 // (internal setup values not saved to user setup file)
11589 fprintf(file, "\n");
11590 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11591 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11592 setup.debug.xsn_mode != AUTO)
11593 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11595 fprintf(file, "\n");
11596 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11597 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11601 SetFilePermissions(filename, PERMS_PRIVATE);
11604 void SaveSetup_AutoSetup(void)
11606 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11610 InitUserDataDirectory();
11612 if (!(file = fopen(filename, MODE_WRITE)))
11614 Warn("cannot write auto setup file '%s'", filename);
11621 fprintFileHeader(file, AUTOSETUP_FILENAME);
11623 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11624 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11628 SetFilePermissions(filename, PERMS_PRIVATE);
11633 void SaveSetup_ServerSetup(void)
11635 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11639 InitUserDataDirectory();
11641 if (!(file = fopen(filename, MODE_WRITE)))
11643 Warn("cannot write server setup file '%s'", filename);
11650 fprintFileHeader(file, SERVERSETUP_FILENAME);
11652 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11654 // just to make things nicer :)
11655 if (server_setup_tokens[i].value == &setup.use_api_server)
11656 fprintf(file, "\n");
11658 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11663 SetFilePermissions(filename, PERMS_PRIVATE);
11668 void SaveSetup_EditorCascade(void)
11670 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11674 InitUserDataDirectory();
11676 if (!(file = fopen(filename, MODE_WRITE)))
11678 Warn("cannot write editor cascade state file '%s'", filename);
11685 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11687 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11688 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11692 SetFilePermissions(filename, PERMS_PRIVATE);
11697 void SaveSetup(void)
11699 SaveSetup_Default();
11700 SaveSetup_AutoSetup();
11701 SaveSetup_ServerSetup();
11702 SaveSetup_EditorCascade();
11705 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11710 if (!(file = fopen(filename, MODE_WRITE)))
11712 Warn("cannot write game controller mappings file '%s'", filename);
11717 BEGIN_HASH_ITERATION(mappings_hash, itr)
11719 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11721 END_HASH_ITERATION(mappings_hash, itr)
11726 void SaveSetup_AddGameControllerMapping(char *mapping)
11728 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11729 SetupFileHash *mappings_hash = newSetupFileHash();
11731 InitUserDataDirectory();
11733 // load existing personal game controller mappings
11734 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11736 // add new mapping to personal game controller mappings
11737 addGameControllerMappingToHash(mappings_hash, mapping);
11739 // save updated personal game controller mappings
11740 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11742 freeSetupFileHash(mappings_hash);
11746 void LoadCustomElementDescriptions(void)
11748 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11749 SetupFileHash *setup_file_hash;
11752 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11754 if (element_info[i].custom_description != NULL)
11756 free(element_info[i].custom_description);
11757 element_info[i].custom_description = NULL;
11761 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11764 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11766 char *token = getStringCat2(element_info[i].token_name, ".name");
11767 char *value = getHashEntry(setup_file_hash, token);
11770 element_info[i].custom_description = getStringCopy(value);
11775 freeSetupFileHash(setup_file_hash);
11778 static int getElementFromToken(char *token)
11780 char *value = getHashEntry(element_token_hash, token);
11783 return atoi(value);
11785 Warn("unknown element token '%s'", token);
11787 return EL_UNDEFINED;
11790 void FreeGlobalAnimEventInfo(void)
11792 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11794 if (gaei->event_list == NULL)
11799 for (i = 0; i < gaei->num_event_lists; i++)
11801 checked_free(gaei->event_list[i]->event_value);
11802 checked_free(gaei->event_list[i]);
11805 checked_free(gaei->event_list);
11807 gaei->event_list = NULL;
11808 gaei->num_event_lists = 0;
11811 static int AddGlobalAnimEventList(void)
11813 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11814 int list_pos = gaei->num_event_lists++;
11816 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11817 sizeof(struct GlobalAnimEventListInfo *));
11819 gaei->event_list[list_pos] =
11820 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11822 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11824 gaeli->event_value = NULL;
11825 gaeli->num_event_values = 0;
11830 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11832 // do not add empty global animation events
11833 if (event_value == ANIM_EVENT_NONE)
11836 // if list position is undefined, create new list
11837 if (list_pos == ANIM_EVENT_UNDEFINED)
11838 list_pos = AddGlobalAnimEventList();
11840 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11841 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11842 int value_pos = gaeli->num_event_values++;
11844 gaeli->event_value = checked_realloc(gaeli->event_value,
11845 gaeli->num_event_values * sizeof(int *));
11847 gaeli->event_value[value_pos] = event_value;
11852 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11854 if (list_pos == ANIM_EVENT_UNDEFINED)
11855 return ANIM_EVENT_NONE;
11857 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11858 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11860 return gaeli->event_value[value_pos];
11863 int GetGlobalAnimEventValueCount(int list_pos)
11865 if (list_pos == ANIM_EVENT_UNDEFINED)
11868 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11869 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11871 return gaeli->num_event_values;
11874 // This function checks if a string <s> of the format "string1, string2, ..."
11875 // exactly contains a string <s_contained>.
11877 static boolean string_has_parameter(char *s, char *s_contained)
11881 if (s == NULL || s_contained == NULL)
11884 if (strlen(s_contained) > strlen(s))
11887 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11889 char next_char = s[strlen(s_contained)];
11891 // check if next character is delimiter or whitespace
11892 if (next_char == ',' || next_char == '\0' ||
11893 next_char == ' ' || next_char == '\t')
11897 // check if string contains another parameter string after a comma
11898 substring = strchr(s, ',');
11899 if (substring == NULL) // string does not contain a comma
11902 // advance string pointer to next character after the comma
11905 // skip potential whitespaces after the comma
11906 while (*substring == ' ' || *substring == '\t')
11909 return string_has_parameter(substring, s_contained);
11912 static int get_anim_parameter_value_ce(char *s)
11915 char *pattern_1 = "ce_change:custom_";
11916 char *pattern_2 = ".page_";
11917 int pattern_1_len = strlen(pattern_1);
11918 char *matching_char = strstr(s_ptr, pattern_1);
11919 int result = ANIM_EVENT_NONE;
11921 if (matching_char == NULL)
11922 return ANIM_EVENT_NONE;
11924 result = ANIM_EVENT_CE_CHANGE;
11926 s_ptr = matching_char + pattern_1_len;
11928 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11929 if (*s_ptr >= '0' && *s_ptr <= '9')
11931 int gic_ce_nr = (*s_ptr++ - '0');
11933 if (*s_ptr >= '0' && *s_ptr <= '9')
11935 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11937 if (*s_ptr >= '0' && *s_ptr <= '9')
11938 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11941 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11942 return ANIM_EVENT_NONE;
11944 // custom element stored as 0 to 255
11947 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11951 // invalid custom element number specified
11953 return ANIM_EVENT_NONE;
11956 // check for change page number ("page_X" or "page_XX") (optional)
11957 if (strPrefix(s_ptr, pattern_2))
11959 s_ptr += strlen(pattern_2);
11961 if (*s_ptr >= '0' && *s_ptr <= '9')
11963 int gic_page_nr = (*s_ptr++ - '0');
11965 if (*s_ptr >= '0' && *s_ptr <= '9')
11966 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11968 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11969 return ANIM_EVENT_NONE;
11971 // change page stored as 1 to 32 (0 means "all change pages")
11973 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11977 // invalid animation part number specified
11979 return ANIM_EVENT_NONE;
11983 // discard result if next character is neither delimiter nor whitespace
11984 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11985 *s_ptr == ' ' || *s_ptr == '\t'))
11986 return ANIM_EVENT_NONE;
11991 static int get_anim_parameter_value(char *s)
11993 int event_value[] =
12001 char *pattern_1[] =
12009 char *pattern_2 = ".part_";
12010 char *matching_char = NULL;
12012 int pattern_1_len = 0;
12013 int result = ANIM_EVENT_NONE;
12016 result = get_anim_parameter_value_ce(s);
12018 if (result != ANIM_EVENT_NONE)
12021 for (i = 0; i < ARRAY_SIZE(event_value); i++)
12023 matching_char = strstr(s_ptr, pattern_1[i]);
12024 pattern_1_len = strlen(pattern_1[i]);
12025 result = event_value[i];
12027 if (matching_char != NULL)
12031 if (matching_char == NULL)
12032 return ANIM_EVENT_NONE;
12034 s_ptr = matching_char + pattern_1_len;
12036 // check for main animation number ("anim_X" or "anim_XX")
12037 if (*s_ptr >= '0' && *s_ptr <= '9')
12039 int gic_anim_nr = (*s_ptr++ - '0');
12041 if (*s_ptr >= '0' && *s_ptr <= '9')
12042 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12044 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12045 return ANIM_EVENT_NONE;
12047 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12051 // invalid main animation number specified
12053 return ANIM_EVENT_NONE;
12056 // check for animation part number ("part_X" or "part_XX") (optional)
12057 if (strPrefix(s_ptr, pattern_2))
12059 s_ptr += strlen(pattern_2);
12061 if (*s_ptr >= '0' && *s_ptr <= '9')
12063 int gic_part_nr = (*s_ptr++ - '0');
12065 if (*s_ptr >= '0' && *s_ptr <= '9')
12066 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12068 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12069 return ANIM_EVENT_NONE;
12071 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12075 // invalid animation part number specified
12077 return ANIM_EVENT_NONE;
12081 // discard result if next character is neither delimiter nor whitespace
12082 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12083 *s_ptr == ' ' || *s_ptr == '\t'))
12084 return ANIM_EVENT_NONE;
12089 static int get_anim_parameter_values(char *s)
12091 int list_pos = ANIM_EVENT_UNDEFINED;
12092 int event_value = ANIM_EVENT_DEFAULT;
12094 if (string_has_parameter(s, "any"))
12095 event_value |= ANIM_EVENT_ANY;
12097 if (string_has_parameter(s, "click:self") ||
12098 string_has_parameter(s, "click") ||
12099 string_has_parameter(s, "self"))
12100 event_value |= ANIM_EVENT_SELF;
12102 if (string_has_parameter(s, "unclick:any"))
12103 event_value |= ANIM_EVENT_UNCLICK_ANY;
12105 // if animation event found, add it to global animation event list
12106 if (event_value != ANIM_EVENT_NONE)
12107 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12111 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12112 event_value = get_anim_parameter_value(s);
12114 // if animation event found, add it to global animation event list
12115 if (event_value != ANIM_EVENT_NONE)
12116 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12118 // continue with next part of the string, starting with next comma
12119 s = strchr(s + 1, ',');
12125 static int get_anim_action_parameter_value(char *token)
12127 // check most common default case first to massively speed things up
12128 if (strEqual(token, ARG_UNDEFINED))
12129 return ANIM_EVENT_ACTION_NONE;
12131 int result = getImageIDFromToken(token);
12135 char *gfx_token = getStringCat2("gfx.", token);
12137 result = getImageIDFromToken(gfx_token);
12139 checked_free(gfx_token);
12144 Key key = getKeyFromX11KeyName(token);
12146 if (key != KSYM_UNDEFINED)
12147 result = -(int)key;
12154 result = get_hash_from_string(token); // unsigned int => int
12155 result = ABS(result); // may be negative now
12156 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12158 setHashEntry(anim_url_hash, int2str(result, 0), token);
12163 result = ANIM_EVENT_ACTION_NONE;
12168 int get_parameter_value(char *value_raw, char *suffix, int type)
12170 char *value = getStringToLower(value_raw);
12171 int result = 0; // probably a save default value
12173 if (strEqual(suffix, ".direction"))
12175 result = (strEqual(value, "left") ? MV_LEFT :
12176 strEqual(value, "right") ? MV_RIGHT :
12177 strEqual(value, "up") ? MV_UP :
12178 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12180 else if (strEqual(suffix, ".position"))
12182 result = (strEqual(value, "left") ? POS_LEFT :
12183 strEqual(value, "right") ? POS_RIGHT :
12184 strEqual(value, "top") ? POS_TOP :
12185 strEqual(value, "upper") ? POS_UPPER :
12186 strEqual(value, "middle") ? POS_MIDDLE :
12187 strEqual(value, "lower") ? POS_LOWER :
12188 strEqual(value, "bottom") ? POS_BOTTOM :
12189 strEqual(value, "any") ? POS_ANY :
12190 strEqual(value, "ce") ? POS_CE :
12191 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12192 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12194 else if (strEqual(suffix, ".align"))
12196 result = (strEqual(value, "left") ? ALIGN_LEFT :
12197 strEqual(value, "right") ? ALIGN_RIGHT :
12198 strEqual(value, "center") ? ALIGN_CENTER :
12199 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12201 else if (strEqual(suffix, ".valign"))
12203 result = (strEqual(value, "top") ? VALIGN_TOP :
12204 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12205 strEqual(value, "middle") ? VALIGN_MIDDLE :
12206 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12208 else if (strEqual(suffix, ".anim_mode"))
12210 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12211 string_has_parameter(value, "loop") ? ANIM_LOOP :
12212 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12213 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12214 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12215 string_has_parameter(value, "random") ? ANIM_RANDOM :
12216 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12217 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12218 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12219 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12220 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12221 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12222 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12223 string_has_parameter(value, "all") ? ANIM_ALL :
12224 string_has_parameter(value, "tiled") ? ANIM_TILED :
12225 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12228 if (string_has_parameter(value, "once"))
12229 result |= ANIM_ONCE;
12231 if (string_has_parameter(value, "reverse"))
12232 result |= ANIM_REVERSE;
12234 if (string_has_parameter(value, "opaque_player"))
12235 result |= ANIM_OPAQUE_PLAYER;
12237 if (string_has_parameter(value, "static_panel"))
12238 result |= ANIM_STATIC_PANEL;
12240 else if (strEqual(suffix, ".init_event") ||
12241 strEqual(suffix, ".anim_event"))
12243 result = get_anim_parameter_values(value);
12245 else if (strEqual(suffix, ".init_delay_action") ||
12246 strEqual(suffix, ".anim_delay_action") ||
12247 strEqual(suffix, ".post_delay_action") ||
12248 strEqual(suffix, ".init_event_action") ||
12249 strEqual(suffix, ".anim_event_action"))
12251 result = get_anim_action_parameter_value(value_raw);
12253 else if (strEqual(suffix, ".class"))
12255 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12256 get_hash_from_string(value));
12258 else if (strEqual(suffix, ".style"))
12260 result = STYLE_DEFAULT;
12262 if (string_has_parameter(value, "accurate_borders"))
12263 result |= STYLE_ACCURATE_BORDERS;
12265 if (string_has_parameter(value, "inner_corners"))
12266 result |= STYLE_INNER_CORNERS;
12268 if (string_has_parameter(value, "reverse"))
12269 result |= STYLE_REVERSE;
12271 if (string_has_parameter(value, "leftmost_position"))
12272 result |= STYLE_LEFTMOST_POSITION;
12274 if (string_has_parameter(value, "block_clicks"))
12275 result |= STYLE_BLOCK;
12277 if (string_has_parameter(value, "passthrough_clicks"))
12278 result |= STYLE_PASSTHROUGH;
12280 if (string_has_parameter(value, "multiple_actions"))
12281 result |= STYLE_MULTIPLE_ACTIONS;
12283 if (string_has_parameter(value, "consume_ce_event"))
12284 result |= STYLE_CONSUME_CE_EVENT;
12286 else if (strEqual(suffix, ".fade_mode"))
12288 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12289 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12290 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12291 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12292 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12293 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12294 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12295 FADE_MODE_DEFAULT);
12297 else if (strEqual(suffix, ".auto_delay_unit"))
12299 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12300 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12301 AUTO_DELAY_UNIT_DEFAULT);
12303 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12305 result = gfx.get_font_from_token_function(value);
12307 else // generic parameter of type integer or boolean
12309 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12310 type == TYPE_INTEGER ? get_integer_from_string(value) :
12311 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12312 ARG_UNDEFINED_VALUE);
12320 static int get_token_parameter_value(char *token, char *value_raw)
12324 if (token == NULL || value_raw == NULL)
12325 return ARG_UNDEFINED_VALUE;
12327 suffix = strrchr(token, '.');
12328 if (suffix == NULL)
12331 if (strEqual(suffix, ".element"))
12332 return getElementFromToken(value_raw);
12334 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12335 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12338 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12339 boolean ignore_defaults)
12343 for (i = 0; image_config_vars[i].token != NULL; i++)
12345 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12347 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12348 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12352 *image_config_vars[i].value =
12353 get_token_parameter_value(image_config_vars[i].token, value);
12357 void InitMenuDesignSettings_Static(void)
12359 // always start with reliable default values from static default config
12360 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12363 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12367 // the following initializes hierarchical values from static configuration
12369 // special case: initialize "ARG_DEFAULT" values in static default config
12370 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12371 titlescreen_initial_first_default.fade_mode =
12372 title_initial_first_default.fade_mode;
12373 titlescreen_initial_first_default.fade_delay =
12374 title_initial_first_default.fade_delay;
12375 titlescreen_initial_first_default.post_delay =
12376 title_initial_first_default.post_delay;
12377 titlescreen_initial_first_default.auto_delay =
12378 title_initial_first_default.auto_delay;
12379 titlescreen_initial_first_default.auto_delay_unit =
12380 title_initial_first_default.auto_delay_unit;
12381 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12382 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12383 titlescreen_first_default.post_delay = title_first_default.post_delay;
12384 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12385 titlescreen_first_default.auto_delay_unit =
12386 title_first_default.auto_delay_unit;
12387 titlemessage_initial_first_default.fade_mode =
12388 title_initial_first_default.fade_mode;
12389 titlemessage_initial_first_default.fade_delay =
12390 title_initial_first_default.fade_delay;
12391 titlemessage_initial_first_default.post_delay =
12392 title_initial_first_default.post_delay;
12393 titlemessage_initial_first_default.auto_delay =
12394 title_initial_first_default.auto_delay;
12395 titlemessage_initial_first_default.auto_delay_unit =
12396 title_initial_first_default.auto_delay_unit;
12397 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12398 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12399 titlemessage_first_default.post_delay = title_first_default.post_delay;
12400 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12401 titlemessage_first_default.auto_delay_unit =
12402 title_first_default.auto_delay_unit;
12404 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12405 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12406 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12407 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12408 titlescreen_initial_default.auto_delay_unit =
12409 title_initial_default.auto_delay_unit;
12410 titlescreen_default.fade_mode = title_default.fade_mode;
12411 titlescreen_default.fade_delay = title_default.fade_delay;
12412 titlescreen_default.post_delay = title_default.post_delay;
12413 titlescreen_default.auto_delay = title_default.auto_delay;
12414 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12415 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12416 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12417 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12418 titlemessage_initial_default.auto_delay_unit =
12419 title_initial_default.auto_delay_unit;
12420 titlemessage_default.fade_mode = title_default.fade_mode;
12421 titlemessage_default.fade_delay = title_default.fade_delay;
12422 titlemessage_default.post_delay = title_default.post_delay;
12423 titlemessage_default.auto_delay = title_default.auto_delay;
12424 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12426 // special case: initialize "ARG_DEFAULT" values in static default config
12427 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12428 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12430 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12431 titlescreen_first[i] = titlescreen_first_default;
12432 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12433 titlemessage_first[i] = titlemessage_first_default;
12435 titlescreen_initial[i] = titlescreen_initial_default;
12436 titlescreen[i] = titlescreen_default;
12437 titlemessage_initial[i] = titlemessage_initial_default;
12438 titlemessage[i] = titlemessage_default;
12441 // special case: initialize "ARG_DEFAULT" values in static default config
12442 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12443 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12445 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12448 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12449 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12450 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12453 // special case: initialize "ARG_DEFAULT" values in static default config
12454 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12455 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12457 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12458 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12459 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12461 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12464 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12468 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12472 struct XY *dst, *src;
12474 game_buttons_xy[] =
12476 { &game.button.save, &game.button.stop },
12477 { &game.button.pause2, &game.button.pause },
12478 { &game.button.load, &game.button.play },
12479 { &game.button.undo, &game.button.stop },
12480 { &game.button.redo, &game.button.play },
12486 // special case: initialize later added SETUP list size from LEVELS value
12487 if (menu.list_size[GAME_MODE_SETUP] == -1)
12488 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12490 // set default position for snapshot buttons to stop/pause/play buttons
12491 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12492 if ((*game_buttons_xy[i].dst).x == -1 &&
12493 (*game_buttons_xy[i].dst).y == -1)
12494 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12496 // --------------------------------------------------------------------------
12497 // dynamic viewports (including playfield margins, borders and alignments)
12498 // --------------------------------------------------------------------------
12500 // dynamic viewports currently only supported for landscape mode
12501 int display_width = MAX(video.display_width, video.display_height);
12502 int display_height = MIN(video.display_width, video.display_height);
12504 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12506 struct RectWithBorder *vp_window = &viewport.window[i];
12507 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12508 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12509 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12510 boolean dynamic_window_width = (vp_window->min_width != -1);
12511 boolean dynamic_window_height = (vp_window->min_height != -1);
12512 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12513 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12515 // adjust window size if min/max width/height is specified
12517 if (vp_window->min_width != -1)
12519 int window_width = display_width;
12521 // when using static window height, use aspect ratio of display
12522 if (vp_window->min_height == -1)
12523 window_width = vp_window->height * display_width / display_height;
12525 vp_window->width = MAX(vp_window->min_width, window_width);
12528 if (vp_window->min_height != -1)
12530 int window_height = display_height;
12532 // when using static window width, use aspect ratio of display
12533 if (vp_window->min_width == -1)
12534 window_height = vp_window->width * display_height / display_width;
12536 vp_window->height = MAX(vp_window->min_height, window_height);
12539 if (vp_window->max_width != -1)
12540 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12542 if (vp_window->max_height != -1)
12543 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12545 int playfield_width = vp_window->width;
12546 int playfield_height = vp_window->height;
12548 // adjust playfield size and position according to specified margins
12550 playfield_width -= vp_playfield->margin_left;
12551 playfield_width -= vp_playfield->margin_right;
12553 playfield_height -= vp_playfield->margin_top;
12554 playfield_height -= vp_playfield->margin_bottom;
12556 // adjust playfield size if min/max width/height is specified
12558 if (vp_playfield->min_width != -1)
12559 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12561 if (vp_playfield->min_height != -1)
12562 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12564 if (vp_playfield->max_width != -1)
12565 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12567 if (vp_playfield->max_height != -1)
12568 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12570 // adjust playfield position according to specified alignment
12572 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12573 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12574 else if (vp_playfield->align == ALIGN_CENTER)
12575 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12576 else if (vp_playfield->align == ALIGN_RIGHT)
12577 vp_playfield->x += playfield_width - vp_playfield->width;
12579 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12580 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12581 else if (vp_playfield->valign == VALIGN_MIDDLE)
12582 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12583 else if (vp_playfield->valign == VALIGN_BOTTOM)
12584 vp_playfield->y += playfield_height - vp_playfield->height;
12586 vp_playfield->x += vp_playfield->margin_left;
12587 vp_playfield->y += vp_playfield->margin_top;
12589 // adjust individual playfield borders if only default border is specified
12591 if (vp_playfield->border_left == -1)
12592 vp_playfield->border_left = vp_playfield->border_size;
12593 if (vp_playfield->border_right == -1)
12594 vp_playfield->border_right = vp_playfield->border_size;
12595 if (vp_playfield->border_top == -1)
12596 vp_playfield->border_top = vp_playfield->border_size;
12597 if (vp_playfield->border_bottom == -1)
12598 vp_playfield->border_bottom = vp_playfield->border_size;
12600 // set dynamic playfield borders if borders are specified as undefined
12601 // (but only if window size was dynamic and playfield size was static)
12603 if (dynamic_window_width && !dynamic_playfield_width)
12605 if (vp_playfield->border_left == -1)
12607 vp_playfield->border_left = (vp_playfield->x -
12608 vp_playfield->margin_left);
12609 vp_playfield->x -= vp_playfield->border_left;
12610 vp_playfield->width += vp_playfield->border_left;
12613 if (vp_playfield->border_right == -1)
12615 vp_playfield->border_right = (vp_window->width -
12617 vp_playfield->width -
12618 vp_playfield->margin_right);
12619 vp_playfield->width += vp_playfield->border_right;
12623 if (dynamic_window_height && !dynamic_playfield_height)
12625 if (vp_playfield->border_top == -1)
12627 vp_playfield->border_top = (vp_playfield->y -
12628 vp_playfield->margin_top);
12629 vp_playfield->y -= vp_playfield->border_top;
12630 vp_playfield->height += vp_playfield->border_top;
12633 if (vp_playfield->border_bottom == -1)
12635 vp_playfield->border_bottom = (vp_window->height -
12637 vp_playfield->height -
12638 vp_playfield->margin_bottom);
12639 vp_playfield->height += vp_playfield->border_bottom;
12643 // adjust playfield size to be a multiple of a defined alignment tile size
12645 int align_size = vp_playfield->align_size;
12646 int playfield_xtiles = vp_playfield->width / align_size;
12647 int playfield_ytiles = vp_playfield->height / align_size;
12648 int playfield_width_corrected = playfield_xtiles * align_size;
12649 int playfield_height_corrected = playfield_ytiles * align_size;
12650 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12651 i == GFX_SPECIAL_ARG_EDITOR);
12653 if (is_playfield_mode &&
12654 dynamic_playfield_width &&
12655 vp_playfield->width != playfield_width_corrected)
12657 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12659 vp_playfield->width = playfield_width_corrected;
12661 if (vp_playfield->align == ALIGN_LEFT)
12663 vp_playfield->border_left += playfield_xdiff;
12665 else if (vp_playfield->align == ALIGN_RIGHT)
12667 vp_playfield->border_right += playfield_xdiff;
12669 else if (vp_playfield->align == ALIGN_CENTER)
12671 int border_left_diff = playfield_xdiff / 2;
12672 int border_right_diff = playfield_xdiff - border_left_diff;
12674 vp_playfield->border_left += border_left_diff;
12675 vp_playfield->border_right += border_right_diff;
12679 if (is_playfield_mode &&
12680 dynamic_playfield_height &&
12681 vp_playfield->height != playfield_height_corrected)
12683 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12685 vp_playfield->height = playfield_height_corrected;
12687 if (vp_playfield->valign == VALIGN_TOP)
12689 vp_playfield->border_top += playfield_ydiff;
12691 else if (vp_playfield->align == VALIGN_BOTTOM)
12693 vp_playfield->border_right += playfield_ydiff;
12695 else if (vp_playfield->align == VALIGN_MIDDLE)
12697 int border_top_diff = playfield_ydiff / 2;
12698 int border_bottom_diff = playfield_ydiff - border_top_diff;
12700 vp_playfield->border_top += border_top_diff;
12701 vp_playfield->border_bottom += border_bottom_diff;
12705 // adjust door positions according to specified alignment
12707 for (j = 0; j < 2; j++)
12709 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12711 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12712 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12713 else if (vp_door->align == ALIGN_CENTER)
12714 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12715 else if (vp_door->align == ALIGN_RIGHT)
12716 vp_door->x += vp_window->width - vp_door->width;
12718 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12719 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12720 else if (vp_door->valign == VALIGN_MIDDLE)
12721 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12722 else if (vp_door->valign == VALIGN_BOTTOM)
12723 vp_door->y += vp_window->height - vp_door->height;
12728 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12732 struct XYTileSize *dst, *src;
12735 editor_buttons_xy[] =
12738 &editor.button.element_left, &editor.palette.element_left,
12739 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12742 &editor.button.element_middle, &editor.palette.element_middle,
12743 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12746 &editor.button.element_right, &editor.palette.element_right,
12747 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12754 // set default position for element buttons to element graphics
12755 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12757 if ((*editor_buttons_xy[i].dst).x == -1 &&
12758 (*editor_buttons_xy[i].dst).y == -1)
12760 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12762 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12764 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12768 // adjust editor palette rows and columns if specified to be dynamic
12770 if (editor.palette.cols == -1)
12772 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12773 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12774 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12776 editor.palette.cols = (vp_width - sc_width) / bt_width;
12778 if (editor.palette.x == -1)
12780 int palette_width = editor.palette.cols * bt_width + sc_width;
12782 editor.palette.x = (vp_width - palette_width) / 2;
12786 if (editor.palette.rows == -1)
12788 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12789 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12790 int tx_height = getFontHeight(FONT_TEXT_2);
12792 editor.palette.rows = (vp_height - tx_height) / bt_height;
12794 if (editor.palette.y == -1)
12796 int palette_height = editor.palette.rows * bt_height + tx_height;
12798 editor.palette.y = (vp_height - palette_height) / 2;
12803 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12804 boolean initialize)
12806 // special case: check if network and preview player positions are redefined,
12807 // to compare this later against the main menu level preview being redefined
12808 struct TokenIntPtrInfo menu_config_players[] =
12810 { "main.network_players.x", &menu.main.network_players.redefined },
12811 { "main.network_players.y", &menu.main.network_players.redefined },
12812 { "main.preview_players.x", &menu.main.preview_players.redefined },
12813 { "main.preview_players.y", &menu.main.preview_players.redefined },
12814 { "preview.x", &preview.redefined },
12815 { "preview.y", &preview.redefined }
12821 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12822 *menu_config_players[i].value = FALSE;
12826 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12827 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12828 *menu_config_players[i].value = TRUE;
12832 static void InitMenuDesignSettings_PreviewPlayers(void)
12834 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12837 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12839 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12842 static void LoadMenuDesignSettingsFromFilename(char *filename)
12844 static struct TitleFadingInfo tfi;
12845 static struct TitleMessageInfo tmi;
12846 static struct TokenInfo title_tokens[] =
12848 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12849 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12850 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12851 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12852 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12856 static struct TokenInfo titlemessage_tokens[] =
12858 { TYPE_INTEGER, &tmi.x, ".x" },
12859 { TYPE_INTEGER, &tmi.y, ".y" },
12860 { TYPE_INTEGER, &tmi.width, ".width" },
12861 { TYPE_INTEGER, &tmi.height, ".height" },
12862 { TYPE_INTEGER, &tmi.chars, ".chars" },
12863 { TYPE_INTEGER, &tmi.lines, ".lines" },
12864 { TYPE_INTEGER, &tmi.align, ".align" },
12865 { TYPE_INTEGER, &tmi.valign, ".valign" },
12866 { TYPE_INTEGER, &tmi.font, ".font" },
12867 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12868 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12869 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12870 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12871 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12872 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12873 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12874 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12875 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12881 struct TitleFadingInfo *info;
12886 // initialize first titles from "enter screen" definitions, if defined
12887 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12888 { &title_first_default, "menu.enter_screen.TITLE" },
12890 // initialize title screens from "next screen" definitions, if defined
12891 { &title_initial_default, "menu.next_screen.TITLE" },
12892 { &title_default, "menu.next_screen.TITLE" },
12898 struct TitleMessageInfo *array;
12901 titlemessage_arrays[] =
12903 // initialize first titles from "enter screen" definitions, if defined
12904 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12905 { titlescreen_first, "menu.enter_screen.TITLE" },
12906 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12907 { titlemessage_first, "menu.enter_screen.TITLE" },
12909 // initialize titles from "next screen" definitions, if defined
12910 { titlescreen_initial, "menu.next_screen.TITLE" },
12911 { titlescreen, "menu.next_screen.TITLE" },
12912 { titlemessage_initial, "menu.next_screen.TITLE" },
12913 { titlemessage, "menu.next_screen.TITLE" },
12915 // overwrite titles with title definitions, if defined
12916 { titlescreen_initial_first, "[title_initial]" },
12917 { titlescreen_first, "[title]" },
12918 { titlemessage_initial_first, "[title_initial]" },
12919 { titlemessage_first, "[title]" },
12921 { titlescreen_initial, "[title_initial]" },
12922 { titlescreen, "[title]" },
12923 { titlemessage_initial, "[title_initial]" },
12924 { titlemessage, "[title]" },
12926 // overwrite titles with title screen/message definitions, if defined
12927 { titlescreen_initial_first, "[titlescreen_initial]" },
12928 { titlescreen_first, "[titlescreen]" },
12929 { titlemessage_initial_first, "[titlemessage_initial]" },
12930 { titlemessage_first, "[titlemessage]" },
12932 { titlescreen_initial, "[titlescreen_initial]" },
12933 { titlescreen, "[titlescreen]" },
12934 { titlemessage_initial, "[titlemessage_initial]" },
12935 { titlemessage, "[titlemessage]" },
12939 SetupFileHash *setup_file_hash;
12942 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12945 // the following initializes hierarchical values from dynamic configuration
12947 // special case: initialize with default values that may be overwritten
12948 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12949 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12951 struct TokenIntPtrInfo menu_config[] =
12953 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12954 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12955 { "menu.list_size", &menu.list_size[i] }
12958 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12960 char *token = menu_config[j].token;
12961 char *value = getHashEntry(setup_file_hash, token);
12964 *menu_config[j].value = get_integer_from_string(value);
12968 // special case: initialize with default values that may be overwritten
12969 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12970 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12972 struct TokenIntPtrInfo menu_config[] =
12974 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12975 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12976 { "menu.list_size.INFO", &menu.list_size_info[i] },
12977 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12978 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12981 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12983 char *token = menu_config[j].token;
12984 char *value = getHashEntry(setup_file_hash, token);
12987 *menu_config[j].value = get_integer_from_string(value);
12991 // special case: initialize with default values that may be overwritten
12992 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12993 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12995 struct TokenIntPtrInfo menu_config[] =
12997 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12998 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
13001 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13003 char *token = menu_config[j].token;
13004 char *value = getHashEntry(setup_file_hash, token);
13007 *menu_config[j].value = get_integer_from_string(value);
13011 // special case: initialize with default values that may be overwritten
13012 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13013 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13015 struct TokenIntPtrInfo menu_config[] =
13017 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
13018 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
13019 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
13020 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
13021 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
13022 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
13023 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
13024 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
13025 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
13026 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
13029 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13031 char *token = menu_config[j].token;
13032 char *value = getHashEntry(setup_file_hash, token);
13035 *menu_config[j].value = get_integer_from_string(value);
13039 // special case: initialize with default values that may be overwritten
13040 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13041 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13043 struct TokenIntPtrInfo menu_config[] =
13045 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13046 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13047 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13048 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13049 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13050 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13051 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13052 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13053 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13056 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13058 char *token = menu_config[j].token;
13059 char *value = getHashEntry(setup_file_hash, token);
13062 *menu_config[j].value = get_token_parameter_value(token, value);
13066 // special case: initialize with default values that may be overwritten
13067 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13068 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13072 char *token_prefix;
13073 struct RectWithBorder *struct_ptr;
13077 { "viewport.window", &viewport.window[i] },
13078 { "viewport.playfield", &viewport.playfield[i] },
13079 { "viewport.door_1", &viewport.door_1[i] },
13080 { "viewport.door_2", &viewport.door_2[i] }
13083 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13085 struct TokenIntPtrInfo vp_config[] =
13087 { ".x", &vp_struct[j].struct_ptr->x },
13088 { ".y", &vp_struct[j].struct_ptr->y },
13089 { ".width", &vp_struct[j].struct_ptr->width },
13090 { ".height", &vp_struct[j].struct_ptr->height },
13091 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13092 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13093 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13094 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13095 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13096 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13097 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13098 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13099 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13100 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13101 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13102 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13103 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13104 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13105 { ".align", &vp_struct[j].struct_ptr->align },
13106 { ".valign", &vp_struct[j].struct_ptr->valign }
13109 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13111 char *token = getStringCat2(vp_struct[j].token_prefix,
13112 vp_config[k].token);
13113 char *value = getHashEntry(setup_file_hash, token);
13116 *vp_config[k].value = get_token_parameter_value(token, value);
13123 // special case: initialize with default values that may be overwritten
13124 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13125 for (i = 0; title_info[i].info != NULL; i++)
13127 struct TitleFadingInfo *info = title_info[i].info;
13128 char *base_token = title_info[i].text;
13130 for (j = 0; title_tokens[j].type != -1; j++)
13132 char *token = getStringCat2(base_token, title_tokens[j].text);
13133 char *value = getHashEntry(setup_file_hash, token);
13137 int parameter_value = get_token_parameter_value(token, value);
13141 *(int *)title_tokens[j].value = (int)parameter_value;
13150 // special case: initialize with default values that may be overwritten
13151 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13152 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13154 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13155 char *base_token = titlemessage_arrays[i].text;
13157 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13159 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13160 char *value = getHashEntry(setup_file_hash, token);
13164 int parameter_value = get_token_parameter_value(token, value);
13166 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13170 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13171 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13173 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13183 // read (and overwrite with) values that may be specified in config file
13184 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13186 // special case: check if network and preview player positions are redefined
13187 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13189 freeSetupFileHash(setup_file_hash);
13192 void LoadMenuDesignSettings(void)
13194 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13196 InitMenuDesignSettings_Static();
13197 InitMenuDesignSettings_SpecialPreProcessing();
13198 InitMenuDesignSettings_PreviewPlayers();
13200 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13202 // first look for special settings configured in level series config
13203 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13205 if (fileExists(filename_base))
13206 LoadMenuDesignSettingsFromFilename(filename_base);
13209 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13211 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13212 LoadMenuDesignSettingsFromFilename(filename_local);
13214 InitMenuDesignSettings_SpecialPostProcessing();
13217 void LoadMenuDesignSettings_AfterGraphics(void)
13219 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13222 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13223 boolean ignore_defaults)
13227 for (i = 0; sound_config_vars[i].token != NULL; i++)
13229 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13231 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13232 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13236 *sound_config_vars[i].value =
13237 get_token_parameter_value(sound_config_vars[i].token, value);
13241 void InitSoundSettings_Static(void)
13243 // always start with reliable default values from static default config
13244 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13247 static void LoadSoundSettingsFromFilename(char *filename)
13249 SetupFileHash *setup_file_hash;
13251 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13254 // read (and overwrite with) values that may be specified in config file
13255 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13257 freeSetupFileHash(setup_file_hash);
13260 void LoadSoundSettings(void)
13262 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13264 InitSoundSettings_Static();
13266 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13268 // first look for special settings configured in level series config
13269 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13271 if (fileExists(filename_base))
13272 LoadSoundSettingsFromFilename(filename_base);
13275 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13277 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13278 LoadSoundSettingsFromFilename(filename_local);
13281 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13283 char *filename = getEditorSetupFilename();
13284 SetupFileList *setup_file_list, *list;
13285 SetupFileHash *element_hash;
13286 int num_unknown_tokens = 0;
13289 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13292 element_hash = newSetupFileHash();
13294 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13295 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13297 // determined size may be larger than needed (due to unknown elements)
13299 for (list = setup_file_list; list != NULL; list = list->next)
13302 // add space for up to 3 more elements for padding that may be needed
13303 *num_elements += 3;
13305 // free memory for old list of elements, if needed
13306 checked_free(*elements);
13308 // allocate memory for new list of elements
13309 *elements = checked_malloc(*num_elements * sizeof(int));
13312 for (list = setup_file_list; list != NULL; list = list->next)
13314 char *value = getHashEntry(element_hash, list->token);
13316 if (value == NULL) // try to find obsolete token mapping
13318 char *mapped_token = get_mapped_token(list->token);
13320 if (mapped_token != NULL)
13322 value = getHashEntry(element_hash, mapped_token);
13324 free(mapped_token);
13330 (*elements)[(*num_elements)++] = atoi(value);
13334 if (num_unknown_tokens == 0)
13337 Warn("unknown token(s) found in config file:");
13338 Warn("- config file: '%s'", filename);
13340 num_unknown_tokens++;
13343 Warn("- token: '%s'", list->token);
13347 if (num_unknown_tokens > 0)
13350 while (*num_elements % 4) // pad with empty elements, if needed
13351 (*elements)[(*num_elements)++] = EL_EMPTY;
13353 freeSetupFileList(setup_file_list);
13354 freeSetupFileHash(element_hash);
13357 for (i = 0; i < *num_elements; i++)
13358 Debug("editor", "element '%s' [%d]\n",
13359 element_info[(*elements)[i]].token_name, (*elements)[i]);
13363 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13366 SetupFileHash *setup_file_hash = NULL;
13367 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13368 char *filename_music, *filename_prefix, *filename_info;
13374 token_to_value_ptr[] =
13376 { "title_header", &tmp_music_file_info.title_header },
13377 { "artist_header", &tmp_music_file_info.artist_header },
13378 { "album_header", &tmp_music_file_info.album_header },
13379 { "year_header", &tmp_music_file_info.year_header },
13380 { "played_header", &tmp_music_file_info.played_header },
13382 { "title", &tmp_music_file_info.title },
13383 { "artist", &tmp_music_file_info.artist },
13384 { "album", &tmp_music_file_info.album },
13385 { "year", &tmp_music_file_info.year },
13386 { "played", &tmp_music_file_info.played },
13392 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13393 getCustomMusicFilename(basename));
13395 if (filename_music == NULL)
13398 // ---------- try to replace file extension ----------
13400 filename_prefix = getStringCopy(filename_music);
13401 if (strrchr(filename_prefix, '.') != NULL)
13402 *strrchr(filename_prefix, '.') = '\0';
13403 filename_info = getStringCat2(filename_prefix, ".txt");
13405 if (fileExists(filename_info))
13406 setup_file_hash = loadSetupFileHash(filename_info);
13408 free(filename_prefix);
13409 free(filename_info);
13411 if (setup_file_hash == NULL)
13413 // ---------- try to add file extension ----------
13415 filename_prefix = getStringCopy(filename_music);
13416 filename_info = getStringCat2(filename_prefix, ".txt");
13418 if (fileExists(filename_info))
13419 setup_file_hash = loadSetupFileHash(filename_info);
13421 free(filename_prefix);
13422 free(filename_info);
13425 if (setup_file_hash == NULL)
13428 // ---------- music file info found ----------
13430 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13432 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13434 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13436 *token_to_value_ptr[i].value_ptr =
13437 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13440 tmp_music_file_info.basename = getStringCopy(basename);
13441 tmp_music_file_info.music = music;
13442 tmp_music_file_info.is_sound = is_sound;
13444 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13445 *new_music_file_info = tmp_music_file_info;
13447 return new_music_file_info;
13450 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13452 return get_music_file_info_ext(basename, music, FALSE);
13455 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13457 return get_music_file_info_ext(basename, sound, TRUE);
13460 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13461 char *basename, boolean is_sound)
13463 for (; list != NULL; list = list->next)
13464 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13470 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13472 return music_info_listed_ext(list, basename, FALSE);
13475 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13477 return music_info_listed_ext(list, basename, TRUE);
13480 void LoadMusicInfo(void)
13482 int num_music_noconf = getMusicListSize_NoConf();
13483 int num_music = getMusicListSize();
13484 int num_sounds = getSoundListSize();
13485 struct FileInfo *music, *sound;
13486 struct MusicFileInfo *next, **new;
13490 while (music_file_info != NULL)
13492 next = music_file_info->next;
13494 checked_free(music_file_info->basename);
13496 checked_free(music_file_info->title_header);
13497 checked_free(music_file_info->artist_header);
13498 checked_free(music_file_info->album_header);
13499 checked_free(music_file_info->year_header);
13500 checked_free(music_file_info->played_header);
13502 checked_free(music_file_info->title);
13503 checked_free(music_file_info->artist);
13504 checked_free(music_file_info->album);
13505 checked_free(music_file_info->year);
13506 checked_free(music_file_info->played);
13508 free(music_file_info);
13510 music_file_info = next;
13513 new = &music_file_info;
13515 // get (configured or unconfigured) music file info for all levels
13516 for (i = leveldir_current->first_level;
13517 i <= leveldir_current->last_level; i++)
13521 if (levelset.music[i] != MUS_UNDEFINED)
13523 // get music file info for configured level music
13524 music_nr = levelset.music[i];
13526 else if (num_music_noconf > 0)
13528 // get music file info for unconfigured level music
13529 int level_pos = i - leveldir_current->first_level;
13531 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13538 char *basename = getMusicInfoEntryFilename(music_nr);
13540 if (basename == NULL)
13543 if (!music_info_listed(music_file_info, basename))
13545 *new = get_music_file_info(basename, music_nr);
13548 new = &(*new)->next;
13552 // get music file info for all remaining configured music files
13553 for (i = 0; i < num_music; i++)
13555 music = getMusicListEntry(i);
13557 if (music->filename == NULL)
13560 if (strEqual(music->filename, UNDEFINED_FILENAME))
13563 // a configured file may be not recognized as music
13564 if (!FileIsMusic(music->filename))
13567 if (!music_info_listed(music_file_info, music->filename))
13569 *new = get_music_file_info(music->filename, i);
13572 new = &(*new)->next;
13576 // get sound file info for all configured sound files
13577 for (i = 0; i < num_sounds; i++)
13579 sound = getSoundListEntry(i);
13581 if (sound->filename == NULL)
13584 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13587 // a configured file may be not recognized as sound
13588 if (!FileIsSound(sound->filename))
13591 if (!sound_info_listed(music_file_info, sound->filename))
13593 *new = get_sound_file_info(sound->filename, i);
13595 new = &(*new)->next;
13599 // add pointers to previous list nodes
13601 struct MusicFileInfo *node = music_file_info;
13603 while (node != NULL)
13606 node->next->prev = node;
13612 static void add_helpanim_entry(int element, int action, int direction,
13613 int delay, int *num_list_entries)
13615 struct HelpAnimInfo *new_list_entry;
13616 (*num_list_entries)++;
13619 checked_realloc(helpanim_info,
13620 *num_list_entries * sizeof(struct HelpAnimInfo));
13621 new_list_entry = &helpanim_info[*num_list_entries - 1];
13623 new_list_entry->element = element;
13624 new_list_entry->action = action;
13625 new_list_entry->direction = direction;
13626 new_list_entry->delay = delay;
13629 static void print_unknown_token(char *filename, char *token, int token_nr)
13634 Warn("unknown token(s) found in config file:");
13635 Warn("- config file: '%s'", filename);
13638 Warn("- token: '%s'", token);
13641 static void print_unknown_token_end(int token_nr)
13647 void LoadHelpAnimInfo(void)
13649 char *filename = getHelpAnimFilename();
13650 SetupFileList *setup_file_list = NULL, *list;
13651 SetupFileHash *element_hash, *action_hash, *direction_hash;
13652 int num_list_entries = 0;
13653 int num_unknown_tokens = 0;
13656 if (fileExists(filename))
13657 setup_file_list = loadSetupFileList(filename);
13659 if (setup_file_list == NULL)
13661 // use reliable default values from static configuration
13662 SetupFileList *insert_ptr;
13664 insert_ptr = setup_file_list =
13665 newSetupFileList(helpanim_config[0].token,
13666 helpanim_config[0].value);
13668 for (i = 1; helpanim_config[i].token; i++)
13669 insert_ptr = addListEntry(insert_ptr,
13670 helpanim_config[i].token,
13671 helpanim_config[i].value);
13674 element_hash = newSetupFileHash();
13675 action_hash = newSetupFileHash();
13676 direction_hash = newSetupFileHash();
13678 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13679 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13681 for (i = 0; i < NUM_ACTIONS; i++)
13682 setHashEntry(action_hash, element_action_info[i].suffix,
13683 i_to_a(element_action_info[i].value));
13685 // do not store direction index (bit) here, but direction value!
13686 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13687 setHashEntry(direction_hash, element_direction_info[i].suffix,
13688 i_to_a(1 << element_direction_info[i].value));
13690 for (list = setup_file_list; list != NULL; list = list->next)
13692 char *element_token, *action_token, *direction_token;
13693 char *element_value, *action_value, *direction_value;
13694 int delay = atoi(list->value);
13696 if (strEqual(list->token, "end"))
13698 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13703 /* first try to break element into element/action/direction parts;
13704 if this does not work, also accept combined "element[.act][.dir]"
13705 elements (like "dynamite.active"), which are unique elements */
13707 if (strchr(list->token, '.') == NULL) // token contains no '.'
13709 element_value = getHashEntry(element_hash, list->token);
13710 if (element_value != NULL) // element found
13711 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13712 &num_list_entries);
13715 // no further suffixes found -- this is not an element
13716 print_unknown_token(filename, list->token, num_unknown_tokens++);
13722 // token has format "<prefix>.<something>"
13724 action_token = strchr(list->token, '.'); // suffix may be action ...
13725 direction_token = action_token; // ... or direction
13727 element_token = getStringCopy(list->token);
13728 *strchr(element_token, '.') = '\0';
13730 element_value = getHashEntry(element_hash, element_token);
13732 if (element_value == NULL) // this is no element
13734 element_value = getHashEntry(element_hash, list->token);
13735 if (element_value != NULL) // combined element found
13736 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13737 &num_list_entries);
13739 print_unknown_token(filename, list->token, num_unknown_tokens++);
13741 free(element_token);
13746 action_value = getHashEntry(action_hash, action_token);
13748 if (action_value != NULL) // action found
13750 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13751 &num_list_entries);
13753 free(element_token);
13758 direction_value = getHashEntry(direction_hash, direction_token);
13760 if (direction_value != NULL) // direction found
13762 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13763 &num_list_entries);
13765 free(element_token);
13770 if (strchr(action_token + 1, '.') == NULL)
13772 // no further suffixes found -- this is not an action nor direction
13774 element_value = getHashEntry(element_hash, list->token);
13775 if (element_value != NULL) // combined element found
13776 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13777 &num_list_entries);
13779 print_unknown_token(filename, list->token, num_unknown_tokens++);
13781 free(element_token);
13786 // token has format "<prefix>.<suffix>.<something>"
13788 direction_token = strchr(action_token + 1, '.');
13790 action_token = getStringCopy(action_token);
13791 *strchr(action_token + 1, '.') = '\0';
13793 action_value = getHashEntry(action_hash, action_token);
13795 if (action_value == NULL) // this is no action
13797 element_value = getHashEntry(element_hash, list->token);
13798 if (element_value != NULL) // combined element found
13799 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13800 &num_list_entries);
13802 print_unknown_token(filename, list->token, num_unknown_tokens++);
13804 free(element_token);
13805 free(action_token);
13810 direction_value = getHashEntry(direction_hash, direction_token);
13812 if (direction_value != NULL) // direction found
13814 add_helpanim_entry(atoi(element_value), atoi(action_value),
13815 atoi(direction_value), delay, &num_list_entries);
13817 free(element_token);
13818 free(action_token);
13823 // this is no direction
13825 element_value = getHashEntry(element_hash, list->token);
13826 if (element_value != NULL) // combined element found
13827 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13828 &num_list_entries);
13830 print_unknown_token(filename, list->token, num_unknown_tokens++);
13832 free(element_token);
13833 free(action_token);
13836 print_unknown_token_end(num_unknown_tokens);
13838 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13839 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13841 freeSetupFileList(setup_file_list);
13842 freeSetupFileHash(element_hash);
13843 freeSetupFileHash(action_hash);
13844 freeSetupFileHash(direction_hash);
13847 for (i = 0; i < num_list_entries; i++)
13848 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13849 EL_NAME(helpanim_info[i].element),
13850 helpanim_info[i].element,
13851 helpanim_info[i].action,
13852 helpanim_info[i].direction,
13853 helpanim_info[i].delay);
13857 void LoadHelpTextInfo(void)
13859 char *filename = getHelpTextFilename();
13862 if (helptext_info != NULL)
13864 freeSetupFileHash(helptext_info);
13865 helptext_info = NULL;
13868 if (fileExists(filename))
13869 helptext_info = loadSetupFileHash(filename);
13871 if (helptext_info == NULL)
13873 // use reliable default values from static configuration
13874 helptext_info = newSetupFileHash();
13876 for (i = 0; helptext_config[i].token; i++)
13877 setHashEntry(helptext_info,
13878 helptext_config[i].token,
13879 helptext_config[i].value);
13883 BEGIN_HASH_ITERATION(helptext_info, itr)
13885 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13886 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13888 END_HASH_ITERATION(hash, itr)
13893 // ----------------------------------------------------------------------------
13895 // ----------------------------------------------------------------------------
13897 #define MAX_NUM_CONVERT_LEVELS 1000
13899 void ConvertLevels(void)
13901 static LevelDirTree *convert_leveldir = NULL;
13902 static int convert_level_nr = -1;
13903 static int num_levels_handled = 0;
13904 static int num_levels_converted = 0;
13905 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13908 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13909 global.convert_leveldir);
13911 if (convert_leveldir == NULL)
13912 Fail("no such level identifier: '%s'", global.convert_leveldir);
13914 leveldir_current = convert_leveldir;
13916 if (global.convert_level_nr != -1)
13918 convert_leveldir->first_level = global.convert_level_nr;
13919 convert_leveldir->last_level = global.convert_level_nr;
13922 convert_level_nr = convert_leveldir->first_level;
13924 PrintLine("=", 79);
13925 Print("Converting levels\n");
13926 PrintLine("-", 79);
13927 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13928 Print("Level series name: '%s'\n", convert_leveldir->name);
13929 Print("Level series author: '%s'\n", convert_leveldir->author);
13930 Print("Number of levels: %d\n", convert_leveldir->levels);
13931 PrintLine("=", 79);
13934 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13935 levels_failed[i] = FALSE;
13937 while (convert_level_nr <= convert_leveldir->last_level)
13939 char *level_filename;
13942 level_nr = convert_level_nr++;
13944 Print("Level %03d: ", level_nr);
13946 LoadLevel(level_nr);
13947 if (level.no_level_file || level.no_valid_file)
13949 Print("(no level)\n");
13953 Print("converting level ... ");
13956 // special case: conversion of some EMC levels as requested by ACME
13957 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13960 level_filename = getDefaultLevelFilename(level_nr);
13961 new_level = !fileExists(level_filename);
13965 SaveLevel(level_nr);
13967 num_levels_converted++;
13969 Print("converted.\n");
13973 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13974 levels_failed[level_nr] = TRUE;
13976 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13979 num_levels_handled++;
13983 PrintLine("=", 79);
13984 Print("Number of levels handled: %d\n", num_levels_handled);
13985 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13986 (num_levels_handled ?
13987 num_levels_converted * 100 / num_levels_handled : 0));
13988 PrintLine("-", 79);
13989 Print("Summary (for automatic parsing by scripts):\n");
13990 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13991 convert_leveldir->identifier, num_levels_converted,
13992 num_levels_handled,
13993 (num_levels_handled ?
13994 num_levels_converted * 100 / num_levels_handled : 0));
13996 if (num_levels_handled != num_levels_converted)
13998 Print(", FAILED:");
13999 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14000 if (levels_failed[i])
14005 PrintLine("=", 79);
14007 CloseAllAndExit(0);
14011 // ----------------------------------------------------------------------------
14012 // create and save images for use in level sketches (raw BMP format)
14013 // ----------------------------------------------------------------------------
14015 void CreateLevelSketchImages(void)
14021 InitElementPropertiesGfxElement();
14023 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14024 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14026 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14028 int element = getMappedElement(i);
14029 char basename1[16];
14030 char basename2[16];
14034 sprintf(basename1, "%04d.bmp", i);
14035 sprintf(basename2, "%04ds.bmp", i);
14037 filename1 = getPath2(global.create_sketch_images_dir, basename1);
14038 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14040 DrawSizedElement(0, 0, element, TILESIZE);
14041 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14043 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14044 Fail("cannot save level sketch image file '%s'", filename1);
14046 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14047 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14049 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14050 Fail("cannot save level sketch image file '%s'", filename2);
14055 // create corresponding SQL statements (for normal and small images)
14058 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14059 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14062 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14063 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14065 // optional: create content for forum level sketch demonstration post
14067 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14070 FreeBitmap(bitmap1);
14071 FreeBitmap(bitmap2);
14074 fprintf(stderr, "\n");
14076 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14078 CloseAllAndExit(0);
14082 // ----------------------------------------------------------------------------
14083 // create and save images for element collecting animations (raw BMP format)
14084 // ----------------------------------------------------------------------------
14086 static boolean createCollectImage(int element)
14088 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14091 void CreateCollectElementImages(void)
14095 int anim_frames = num_steps - 1;
14096 int tile_size = TILESIZE;
14097 int anim_width = tile_size * anim_frames;
14098 int anim_height = tile_size;
14099 int num_collect_images = 0;
14100 int pos_collect_images = 0;
14102 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14103 if (createCollectImage(i))
14104 num_collect_images++;
14106 Info("Creating %d element collecting animation images ...",
14107 num_collect_images);
14109 int dst_width = anim_width * 2;
14110 int dst_height = anim_height * num_collect_images / 2;
14111 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14112 char *basename_bmp = "RocksCollect.bmp";
14113 char *basename_png = "RocksCollect.png";
14114 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14115 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14116 int len_filename_bmp = strlen(filename_bmp);
14117 int len_filename_png = strlen(filename_png);
14118 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14119 char cmd_convert[max_command_len];
14121 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14125 // force using RGBA surface for destination bitmap
14126 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14127 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14129 dst_bitmap->surface =
14130 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14132 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14134 if (!createCollectImage(i))
14137 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14138 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14139 int graphic = el2img(i);
14140 char *token_name = element_info[i].token_name;
14141 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14142 Bitmap *src_bitmap;
14145 Info("- creating collecting image for '%s' ...", token_name);
14147 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14149 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14150 tile_size, tile_size, 0, 0);
14152 // force using RGBA surface for temporary bitmap (using transparent black)
14153 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14154 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14156 tmp_bitmap->surface =
14157 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14159 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14161 for (j = 0; j < anim_frames; j++)
14163 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14164 int frame_size = frame_size_final * num_steps;
14165 int offset = (tile_size - frame_size_final) / 2;
14166 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14168 while (frame_size > frame_size_final)
14172 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14174 FreeBitmap(frame_bitmap);
14176 frame_bitmap = half_bitmap;
14179 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14180 frame_size_final, frame_size_final,
14181 dst_x + j * tile_size + offset, dst_y + offset);
14183 FreeBitmap(frame_bitmap);
14186 tmp_bitmap->surface_masked = NULL;
14188 FreeBitmap(tmp_bitmap);
14190 pos_collect_images++;
14193 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14194 Fail("cannot save element collecting image file '%s'", filename_bmp);
14196 FreeBitmap(dst_bitmap);
14198 Info("Converting image file from BMP to PNG ...");
14200 if (system(cmd_convert) != 0)
14201 Fail("converting image file failed");
14203 unlink(filename_bmp);
14207 CloseAllAndExit(0);
14211 // ----------------------------------------------------------------------------
14212 // create and save images for custom and group elements (raw BMP format)
14213 // ----------------------------------------------------------------------------
14215 void CreateCustomElementImages(char *directory)
14217 char *src_basename = "RocksCE-template.ilbm";
14218 char *dst_basename = "RocksCE.bmp";
14219 char *src_filename = getPath2(directory, src_basename);
14220 char *dst_filename = getPath2(directory, dst_basename);
14221 Bitmap *src_bitmap;
14223 int yoffset_ce = 0;
14224 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14227 InitVideoDefaults();
14229 ReCreateBitmap(&backbuffer, video.width, video.height);
14231 src_bitmap = LoadImage(src_filename);
14233 bitmap = CreateBitmap(TILEX * 16 * 2,
14234 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14237 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14244 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14245 TILEX * x, TILEY * y + yoffset_ce);
14247 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14249 TILEX * x + TILEX * 16,
14250 TILEY * y + yoffset_ce);
14252 for (j = 2; j >= 0; j--)
14256 BlitBitmap(src_bitmap, bitmap,
14257 TILEX + c * 7, 0, 6, 10,
14258 TILEX * x + 6 + j * 7,
14259 TILEY * y + 11 + yoffset_ce);
14261 BlitBitmap(src_bitmap, bitmap,
14262 TILEX + c * 8, TILEY, 6, 10,
14263 TILEX * 16 + TILEX * x + 6 + j * 8,
14264 TILEY * y + 10 + yoffset_ce);
14270 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14277 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14278 TILEX * x, TILEY * y + yoffset_ge);
14280 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14282 TILEX * x + TILEX * 16,
14283 TILEY * y + yoffset_ge);
14285 for (j = 1; j >= 0; j--)
14289 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14290 TILEX * x + 6 + j * 10,
14291 TILEY * y + 11 + yoffset_ge);
14293 BlitBitmap(src_bitmap, bitmap,
14294 TILEX + c * 8, TILEY + 12, 6, 10,
14295 TILEX * 16 + TILEX * x + 10 + j * 8,
14296 TILEY * y + 10 + yoffset_ge);
14302 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14303 Fail("cannot save CE graphics file '%s'", dst_filename);
14305 FreeBitmap(bitmap);
14307 CloseAllAndExit(0);