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
331 static struct LevelFileConfigInfo chunk_config_ELEM[] =
333 // (these values are the same for each player)
336 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
337 &li.block_last_field, FALSE // default case for EM levels
341 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
342 &li.sp_block_last_field, TRUE // default case for SP levels
346 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
347 &li.instant_relocation, FALSE
351 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
352 &li.can_pass_to_walkable, FALSE
356 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
357 &li.block_snap_field, TRUE
361 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
362 &li.continuous_snapping, TRUE
366 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
367 &li.shifted_relocation, FALSE
371 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
372 &li.lazy_relocation, FALSE
376 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
377 &li.finish_dig_collect, TRUE
381 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
382 &li.keep_walkable_ce, FALSE
385 // (these values are different for each player)
388 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
389 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
393 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
394 &li.initial_player_gravity[0], FALSE
398 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
399 &li.use_start_element[0], FALSE
403 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
404 &li.start_element[0], EL_PLAYER_1
408 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
409 &li.use_artwork_element[0], FALSE
413 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
414 &li.artwork_element[0], EL_PLAYER_1
418 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
419 &li.use_explosion_element[0], FALSE
423 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
424 &li.explosion_element[0], EL_PLAYER_1
428 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
429 &li.use_initial_inventory[0], FALSE
433 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
434 &li.initial_inventory_size[0], 1
438 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
439 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
440 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
445 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
446 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
450 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
451 &li.initial_player_gravity[1], FALSE
455 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
456 &li.use_start_element[1], FALSE
460 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
461 &li.start_element[1], EL_PLAYER_2
465 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
466 &li.use_artwork_element[1], FALSE
470 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
471 &li.artwork_element[1], EL_PLAYER_2
475 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
476 &li.use_explosion_element[1], FALSE
480 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
481 &li.explosion_element[1], EL_PLAYER_2
485 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
486 &li.use_initial_inventory[1], FALSE
490 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
491 &li.initial_inventory_size[1], 1
495 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
496 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
497 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
502 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
503 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
508 &li.initial_player_gravity[2], FALSE
512 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
513 &li.use_start_element[2], FALSE
517 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
518 &li.start_element[2], EL_PLAYER_3
522 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
523 &li.use_artwork_element[2], FALSE
527 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
528 &li.artwork_element[2], EL_PLAYER_3
532 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
533 &li.use_explosion_element[2], FALSE
537 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
538 &li.explosion_element[2], EL_PLAYER_3
542 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
543 &li.use_initial_inventory[2], FALSE
547 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
548 &li.initial_inventory_size[2], 1
552 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
553 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
554 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
559 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
560 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
564 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
565 &li.initial_player_gravity[3], FALSE
569 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
570 &li.use_start_element[3], FALSE
574 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
575 &li.start_element[3], EL_PLAYER_4
579 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
580 &li.use_artwork_element[3], FALSE
584 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
585 &li.artwork_element[3], EL_PLAYER_4
589 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
590 &li.use_explosion_element[3], FALSE
594 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
595 &li.explosion_element[3], EL_PLAYER_4
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
600 &li.use_initial_inventory[3], FALSE
604 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
605 &li.initial_inventory_size[3], 1
609 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
610 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
611 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
614 // (these values are only valid for BD style levels)
617 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
618 &li.bd_diagonal_movements, FALSE
623 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
624 &li.score[SC_DIAMOND_EXTRA], 20
627 // (the following values are related to various game elements)
631 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
632 &li.score[SC_EMERALD], 10
637 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
638 &li.score[SC_DIAMOND], 10
643 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
644 &li.score[SC_BUG], 10
649 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
650 &li.score[SC_SPACESHIP], 10
655 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
656 &li.score[SC_PACMAN], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
662 &li.score[SC_NUT], 10
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
668 &li.score[SC_DYNAMITE], 10
673 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
674 &li.score[SC_KEY], 10
679 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
680 &li.score[SC_PEARL], 10
685 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
686 &li.score[SC_CRYSTAL], 10
691 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
692 &li.amoeba_content, EL_DIAMOND
696 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
701 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
702 &li.grow_into_diggable, TRUE
707 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
708 &li.yamyam_content, EL_ROCK, NULL,
709 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
713 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
714 &li.score[SC_YAMYAM], 10
719 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
720 &li.score[SC_ROBOT], 10
724 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
730 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
736 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
737 &li.time_magic_wall, 10
742 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
743 &li.game_of_life[0], 2
747 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
748 &li.game_of_life[1], 3
752 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
753 &li.game_of_life[2], 3
757 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
758 &li.game_of_life[3], 3
762 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
763 &li.use_life_bugs, FALSE
768 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
773 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
778 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
783 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
788 EL_TIMEGATE_SWITCH, -1,
789 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
790 &li.time_timegate, 10
794 EL_LIGHT_SWITCH_ACTIVE, -1,
795 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
800 EL_SHIELD_NORMAL, -1,
801 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
802 &li.shield_normal_time, 10
805 EL_SHIELD_NORMAL, -1,
806 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
807 &li.score[SC_SHIELD], 10
811 EL_SHIELD_DEADLY, -1,
812 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
813 &li.shield_deadly_time, 10
816 EL_SHIELD_DEADLY, -1,
817 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
818 &li.score[SC_SHIELD], 10
823 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
828 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
829 &li.extra_time_score, 10
833 EL_TIME_ORB_FULL, -1,
834 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
835 &li.time_orb_time, 10
838 EL_TIME_ORB_FULL, -1,
839 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
840 &li.use_time_orb_bug, FALSE
845 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
846 &li.use_spring_bug, FALSE
851 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
852 &li.android_move_time, 10
856 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
857 &li.android_clone_time, 10
860 EL_EMC_ANDROID, SAVE_CONF_NEVER,
861 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
862 &li.android_clone_element[0], EL_EMPTY, NULL,
863 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
867 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
868 &li.android_clone_element[0], EL_EMPTY, NULL,
869 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
874 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
879 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
884 EL_EMC_MAGNIFIER, -1,
885 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
886 &li.magnify_score, 10
889 EL_EMC_MAGNIFIER, -1,
890 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
895 EL_EMC_MAGIC_BALL, -1,
896 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
900 EL_EMC_MAGIC_BALL, -1,
901 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
902 &li.ball_random, FALSE
905 EL_EMC_MAGIC_BALL, -1,
906 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
907 &li.ball_active_initial, FALSE
910 EL_EMC_MAGIC_BALL, -1,
911 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
912 &li.ball_content, EL_EMPTY, NULL,
913 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
917 EL_SOKOBAN_FIELD_EMPTY, -1,
918 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
919 &li.sb_fields_needed, TRUE
923 EL_SOKOBAN_OBJECT, -1,
924 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
925 &li.sb_objects_needed, TRUE
930 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
931 &li.mm_laser_red, FALSE
935 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
936 &li.mm_laser_green, FALSE
940 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
941 &li.mm_laser_blue, TRUE
946 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
947 &li.df_laser_red, TRUE
951 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
952 &li.df_laser_green, TRUE
956 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
957 &li.df_laser_blue, FALSE
961 EL_MM_FUSE_ACTIVE, -1,
962 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
967 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
973 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
978 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
979 &li.mm_ball_choice_mode, ANIM_RANDOM
983 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
984 &li.mm_ball_content, EL_EMPTY, NULL,
985 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
989 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
990 &li.rotate_mm_ball_content, TRUE
994 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
995 &li.explode_mm_ball, FALSE
999 EL_MM_STEEL_BLOCK, -1,
1000 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1001 &li.mm_time_block, 75
1004 EL_MM_LIGHTBALL, -1,
1005 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
1006 &li.score[SC_ELEM_BONUS], 10
1016 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1020 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1021 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1025 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1026 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1031 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1032 &xx_envelope.autowrap, FALSE
1036 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1037 &xx_envelope.centered, FALSE
1042 TYPE_STRING, CONF_VALUE_BYTES(1),
1043 &xx_envelope.text, -1, NULL,
1044 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1045 &xx_default_string_empty[0]
1055 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1059 TYPE_STRING, CONF_VALUE_BYTES(1),
1060 &xx_ei.description[0], -1,
1061 &yy_ei.description[0],
1062 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1063 &xx_default_description[0]
1068 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1069 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1070 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1072 #if ENABLE_RESERVED_CODE
1073 // (reserved for later use)
1076 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1077 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1078 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1084 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1085 &xx_ei.use_gfx_element, FALSE,
1086 &yy_ei.use_gfx_element
1090 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1091 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1092 &yy_ei.gfx_element_initial
1097 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1098 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1099 &yy_ei.access_direction
1104 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1105 &xx_ei.collect_score_initial, 10,
1106 &yy_ei.collect_score_initial
1110 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1111 &xx_ei.collect_count_initial, 1,
1112 &yy_ei.collect_count_initial
1117 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1118 &xx_ei.ce_value_fixed_initial, 0,
1119 &yy_ei.ce_value_fixed_initial
1123 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1124 &xx_ei.ce_value_random_initial, 0,
1125 &yy_ei.ce_value_random_initial
1129 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1130 &xx_ei.use_last_ce_value, FALSE,
1131 &yy_ei.use_last_ce_value
1136 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1137 &xx_ei.push_delay_fixed, 8,
1138 &yy_ei.push_delay_fixed
1142 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1143 &xx_ei.push_delay_random, 8,
1144 &yy_ei.push_delay_random
1148 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1149 &xx_ei.drop_delay_fixed, 0,
1150 &yy_ei.drop_delay_fixed
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1155 &xx_ei.drop_delay_random, 0,
1156 &yy_ei.drop_delay_random
1160 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1161 &xx_ei.move_delay_fixed, 0,
1162 &yy_ei.move_delay_fixed
1166 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1167 &xx_ei.move_delay_random, 0,
1168 &yy_ei.move_delay_random
1172 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1173 &xx_ei.step_delay_fixed, 0,
1174 &yy_ei.step_delay_fixed
1178 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1179 &xx_ei.step_delay_random, 0,
1180 &yy_ei.step_delay_random
1185 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1186 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1191 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1192 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1193 &yy_ei.move_direction_initial
1197 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1198 &xx_ei.move_stepsize, TILEX / 8,
1199 &yy_ei.move_stepsize
1204 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1205 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1206 &yy_ei.move_enter_element
1210 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1211 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1212 &yy_ei.move_leave_element
1216 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1217 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1218 &yy_ei.move_leave_type
1223 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1224 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1225 &yy_ei.slippery_type
1230 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1231 &xx_ei.explosion_type, EXPLODES_3X3,
1232 &yy_ei.explosion_type
1236 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1237 &xx_ei.explosion_delay, 16,
1238 &yy_ei.explosion_delay
1242 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1243 &xx_ei.ignition_delay, 8,
1244 &yy_ei.ignition_delay
1249 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1250 &xx_ei.content, EL_EMPTY_SPACE,
1252 &xx_num_contents, 1, 1
1255 // ---------- "num_change_pages" must be the last entry ---------------------
1258 -1, SAVE_CONF_ALWAYS,
1259 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1260 &xx_ei.num_change_pages, 1,
1261 &yy_ei.num_change_pages
1272 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1274 // ---------- "current_change_page" must be the first entry -----------------
1277 -1, SAVE_CONF_ALWAYS,
1278 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1279 &xx_current_change_page, -1
1282 // ---------- (the remaining entries can be in any order) -------------------
1286 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1287 &xx_change.can_change, FALSE
1292 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1293 &xx_event_bits[0], 0
1297 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1298 &xx_event_bits[1], 0
1303 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1304 &xx_change.trigger_player, CH_PLAYER_ANY
1308 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1309 &xx_change.trigger_side, CH_SIDE_ANY
1313 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1314 &xx_change.trigger_page, CH_PAGE_ANY
1319 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1320 &xx_change.target_element, EL_EMPTY_SPACE
1325 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1326 &xx_change.delay_fixed, 0
1330 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1331 &xx_change.delay_random, 0
1335 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1336 &xx_change.delay_frames, FRAMES_PER_SECOND
1341 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1342 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1347 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1348 &xx_change.explode, FALSE
1352 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1353 &xx_change.use_target_content, FALSE
1357 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1358 &xx_change.only_if_complete, FALSE
1362 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1363 &xx_change.use_random_replace, FALSE
1367 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1368 &xx_change.random_percentage, 100
1372 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1373 &xx_change.replace_when, CP_WHEN_EMPTY
1378 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1379 &xx_change.has_action, FALSE
1383 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1384 &xx_change.action_type, CA_NO_ACTION
1388 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1389 &xx_change.action_mode, CA_MODE_UNDEFINED
1393 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1394 &xx_change.action_arg, CA_ARG_UNDEFINED
1399 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1400 &xx_change.action_element, EL_EMPTY_SPACE
1405 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1406 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1407 &xx_num_contents, 1, 1
1417 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1421 TYPE_STRING, CONF_VALUE_BYTES(1),
1422 &xx_ei.description[0], -1, NULL,
1423 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1424 &xx_default_description[0]
1429 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1430 &xx_ei.use_gfx_element, FALSE
1434 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1435 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1440 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1441 &xx_group.choice_mode, ANIM_RANDOM
1446 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1447 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1448 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1458 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1462 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1463 &xx_ei.use_gfx_element, FALSE
1467 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1468 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1478 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1482 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1483 &li.block_snap_field, TRUE
1487 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1488 &li.continuous_snapping, TRUE
1492 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1493 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1497 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1498 &li.use_start_element[0], FALSE
1502 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1503 &li.start_element[0], EL_PLAYER_1
1507 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1508 &li.use_artwork_element[0], FALSE
1512 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1513 &li.artwork_element[0], EL_PLAYER_1
1517 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1518 &li.use_explosion_element[0], FALSE
1522 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1523 &li.explosion_element[0], EL_PLAYER_1
1538 filetype_id_list[] =
1540 { LEVEL_FILE_TYPE_RND, "RND" },
1541 { LEVEL_FILE_TYPE_BD, "BD" },
1542 { LEVEL_FILE_TYPE_EM, "EM" },
1543 { LEVEL_FILE_TYPE_SP, "SP" },
1544 { LEVEL_FILE_TYPE_DX, "DX" },
1545 { LEVEL_FILE_TYPE_SB, "SB" },
1546 { LEVEL_FILE_TYPE_DC, "DC" },
1547 { LEVEL_FILE_TYPE_MM, "MM" },
1548 { LEVEL_FILE_TYPE_MM, "DF" },
1553 // ============================================================================
1554 // level file functions
1555 // ============================================================================
1557 static boolean check_special_flags(char *flag)
1559 if (strEqual(options.special_flags, flag) ||
1560 strEqual(leveldir_current->special_flags, flag))
1566 static struct DateInfo getCurrentDate(void)
1568 time_t epoch_seconds = time(NULL);
1569 struct tm *now = localtime(&epoch_seconds);
1570 struct DateInfo date;
1572 date.year = now->tm_year + 1900;
1573 date.month = now->tm_mon + 1;
1574 date.day = now->tm_mday;
1576 date.src = DATE_SRC_CLOCK;
1581 static void resetEventFlags(struct ElementChangeInfo *change)
1585 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1586 change->has_event[i] = FALSE;
1589 static void resetEventBits(void)
1593 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1594 xx_event_bits[i] = 0;
1597 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1601 /* important: only change event flag if corresponding event bit is set
1602 (this is because all xx_event_bits[] values are loaded separately,
1603 and all xx_event_bits[] values are set back to zero before loading
1604 another value xx_event_bits[x] (each value representing 32 flags)) */
1606 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1607 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1608 change->has_event[i] = TRUE;
1611 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1615 /* in contrast to the above function setEventFlagsFromEventBits(), it
1616 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1617 depending on the corresponding change->has_event[i] values here, as
1618 all xx_event_bits[] values are reset in resetEventBits() before */
1620 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1621 if (change->has_event[i])
1622 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1625 static char *getDefaultElementDescription(struct ElementInfo *ei)
1627 static char description[MAX_ELEMENT_NAME_LEN + 1];
1628 char *default_description = (ei->custom_description != NULL ?
1629 ei->custom_description :
1630 ei->editor_description);
1633 // always start with reliable default values
1634 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1635 description[i] = '\0';
1637 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1638 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1640 return &description[0];
1643 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1645 char *default_description = getDefaultElementDescription(ei);
1648 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1649 ei->description[i] = default_description[i];
1652 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1656 for (i = 0; conf[i].data_type != -1; i++)
1658 int default_value = conf[i].default_value;
1659 int data_type = conf[i].data_type;
1660 int conf_type = conf[i].conf_type;
1661 int byte_mask = conf_type & CONF_MASK_BYTES;
1663 if (byte_mask == CONF_MASK_MULTI_BYTES)
1665 int default_num_entities = conf[i].default_num_entities;
1666 int max_num_entities = conf[i].max_num_entities;
1668 *(int *)(conf[i].num_entities) = default_num_entities;
1670 if (data_type == TYPE_STRING)
1672 char *default_string = conf[i].default_string;
1673 char *string = (char *)(conf[i].value);
1675 strncpy(string, default_string, max_num_entities);
1677 else if (data_type == TYPE_ELEMENT_LIST)
1679 int *element_array = (int *)(conf[i].value);
1682 for (j = 0; j < max_num_entities; j++)
1683 element_array[j] = default_value;
1685 else if (data_type == TYPE_CONTENT_LIST)
1687 struct Content *content = (struct Content *)(conf[i].value);
1690 for (c = 0; c < max_num_entities; c++)
1691 for (y = 0; y < 3; y++)
1692 for (x = 0; x < 3; x++)
1693 content[c].e[x][y] = default_value;
1696 else // constant size configuration data (1, 2 or 4 bytes)
1698 if (data_type == TYPE_BOOLEAN)
1699 *(boolean *)(conf[i].value) = default_value;
1701 *(int *) (conf[i].value) = default_value;
1706 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1710 for (i = 0; conf[i].data_type != -1; i++)
1712 int data_type = conf[i].data_type;
1713 int conf_type = conf[i].conf_type;
1714 int byte_mask = conf_type & CONF_MASK_BYTES;
1716 if (byte_mask == CONF_MASK_MULTI_BYTES)
1718 int max_num_entities = conf[i].max_num_entities;
1720 if (data_type == TYPE_STRING)
1722 char *string = (char *)(conf[i].value);
1723 char *string_copy = (char *)(conf[i].value_copy);
1725 strncpy(string_copy, string, max_num_entities);
1727 else if (data_type == TYPE_ELEMENT_LIST)
1729 int *element_array = (int *)(conf[i].value);
1730 int *element_array_copy = (int *)(conf[i].value_copy);
1733 for (j = 0; j < max_num_entities; j++)
1734 element_array_copy[j] = element_array[j];
1736 else if (data_type == TYPE_CONTENT_LIST)
1738 struct Content *content = (struct Content *)(conf[i].value);
1739 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1742 for (c = 0; c < max_num_entities; c++)
1743 for (y = 0; y < 3; y++)
1744 for (x = 0; x < 3; x++)
1745 content_copy[c].e[x][y] = content[c].e[x][y];
1748 else // constant size configuration data (1, 2 or 4 bytes)
1750 if (data_type == TYPE_BOOLEAN)
1751 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1753 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1758 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1762 xx_ei = *ei_from; // copy element data into temporary buffer
1763 yy_ei = *ei_to; // copy element data into temporary buffer
1765 copyConfigFromConfigList(chunk_config_CUSX_base);
1770 // ---------- reinitialize and copy change pages ----------
1772 ei_to->num_change_pages = ei_from->num_change_pages;
1773 ei_to->current_change_page = ei_from->current_change_page;
1775 setElementChangePages(ei_to, ei_to->num_change_pages);
1777 for (i = 0; i < ei_to->num_change_pages; i++)
1778 ei_to->change_page[i] = ei_from->change_page[i];
1780 // ---------- copy group element info ----------
1781 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1782 *ei_to->group = *ei_from->group;
1784 // mark this custom element as modified
1785 ei_to->modified_settings = TRUE;
1788 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1790 int change_page_size = sizeof(struct ElementChangeInfo);
1792 ei->num_change_pages = MAX(1, change_pages);
1795 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1797 if (ei->current_change_page >= ei->num_change_pages)
1798 ei->current_change_page = ei->num_change_pages - 1;
1800 ei->change = &ei->change_page[ei->current_change_page];
1803 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1805 xx_change = *change; // copy change data into temporary buffer
1807 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1809 *change = xx_change;
1811 resetEventFlags(change);
1813 change->direct_action = 0;
1814 change->other_action = 0;
1816 change->pre_change_function = NULL;
1817 change->change_function = NULL;
1818 change->post_change_function = NULL;
1821 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1825 li = *level; // copy level data into temporary buffer
1826 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1827 *level = li; // copy temporary buffer back to level data
1829 setLevelInfoToDefaults_BD();
1830 setLevelInfoToDefaults_EM();
1831 setLevelInfoToDefaults_SP();
1832 setLevelInfoToDefaults_MM();
1834 level->native_bd_level = &native_bd_level;
1835 level->native_em_level = &native_em_level;
1836 level->native_sp_level = &native_sp_level;
1837 level->native_mm_level = &native_mm_level;
1839 level->file_version = FILE_VERSION_ACTUAL;
1840 level->game_version = GAME_VERSION_ACTUAL;
1842 level->creation_date = getCurrentDate();
1844 level->encoding_16bit_field = TRUE;
1845 level->encoding_16bit_yamyam = TRUE;
1846 level->encoding_16bit_amoeba = TRUE;
1848 // clear level name and level author string buffers
1849 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1850 level->name[i] = '\0';
1851 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1852 level->author[i] = '\0';
1854 // set level name and level author to default values
1855 strcpy(level->name, NAMELESS_LEVEL_NAME);
1856 strcpy(level->author, ANONYMOUS_NAME);
1858 // set level playfield to playable default level with player and exit
1859 for (x = 0; x < MAX_LEV_FIELDX; x++)
1860 for (y = 0; y < MAX_LEV_FIELDY; y++)
1861 level->field[x][y] = EL_SAND;
1863 level->field[0][0] = EL_PLAYER_1;
1864 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1866 BorderElement = EL_STEELWALL;
1868 // detect custom elements when loading them
1869 level->file_has_custom_elements = FALSE;
1871 // set all bug compatibility flags to "false" => do not emulate this bug
1872 level->use_action_after_change_bug = FALSE;
1874 if (leveldir_current)
1876 // try to determine better author name than 'anonymous'
1877 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1879 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1880 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1884 switch (LEVELCLASS(leveldir_current))
1886 case LEVELCLASS_TUTORIAL:
1887 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1890 case LEVELCLASS_CONTRIB:
1891 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1892 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1895 case LEVELCLASS_PRIVATE:
1896 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1897 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1901 // keep default value
1908 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1910 static boolean clipboard_elements_initialized = FALSE;
1913 InitElementPropertiesStatic();
1915 li = *level; // copy level data into temporary buffer
1916 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1917 *level = li; // copy temporary buffer back to level data
1919 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1922 struct ElementInfo *ei = &element_info[element];
1924 if (element == EL_MM_GRAY_BALL)
1926 struct LevelInfo_MM *level_mm = level->native_mm_level;
1929 for (j = 0; j < level->num_mm_ball_contents; j++)
1930 level->mm_ball_content[j] =
1931 map_element_MM_to_RND(level_mm->ball_content[j]);
1934 // never initialize clipboard elements after the very first time
1935 // (to be able to use clipboard elements between several levels)
1936 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1939 if (IS_ENVELOPE(element))
1941 int envelope_nr = element - EL_ENVELOPE_1;
1943 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1945 level->envelope[envelope_nr] = xx_envelope;
1948 if (IS_CUSTOM_ELEMENT(element) ||
1949 IS_GROUP_ELEMENT(element) ||
1950 IS_INTERNAL_ELEMENT(element))
1952 xx_ei = *ei; // copy element data into temporary buffer
1954 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1959 setElementChangePages(ei, 1);
1960 setElementChangeInfoToDefaults(ei->change);
1962 if (IS_CUSTOM_ELEMENT(element) ||
1963 IS_GROUP_ELEMENT(element))
1965 setElementDescriptionToDefault(ei);
1967 ei->modified_settings = FALSE;
1970 if (IS_CUSTOM_ELEMENT(element) ||
1971 IS_INTERNAL_ELEMENT(element))
1973 // internal values used in level editor
1975 ei->access_type = 0;
1976 ei->access_layer = 0;
1977 ei->access_protected = 0;
1978 ei->walk_to_action = 0;
1979 ei->smash_targets = 0;
1982 ei->can_explode_by_fire = FALSE;
1983 ei->can_explode_smashed = FALSE;
1984 ei->can_explode_impact = FALSE;
1986 ei->current_change_page = 0;
1989 if (IS_GROUP_ELEMENT(element) ||
1990 IS_INTERNAL_ELEMENT(element))
1992 struct ElementGroupInfo *group;
1994 // initialize memory for list of elements in group
1995 if (ei->group == NULL)
1996 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2000 xx_group = *group; // copy group data into temporary buffer
2002 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2007 if (IS_EMPTY_ELEMENT(element) ||
2008 IS_INTERNAL_ELEMENT(element))
2010 xx_ei = *ei; // copy element data into temporary buffer
2012 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2018 clipboard_elements_initialized = TRUE;
2021 static void setLevelInfoToDefaults(struct LevelInfo *level,
2022 boolean level_info_only,
2023 boolean reset_file_status)
2025 setLevelInfoToDefaults_Level(level);
2027 if (!level_info_only)
2028 setLevelInfoToDefaults_Elements(level);
2030 if (reset_file_status)
2032 level->no_valid_file = FALSE;
2033 level->no_level_file = FALSE;
2036 level->changed = FALSE;
2039 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2041 level_file_info->nr = 0;
2042 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2043 level_file_info->packed = FALSE;
2045 setString(&level_file_info->basename, NULL);
2046 setString(&level_file_info->filename, NULL);
2049 int getMappedElement_SB(int, boolean);
2051 static void ActivateLevelTemplate(void)
2055 if (check_special_flags("load_xsb_to_ces"))
2057 // fill smaller playfields with padding "beyond border wall" elements
2058 if (level.fieldx < level_template.fieldx ||
2059 level.fieldy < level_template.fieldy)
2061 short field[level.fieldx][level.fieldy];
2062 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2063 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2064 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2065 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2067 // copy old playfield (which is smaller than the visible area)
2068 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2069 field[x][y] = level.field[x][y];
2071 // fill new, larger playfield with "beyond border wall" elements
2072 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2073 level.field[x][y] = getMappedElement_SB('_', TRUE);
2075 // copy the old playfield to the middle of the new playfield
2076 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2077 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2079 level.fieldx = new_fieldx;
2080 level.fieldy = new_fieldy;
2084 // Currently there is no special action needed to activate the template
2085 // data, because 'element_info' property settings overwrite the original
2086 // level data, while all other variables do not change.
2088 // Exception: 'from_level_template' elements in the original level playfield
2089 // are overwritten with the corresponding elements at the same position in
2090 // playfield from the level template.
2092 for (x = 0; x < level.fieldx; x++)
2093 for (y = 0; y < level.fieldy; y++)
2094 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2095 level.field[x][y] = level_template.field[x][y];
2097 if (check_special_flags("load_xsb_to_ces"))
2099 struct LevelInfo level_backup = level;
2101 // overwrite all individual level settings from template level settings
2102 level = level_template;
2104 // restore level file info
2105 level.file_info = level_backup.file_info;
2107 // restore playfield size
2108 level.fieldx = level_backup.fieldx;
2109 level.fieldy = level_backup.fieldy;
2111 // restore playfield content
2112 for (x = 0; x < level.fieldx; x++)
2113 for (y = 0; y < level.fieldy; y++)
2114 level.field[x][y] = level_backup.field[x][y];
2116 // restore name and author from individual level
2117 strcpy(level.name, level_backup.name);
2118 strcpy(level.author, level_backup.author);
2120 // restore flag "use_custom_template"
2121 level.use_custom_template = level_backup.use_custom_template;
2125 static boolean checkForPackageFromBasename_BD(char *basename)
2127 // check for native BD level file extensions
2128 if (!strSuffixLower(basename, ".bd") &&
2129 !strSuffixLower(basename, ".bdr") &&
2130 !strSuffixLower(basename, ".brc") &&
2131 !strSuffixLower(basename, ".gds"))
2134 // check for standard single-level BD files (like "001.bd")
2135 if (strSuffixLower(basename, ".bd") &&
2136 strlen(basename) == 6 &&
2137 basename[0] >= '0' && basename[0] <= '9' &&
2138 basename[1] >= '0' && basename[1] <= '9' &&
2139 basename[2] >= '0' && basename[2] <= '9')
2142 // this is a level package in native BD file format
2146 static char *getLevelFilenameFromBasename(char *basename)
2148 static char *filename = NULL;
2150 checked_free(filename);
2152 filename = getPath2(getCurrentLevelDir(), basename);
2157 static int getFileTypeFromBasename(char *basename)
2159 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2161 static char *filename = NULL;
2162 struct stat file_status;
2164 // ---------- try to determine file type from filename ----------
2166 // check for typical filename of a Supaplex level package file
2167 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2168 return LEVEL_FILE_TYPE_SP;
2170 // check for typical filename of a Diamond Caves II level package file
2171 if (strSuffixLower(basename, ".dc") ||
2172 strSuffixLower(basename, ".dc2"))
2173 return LEVEL_FILE_TYPE_DC;
2175 // check for typical filename of a Sokoban level package file
2176 if (strSuffixLower(basename, ".xsb") &&
2177 strchr(basename, '%') == NULL)
2178 return LEVEL_FILE_TYPE_SB;
2180 // check for typical filename of a Boulder Dash (GDash) level package file
2181 if (checkForPackageFromBasename_BD(basename))
2182 return LEVEL_FILE_TYPE_BD;
2184 // ---------- try to determine file type from filesize ----------
2186 checked_free(filename);
2187 filename = getPath2(getCurrentLevelDir(), basename);
2189 if (stat(filename, &file_status) == 0)
2191 // check for typical filesize of a Supaplex level package file
2192 if (file_status.st_size == 170496)
2193 return LEVEL_FILE_TYPE_SP;
2196 return LEVEL_FILE_TYPE_UNKNOWN;
2199 static int getFileTypeFromMagicBytes(char *filename, int type)
2203 if ((file = openFile(filename, MODE_READ)))
2205 char chunk_name[CHUNK_ID_LEN + 1];
2207 getFileChunkBE(file, chunk_name, NULL);
2209 if (strEqual(chunk_name, "MMII") ||
2210 strEqual(chunk_name, "MIRR"))
2211 type = LEVEL_FILE_TYPE_MM;
2219 static boolean checkForPackageFromBasename(char *basename)
2221 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2222 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2224 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2227 static char *getSingleLevelBasenameExt(int nr, char *extension)
2229 static char basename[MAX_FILENAME_LEN];
2232 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2234 sprintf(basename, "%03d.%s", nr, extension);
2239 static char *getSingleLevelBasename(int nr)
2241 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2244 static char *getPackedLevelBasename(int type)
2246 static char basename[MAX_FILENAME_LEN];
2247 char *directory = getCurrentLevelDir();
2249 DirectoryEntry *dir_entry;
2251 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2253 if ((dir = openDirectory(directory)) == NULL)
2255 Warn("cannot read current level directory '%s'", directory);
2260 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2262 char *entry_basename = dir_entry->basename;
2263 int entry_type = getFileTypeFromBasename(entry_basename);
2265 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2267 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2270 strcpy(basename, entry_basename);
2277 closeDirectory(dir);
2282 static char *getSingleLevelFilename(int nr)
2284 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2287 #if ENABLE_UNUSED_CODE
2288 static char *getPackedLevelFilename(int type)
2290 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2294 char *getDefaultLevelFilename(int nr)
2296 return getSingleLevelFilename(nr);
2299 #if ENABLE_UNUSED_CODE
2300 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2304 lfi->packed = FALSE;
2306 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2307 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2311 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2312 int type, char *format, ...)
2314 static char basename[MAX_FILENAME_LEN];
2317 va_start(ap, format);
2318 vsprintf(basename, format, ap);
2322 lfi->packed = FALSE;
2324 setString(&lfi->basename, basename);
2325 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2328 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2334 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2335 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2338 static int getFiletypeFromID(char *filetype_id)
2340 char *filetype_id_lower;
2341 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2344 if (filetype_id == NULL)
2345 return LEVEL_FILE_TYPE_UNKNOWN;
2347 filetype_id_lower = getStringToLower(filetype_id);
2349 for (i = 0; filetype_id_list[i].id != NULL; i++)
2351 char *id_lower = getStringToLower(filetype_id_list[i].id);
2353 if (strEqual(filetype_id_lower, id_lower))
2354 filetype = filetype_id_list[i].filetype;
2358 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2362 free(filetype_id_lower);
2367 char *getLocalLevelTemplateFilename(void)
2369 return getDefaultLevelFilename(-1);
2372 char *getGlobalLevelTemplateFilename(void)
2374 // global variable "leveldir_current" must be modified in the loop below
2375 LevelDirTree *leveldir_current_last = leveldir_current;
2376 char *filename = NULL;
2378 // check for template level in path from current to topmost tree node
2380 while (leveldir_current != NULL)
2382 filename = getDefaultLevelFilename(-1);
2384 if (fileExists(filename))
2387 leveldir_current = leveldir_current->node_parent;
2390 // restore global variable "leveldir_current" modified in above loop
2391 leveldir_current = leveldir_current_last;
2396 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2400 // special case: level number is negative => check for level template file
2403 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2404 getSingleLevelBasename(-1));
2406 // replace local level template filename with global template filename
2407 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2409 // no fallback if template file not existing
2413 // special case: check for file name/pattern specified in "levelinfo.conf"
2414 if (leveldir_current->level_filename != NULL)
2416 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2418 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2419 leveldir_current->level_filename, nr);
2421 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2423 if (fileExists(lfi->filename))
2426 else if (leveldir_current->level_filetype != NULL)
2428 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2430 // check for specified native level file with standard file name
2431 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2432 "%03d.%s", nr, LEVELFILE_EXTENSION);
2433 if (fileExists(lfi->filename))
2437 // check for native Rocks'n'Diamonds level file
2438 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2439 "%03d.%s", nr, LEVELFILE_EXTENSION);
2440 if (fileExists(lfi->filename))
2443 // check for native Boulder Dash level file
2444 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2445 if (fileExists(lfi->filename))
2448 // check for Emerald Mine level file (V1)
2449 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2450 'a' + (nr / 10) % 26, '0' + nr % 10);
2451 if (fileExists(lfi->filename))
2453 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2454 'A' + (nr / 10) % 26, '0' + nr % 10);
2455 if (fileExists(lfi->filename))
2458 // check for Emerald Mine level file (V2 to V5)
2459 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2460 if (fileExists(lfi->filename))
2463 // check for Emerald Mine level file (V6 / single mode)
2464 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2465 if (fileExists(lfi->filename))
2467 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2468 if (fileExists(lfi->filename))
2471 // check for Emerald Mine level file (V6 / teamwork mode)
2472 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2473 if (fileExists(lfi->filename))
2475 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2476 if (fileExists(lfi->filename))
2479 // check for various packed level file formats
2480 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2481 if (fileExists(lfi->filename))
2484 // no known level file found -- use default values (and fail later)
2485 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2486 "%03d.%s", nr, LEVELFILE_EXTENSION);
2489 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2491 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2492 lfi->type = getFileTypeFromBasename(lfi->basename);
2494 if (lfi->type == LEVEL_FILE_TYPE_RND)
2495 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2498 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2500 // always start with reliable default values
2501 setFileInfoToDefaults(level_file_info);
2503 level_file_info->nr = nr; // set requested level number
2505 determineLevelFileInfo_Filename(level_file_info);
2506 determineLevelFileInfo_Filetype(level_file_info);
2509 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2510 struct LevelFileInfo *lfi_to)
2512 lfi_to->nr = lfi_from->nr;
2513 lfi_to->type = lfi_from->type;
2514 lfi_to->packed = lfi_from->packed;
2516 setString(&lfi_to->basename, lfi_from->basename);
2517 setString(&lfi_to->filename, lfi_from->filename);
2520 // ----------------------------------------------------------------------------
2521 // functions for loading R'n'D level
2522 // ----------------------------------------------------------------------------
2524 int getMappedElement(int element)
2526 // remap some (historic, now obsolete) elements
2530 case EL_PLAYER_OBSOLETE:
2531 element = EL_PLAYER_1;
2534 case EL_KEY_OBSOLETE:
2538 case EL_EM_KEY_1_FILE_OBSOLETE:
2539 element = EL_EM_KEY_1;
2542 case EL_EM_KEY_2_FILE_OBSOLETE:
2543 element = EL_EM_KEY_2;
2546 case EL_EM_KEY_3_FILE_OBSOLETE:
2547 element = EL_EM_KEY_3;
2550 case EL_EM_KEY_4_FILE_OBSOLETE:
2551 element = EL_EM_KEY_4;
2554 case EL_ENVELOPE_OBSOLETE:
2555 element = EL_ENVELOPE_1;
2563 if (element >= NUM_FILE_ELEMENTS)
2565 Warn("invalid level element %d", element);
2567 element = EL_UNKNOWN;
2575 static int getMappedElementByVersion(int element, int game_version)
2577 // remap some elements due to certain game version
2579 if (game_version <= VERSION_IDENT(2,2,0,0))
2581 // map game font elements
2582 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2583 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2584 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2585 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2588 if (game_version < VERSION_IDENT(3,0,0,0))
2590 // map Supaplex gravity tube elements
2591 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2592 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2593 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2594 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2601 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2603 level->file_version = getFileVersion(file);
2604 level->game_version = getFileVersion(file);
2609 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2611 level->creation_date.year = getFile16BitBE(file);
2612 level->creation_date.month = getFile8Bit(file);
2613 level->creation_date.day = getFile8Bit(file);
2615 level->creation_date.src = DATE_SRC_LEVELFILE;
2620 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2622 int initial_player_stepsize;
2623 int initial_player_gravity;
2626 level->fieldx = getFile8Bit(file);
2627 level->fieldy = getFile8Bit(file);
2629 level->time = getFile16BitBE(file);
2630 level->gems_needed = getFile16BitBE(file);
2632 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2633 level->name[i] = getFile8Bit(file);
2634 level->name[MAX_LEVEL_NAME_LEN] = 0;
2636 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2637 level->score[i] = getFile8Bit(file);
2639 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2640 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2641 for (y = 0; y < 3; y++)
2642 for (x = 0; x < 3; x++)
2643 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2645 level->amoeba_speed = getFile8Bit(file);
2646 level->time_magic_wall = getFile8Bit(file);
2647 level->time_wheel = getFile8Bit(file);
2648 level->amoeba_content = getMappedElement(getFile8Bit(file));
2650 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2653 for (i = 0; i < MAX_PLAYERS; i++)
2654 level->initial_player_stepsize[i] = initial_player_stepsize;
2656 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2658 for (i = 0; i < MAX_PLAYERS; i++)
2659 level->initial_player_gravity[i] = initial_player_gravity;
2661 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2662 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2664 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2666 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2667 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2668 level->can_move_into_acid_bits = getFile32BitBE(file);
2669 level->dont_collide_with_bits = getFile8Bit(file);
2671 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2672 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2674 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2675 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2676 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2678 level->game_engine_type = getFile8Bit(file);
2680 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2685 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2689 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2690 level->name[i] = getFile8Bit(file);
2691 level->name[MAX_LEVEL_NAME_LEN] = 0;
2696 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2700 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2701 level->author[i] = getFile8Bit(file);
2702 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2707 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2710 int chunk_size_expected = level->fieldx * level->fieldy;
2712 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2713 stored with 16-bit encoding (and should be twice as big then).
2714 Even worse, playfield data was stored 16-bit when only yamyam content
2715 contained 16-bit elements and vice versa. */
2717 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2718 chunk_size_expected *= 2;
2720 if (chunk_size_expected != chunk_size)
2722 ReadUnusedBytesFromFile(file, chunk_size);
2723 return chunk_size_expected;
2726 for (y = 0; y < level->fieldy; y++)
2727 for (x = 0; x < level->fieldx; x++)
2728 level->field[x][y] =
2729 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2734 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2737 int header_size = 4;
2738 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2739 int chunk_size_expected = header_size + content_size;
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 += content_size;
2749 if (chunk_size_expected != chunk_size)
2751 ReadUnusedBytesFromFile(file, chunk_size);
2752 return chunk_size_expected;
2756 level->num_yamyam_contents = getFile8Bit(file);
2760 // correct invalid number of content fields -- should never happen
2761 if (level->num_yamyam_contents < 1 ||
2762 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2763 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2765 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2766 for (y = 0; y < 3; y++)
2767 for (x = 0; x < 3; x++)
2768 level->yamyam_content[i].e[x][y] =
2769 getMappedElement(level->encoding_16bit_field ?
2770 getFile16BitBE(file) : getFile8Bit(file));
2774 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2779 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2781 element = getMappedElement(getFile16BitBE(file));
2782 num_contents = getFile8Bit(file);
2784 getFile8Bit(file); // content x size (unused)
2785 getFile8Bit(file); // content y size (unused)
2787 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2789 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2790 for (y = 0; y < 3; y++)
2791 for (x = 0; x < 3; x++)
2792 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2794 // correct invalid number of content fields -- should never happen
2795 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2796 num_contents = STD_ELEMENT_CONTENTS;
2798 if (element == EL_YAMYAM)
2800 level->num_yamyam_contents = num_contents;
2802 for (i = 0; i < num_contents; i++)
2803 for (y = 0; y < 3; y++)
2804 for (x = 0; x < 3; x++)
2805 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2807 else if (element == EL_BD_AMOEBA)
2809 level->amoeba_content = content_array[0][0][0];
2813 Warn("cannot load content for element '%d'", element);
2819 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2825 int chunk_size_expected;
2827 element = getMappedElement(getFile16BitBE(file));
2828 if (!IS_ENVELOPE(element))
2829 element = EL_ENVELOPE_1;
2831 envelope_nr = element - EL_ENVELOPE_1;
2833 envelope_len = getFile16BitBE(file);
2835 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2836 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2838 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2840 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2841 if (chunk_size_expected != chunk_size)
2843 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2844 return chunk_size_expected;
2847 for (i = 0; i < envelope_len; i++)
2848 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2853 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2855 int num_changed_custom_elements = getFile16BitBE(file);
2856 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2859 if (chunk_size_expected != chunk_size)
2861 ReadUnusedBytesFromFile(file, chunk_size - 2);
2862 return chunk_size_expected;
2865 for (i = 0; i < num_changed_custom_elements; i++)
2867 int element = getMappedElement(getFile16BitBE(file));
2868 int properties = getFile32BitBE(file);
2870 if (IS_CUSTOM_ELEMENT(element))
2871 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2873 Warn("invalid custom element number %d", element);
2875 // older game versions that wrote level files with CUS1 chunks used
2876 // different default push delay values (not yet stored in level file)
2877 element_info[element].push_delay_fixed = 2;
2878 element_info[element].push_delay_random = 8;
2881 level->file_has_custom_elements = TRUE;
2886 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2888 int num_changed_custom_elements = getFile16BitBE(file);
2889 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2892 if (chunk_size_expected != chunk_size)
2894 ReadUnusedBytesFromFile(file, chunk_size - 2);
2895 return chunk_size_expected;
2898 for (i = 0; i < num_changed_custom_elements; i++)
2900 int element = getMappedElement(getFile16BitBE(file));
2901 int custom_target_element = getMappedElement(getFile16BitBE(file));
2903 if (IS_CUSTOM_ELEMENT(element))
2904 element_info[element].change->target_element = custom_target_element;
2906 Warn("invalid custom element number %d", element);
2909 level->file_has_custom_elements = TRUE;
2914 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2916 int num_changed_custom_elements = getFile16BitBE(file);
2917 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2920 if (chunk_size_expected != chunk_size)
2922 ReadUnusedBytesFromFile(file, chunk_size - 2);
2923 return chunk_size_expected;
2926 for (i = 0; i < num_changed_custom_elements; i++)
2928 int element = getMappedElement(getFile16BitBE(file));
2929 struct ElementInfo *ei = &element_info[element];
2930 unsigned int event_bits;
2932 if (!IS_CUSTOM_ELEMENT(element))
2934 Warn("invalid custom element number %d", element);
2936 element = EL_INTERNAL_DUMMY;
2939 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2940 ei->description[j] = getFile8Bit(file);
2941 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2943 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2945 // some free bytes for future properties and padding
2946 ReadUnusedBytesFromFile(file, 7);
2948 ei->use_gfx_element = getFile8Bit(file);
2949 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2951 ei->collect_score_initial = getFile8Bit(file);
2952 ei->collect_count_initial = getFile8Bit(file);
2954 ei->push_delay_fixed = getFile16BitBE(file);
2955 ei->push_delay_random = getFile16BitBE(file);
2956 ei->move_delay_fixed = getFile16BitBE(file);
2957 ei->move_delay_random = getFile16BitBE(file);
2959 ei->move_pattern = getFile16BitBE(file);
2960 ei->move_direction_initial = getFile8Bit(file);
2961 ei->move_stepsize = getFile8Bit(file);
2963 for (y = 0; y < 3; y++)
2964 for (x = 0; x < 3; x++)
2965 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2967 // bits 0 - 31 of "has_event[]"
2968 event_bits = getFile32BitBE(file);
2969 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2970 if (event_bits & (1u << j))
2971 ei->change->has_event[j] = TRUE;
2973 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2975 ei->change->delay_fixed = getFile16BitBE(file);
2976 ei->change->delay_random = getFile16BitBE(file);
2977 ei->change->delay_frames = getFile16BitBE(file);
2979 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2981 ei->change->explode = getFile8Bit(file);
2982 ei->change->use_target_content = getFile8Bit(file);
2983 ei->change->only_if_complete = getFile8Bit(file);
2984 ei->change->use_random_replace = getFile8Bit(file);
2986 ei->change->random_percentage = getFile8Bit(file);
2987 ei->change->replace_when = getFile8Bit(file);
2989 for (y = 0; y < 3; y++)
2990 for (x = 0; x < 3; x++)
2991 ei->change->target_content.e[x][y] =
2992 getMappedElement(getFile16BitBE(file));
2994 ei->slippery_type = getFile8Bit(file);
2996 // some free bytes for future properties and padding
2997 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2999 // mark that this custom element has been modified
3000 ei->modified_settings = TRUE;
3003 level->file_has_custom_elements = TRUE;
3008 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3010 struct ElementInfo *ei;
3011 int chunk_size_expected;
3015 // ---------- custom element base property values (96 bytes) ----------------
3017 element = getMappedElement(getFile16BitBE(file));
3019 if (!IS_CUSTOM_ELEMENT(element))
3021 Warn("invalid custom element number %d", element);
3023 ReadUnusedBytesFromFile(file, chunk_size - 2);
3028 ei = &element_info[element];
3030 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3031 ei->description[i] = getFile8Bit(file);
3032 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3034 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3036 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3038 ei->num_change_pages = getFile8Bit(file);
3040 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3041 if (chunk_size_expected != chunk_size)
3043 ReadUnusedBytesFromFile(file, chunk_size - 43);
3044 return chunk_size_expected;
3047 ei->ce_value_fixed_initial = getFile16BitBE(file);
3048 ei->ce_value_random_initial = getFile16BitBE(file);
3049 ei->use_last_ce_value = getFile8Bit(file);
3051 ei->use_gfx_element = getFile8Bit(file);
3052 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3054 ei->collect_score_initial = getFile8Bit(file);
3055 ei->collect_count_initial = getFile8Bit(file);
3057 ei->drop_delay_fixed = getFile8Bit(file);
3058 ei->push_delay_fixed = getFile8Bit(file);
3059 ei->drop_delay_random = getFile8Bit(file);
3060 ei->push_delay_random = getFile8Bit(file);
3061 ei->move_delay_fixed = getFile16BitBE(file);
3062 ei->move_delay_random = getFile16BitBE(file);
3064 // bits 0 - 15 of "move_pattern" ...
3065 ei->move_pattern = getFile16BitBE(file);
3066 ei->move_direction_initial = getFile8Bit(file);
3067 ei->move_stepsize = getFile8Bit(file);
3069 ei->slippery_type = getFile8Bit(file);
3071 for (y = 0; y < 3; y++)
3072 for (x = 0; x < 3; x++)
3073 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3075 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3076 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3077 ei->move_leave_type = getFile8Bit(file);
3079 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3080 ei->move_pattern |= (getFile16BitBE(file) << 16);
3082 ei->access_direction = getFile8Bit(file);
3084 ei->explosion_delay = getFile8Bit(file);
3085 ei->ignition_delay = getFile8Bit(file);
3086 ei->explosion_type = getFile8Bit(file);
3088 // some free bytes for future custom property values and padding
3089 ReadUnusedBytesFromFile(file, 1);
3091 // ---------- change page property values (48 bytes) ------------------------
3093 setElementChangePages(ei, ei->num_change_pages);
3095 for (i = 0; i < ei->num_change_pages; i++)
3097 struct ElementChangeInfo *change = &ei->change_page[i];
3098 unsigned int event_bits;
3100 // always start with reliable default values
3101 setElementChangeInfoToDefaults(change);
3103 // bits 0 - 31 of "has_event[]" ...
3104 event_bits = getFile32BitBE(file);
3105 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3106 if (event_bits & (1u << j))
3107 change->has_event[j] = TRUE;
3109 change->target_element = getMappedElement(getFile16BitBE(file));
3111 change->delay_fixed = getFile16BitBE(file);
3112 change->delay_random = getFile16BitBE(file);
3113 change->delay_frames = getFile16BitBE(file);
3115 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3117 change->explode = getFile8Bit(file);
3118 change->use_target_content = getFile8Bit(file);
3119 change->only_if_complete = getFile8Bit(file);
3120 change->use_random_replace = getFile8Bit(file);
3122 change->random_percentage = getFile8Bit(file);
3123 change->replace_when = getFile8Bit(file);
3125 for (y = 0; y < 3; y++)
3126 for (x = 0; x < 3; x++)
3127 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3129 change->can_change = getFile8Bit(file);
3131 change->trigger_side = getFile8Bit(file);
3133 change->trigger_player = getFile8Bit(file);
3134 change->trigger_page = getFile8Bit(file);
3136 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3137 CH_PAGE_ANY : (1 << change->trigger_page));
3139 change->has_action = getFile8Bit(file);
3140 change->action_type = getFile8Bit(file);
3141 change->action_mode = getFile8Bit(file);
3142 change->action_arg = getFile16BitBE(file);
3144 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3145 event_bits = getFile8Bit(file);
3146 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3147 if (event_bits & (1u << (j - 32)))
3148 change->has_event[j] = TRUE;
3151 // mark this custom element as modified
3152 ei->modified_settings = TRUE;
3154 level->file_has_custom_elements = TRUE;
3159 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3161 struct ElementInfo *ei;
3162 struct ElementGroupInfo *group;
3166 element = getMappedElement(getFile16BitBE(file));
3168 if (!IS_GROUP_ELEMENT(element))
3170 Warn("invalid group element number %d", element);
3172 ReadUnusedBytesFromFile(file, chunk_size - 2);
3177 ei = &element_info[element];
3179 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3180 ei->description[i] = getFile8Bit(file);
3181 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3183 group = element_info[element].group;
3185 group->num_elements = getFile8Bit(file);
3187 ei->use_gfx_element = getFile8Bit(file);
3188 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3190 group->choice_mode = getFile8Bit(file);
3192 // some free bytes for future values and padding
3193 ReadUnusedBytesFromFile(file, 3);
3195 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3196 group->element[i] = getMappedElement(getFile16BitBE(file));
3198 // mark this group element as modified
3199 element_info[element].modified_settings = TRUE;
3201 level->file_has_custom_elements = TRUE;
3206 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3207 int element, int real_element)
3209 int micro_chunk_size = 0;
3210 int conf_type = getFile8Bit(file);
3211 int byte_mask = conf_type & CONF_MASK_BYTES;
3212 boolean element_found = FALSE;
3215 micro_chunk_size += 1;
3217 if (byte_mask == CONF_MASK_MULTI_BYTES)
3219 int num_bytes = getFile16BitBE(file);
3220 byte *buffer = checked_malloc(num_bytes);
3222 ReadBytesFromFile(file, buffer, num_bytes);
3224 for (i = 0; conf[i].data_type != -1; i++)
3226 if (conf[i].element == element &&
3227 conf[i].conf_type == conf_type)
3229 int data_type = conf[i].data_type;
3230 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3231 int max_num_entities = conf[i].max_num_entities;
3233 if (num_entities > max_num_entities)
3235 Warn("truncating number of entities for element %d from %d to %d",
3236 element, num_entities, max_num_entities);
3238 num_entities = max_num_entities;
3241 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3242 data_type == TYPE_CONTENT_LIST))
3244 // for element and content lists, zero entities are not allowed
3245 Warn("found empty list of entities for element %d", element);
3247 // do not set "num_entities" here to prevent reading behind buffer
3249 *(int *)(conf[i].num_entities) = 1; // at least one is required
3253 *(int *)(conf[i].num_entities) = num_entities;
3256 element_found = TRUE;
3258 if (data_type == TYPE_STRING)
3260 char *string = (char *)(conf[i].value);
3263 for (j = 0; j < max_num_entities; j++)
3264 string[j] = (j < num_entities ? buffer[j] : '\0');
3266 else if (data_type == TYPE_ELEMENT_LIST)
3268 int *element_array = (int *)(conf[i].value);
3271 for (j = 0; j < num_entities; j++)
3273 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3275 else if (data_type == TYPE_CONTENT_LIST)
3277 struct Content *content= (struct Content *)(conf[i].value);
3280 for (c = 0; c < num_entities; c++)
3281 for (y = 0; y < 3; y++)
3282 for (x = 0; x < 3; x++)
3283 content[c].e[x][y] =
3284 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3287 element_found = FALSE;
3293 checked_free(buffer);
3295 micro_chunk_size += 2 + num_bytes;
3297 else // constant size configuration data (1, 2 or 4 bytes)
3299 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3300 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3301 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3303 for (i = 0; conf[i].data_type != -1; i++)
3305 if (conf[i].element == element &&
3306 conf[i].conf_type == conf_type)
3308 int data_type = conf[i].data_type;
3310 if (data_type == TYPE_ELEMENT)
3311 value = getMappedElement(value);
3313 if (data_type == TYPE_BOOLEAN)
3314 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3316 *(int *) (conf[i].value) = value;
3318 element_found = TRUE;
3324 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3329 char *error_conf_chunk_bytes =
3330 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3331 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3332 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3333 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3334 int error_element = real_element;
3336 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3337 error_conf_chunk_bytes, error_conf_chunk_token,
3338 error_element, EL_NAME(error_element));
3341 return micro_chunk_size;
3344 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3346 int real_chunk_size = 0;
3348 li = *level; // copy level data into temporary buffer
3350 while (!checkEndOfFile(file))
3352 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3354 if (real_chunk_size >= chunk_size)
3358 *level = li; // copy temporary buffer back to level data
3360 return real_chunk_size;
3363 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3365 int real_chunk_size = 0;
3367 li = *level; // copy level data into temporary buffer
3369 while (!checkEndOfFile(file))
3371 int element = getMappedElement(getFile16BitBE(file));
3373 real_chunk_size += 2;
3374 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3376 if (real_chunk_size >= chunk_size)
3380 *level = li; // copy temporary buffer back to level data
3382 return real_chunk_size;
3385 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3387 int real_chunk_size = 0;
3389 li = *level; // copy level data into temporary buffer
3391 while (!checkEndOfFile(file))
3393 int element = getMappedElement(getFile16BitBE(file));
3395 real_chunk_size += 2;
3396 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3398 if (real_chunk_size >= chunk_size)
3402 *level = li; // copy temporary buffer back to level data
3404 return real_chunk_size;
3407 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3409 int element = getMappedElement(getFile16BitBE(file));
3410 int envelope_nr = element - EL_ENVELOPE_1;
3411 int real_chunk_size = 2;
3413 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3415 while (!checkEndOfFile(file))
3417 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3420 if (real_chunk_size >= chunk_size)
3424 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3426 return real_chunk_size;
3429 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3431 int element = getMappedElement(getFile16BitBE(file));
3432 int real_chunk_size = 2;
3433 struct ElementInfo *ei = &element_info[element];
3436 xx_ei = *ei; // copy element data into temporary buffer
3438 xx_ei.num_change_pages = -1;
3440 while (!checkEndOfFile(file))
3442 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3444 if (xx_ei.num_change_pages != -1)
3447 if (real_chunk_size >= chunk_size)
3453 if (ei->num_change_pages == -1)
3455 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3458 ei->num_change_pages = 1;
3460 setElementChangePages(ei, 1);
3461 setElementChangeInfoToDefaults(ei->change);
3463 return real_chunk_size;
3466 // initialize number of change pages stored for this custom element
3467 setElementChangePages(ei, ei->num_change_pages);
3468 for (i = 0; i < ei->num_change_pages; i++)
3469 setElementChangeInfoToDefaults(&ei->change_page[i]);
3471 // start with reading properties for the first change page
3472 xx_current_change_page = 0;
3474 while (!checkEndOfFile(file))
3476 // level file might contain invalid change page number
3477 if (xx_current_change_page >= ei->num_change_pages)
3480 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3482 xx_change = *change; // copy change data into temporary buffer
3484 resetEventBits(); // reset bits; change page might have changed
3486 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3489 *change = xx_change;
3491 setEventFlagsFromEventBits(change);
3493 if (real_chunk_size >= chunk_size)
3497 level->file_has_custom_elements = TRUE;
3499 return real_chunk_size;
3502 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3504 int element = getMappedElement(getFile16BitBE(file));
3505 int real_chunk_size = 2;
3506 struct ElementInfo *ei = &element_info[element];
3507 struct ElementGroupInfo *group = ei->group;
3512 xx_ei = *ei; // copy element data into temporary buffer
3513 xx_group = *group; // copy group data into temporary buffer
3515 while (!checkEndOfFile(file))
3517 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3520 if (real_chunk_size >= chunk_size)
3527 level->file_has_custom_elements = TRUE;
3529 return real_chunk_size;
3532 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3534 int element = getMappedElement(getFile16BitBE(file));
3535 int real_chunk_size = 2;
3536 struct ElementInfo *ei = &element_info[element];
3538 xx_ei = *ei; // copy element data into temporary buffer
3540 while (!checkEndOfFile(file))
3542 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3545 if (real_chunk_size >= chunk_size)
3551 level->file_has_custom_elements = TRUE;
3553 return real_chunk_size;
3556 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3557 struct LevelFileInfo *level_file_info,
3558 boolean level_info_only)
3560 char *filename = level_file_info->filename;
3561 char cookie[MAX_LINE_LEN];
3562 char chunk_name[CHUNK_ID_LEN + 1];
3566 if (!(file = openFile(filename, MODE_READ)))
3568 level->no_valid_file = TRUE;
3569 level->no_level_file = TRUE;
3571 if (level_info_only)
3574 Warn("cannot read level '%s' -- using empty level", filename);
3576 if (!setup.editor.use_template_for_new_levels)
3579 // if level file not found, try to initialize level data from template
3580 filename = getGlobalLevelTemplateFilename();
3582 if (!(file = openFile(filename, MODE_READ)))
3585 // default: for empty levels, use level template for custom elements
3586 level->use_custom_template = TRUE;
3588 level->no_valid_file = FALSE;
3591 getFileChunkBE(file, chunk_name, NULL);
3592 if (strEqual(chunk_name, "RND1"))
3594 getFile32BitBE(file); // not used
3596 getFileChunkBE(file, chunk_name, NULL);
3597 if (!strEqual(chunk_name, "CAVE"))
3599 level->no_valid_file = TRUE;
3601 Warn("unknown format of level file '%s'", filename);
3608 else // check for pre-2.0 file format with cookie string
3610 strcpy(cookie, chunk_name);
3611 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3613 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3614 cookie[strlen(cookie) - 1] = '\0';
3616 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3618 level->no_valid_file = TRUE;
3620 Warn("unknown format of level file '%s'", filename);
3627 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3629 level->no_valid_file = TRUE;
3631 Warn("unsupported version of level file '%s'", filename);
3638 // pre-2.0 level files have no game version, so use file version here
3639 level->game_version = level->file_version;
3642 if (level->file_version < FILE_VERSION_1_2)
3644 // level files from versions before 1.2.0 without chunk structure
3645 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3646 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3654 int (*loader)(File *, int, struct LevelInfo *);
3658 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3659 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3660 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3661 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3662 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3663 { "INFO", -1, LoadLevel_INFO },
3664 { "BODY", -1, LoadLevel_BODY },
3665 { "CONT", -1, LoadLevel_CONT },
3666 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3667 { "CNT3", -1, LoadLevel_CNT3 },
3668 { "CUS1", -1, LoadLevel_CUS1 },
3669 { "CUS2", -1, LoadLevel_CUS2 },
3670 { "CUS3", -1, LoadLevel_CUS3 },
3671 { "CUS4", -1, LoadLevel_CUS4 },
3672 { "GRP1", -1, LoadLevel_GRP1 },
3673 { "CONF", -1, LoadLevel_CONF },
3674 { "ELEM", -1, LoadLevel_ELEM },
3675 { "NOTE", -1, LoadLevel_NOTE },
3676 { "CUSX", -1, LoadLevel_CUSX },
3677 { "GRPX", -1, LoadLevel_GRPX },
3678 { "EMPX", -1, LoadLevel_EMPX },
3683 while (getFileChunkBE(file, chunk_name, &chunk_size))
3687 while (chunk_info[i].name != NULL &&
3688 !strEqual(chunk_name, chunk_info[i].name))
3691 if (chunk_info[i].name == NULL)
3693 Warn("unknown chunk '%s' in level file '%s'",
3694 chunk_name, filename);
3696 ReadUnusedBytesFromFile(file, chunk_size);
3698 else if (chunk_info[i].size != -1 &&
3699 chunk_info[i].size != chunk_size)
3701 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3702 chunk_size, chunk_name, filename);
3704 ReadUnusedBytesFromFile(file, chunk_size);
3708 // call function to load this level chunk
3709 int chunk_size_expected =
3710 (chunk_info[i].loader)(file, chunk_size, level);
3712 if (chunk_size_expected < 0)
3714 Warn("error reading chunk '%s' in level file '%s'",
3715 chunk_name, filename);
3720 // the size of some chunks cannot be checked before reading other
3721 // chunks first (like "HEAD" and "BODY") that contain some header
3722 // information, so check them here
3723 if (chunk_size_expected != chunk_size)
3725 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3726 chunk_size, chunk_name, filename);
3738 // ----------------------------------------------------------------------------
3739 // functions for loading BD level
3740 // ----------------------------------------------------------------------------
3742 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3744 struct LevelInfo_BD *level_bd = level->native_bd_level;
3745 GdCave *cave = NULL; // will be changed below
3746 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3747 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3750 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3752 // cave and map newly allocated when set to defaults above
3753 cave = level_bd->cave;
3755 for (i = 0; i < 5; i++)
3757 cave->level_time[i] = level->time;
3758 cave->level_diamonds[i] = level->gems_needed;
3759 cave->level_magic_wall_time[i] = level->time_magic_wall;
3761 cave->level_speed[i] = level->bd_cycle_delay_ms;
3762 cave->level_ckdelay[i] = level->bd_cycle_delay_c64;
3763 cave->level_hatching_delay_frame[i] = level->bd_hatching_delay_cycles;
3764 cave->level_hatching_delay_time[i] = level->bd_hatching_delay_seconds;
3766 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3769 cave->diamond_value = level->score[SC_EMERALD];
3770 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3772 cave->scheduling = level->bd_scheduling_type;
3773 cave->pal_timing = level->bd_pal_timing;
3774 cave->intermission = level->bd_intermission;
3775 cave->diagonal_movements = level->bd_diagonal_movements;
3777 cave->lineshift = level->bd_line_shifting_borders;
3779 strncpy(cave->name, level->name, sizeof(GdString));
3780 cave->name[sizeof(GdString) - 1] = '\0';
3782 for (x = 0; x < cave->w; x++)
3783 for (y = 0; y < cave->h; y++)
3784 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3787 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3789 struct LevelInfo_BD *level_bd = level->native_bd_level;
3790 GdCave *cave = level_bd->cave;
3791 int bd_level_nr = level_bd->level_nr;
3794 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3795 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3797 level->time = cave->level_time[bd_level_nr];
3798 level->gems_needed = cave->level_diamonds[bd_level_nr];
3799 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3801 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3802 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3803 level->bd_hatching_delay_cycles = cave->level_hatching_delay_frame[bd_level_nr];
3804 level->bd_hatching_delay_seconds = cave->level_hatching_delay_time[bd_level_nr];
3806 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3807 level->score[SC_EMERALD] = cave->diamond_value;
3808 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3810 level->bd_scheduling_type = cave->scheduling;
3811 level->bd_pal_timing = cave->pal_timing;
3812 level->bd_intermission = cave->intermission;
3813 level->bd_diagonal_movements = cave->diagonal_movements;
3815 level->bd_line_shifting_borders = cave->lineshift;
3817 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3819 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3820 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3822 for (x = 0; x < level->fieldx; x++)
3823 for (y = 0; y < level->fieldy; y++)
3824 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3826 checked_free(cave_name);
3829 static void setTapeInfoToDefaults(void);
3831 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3833 struct LevelInfo_BD *level_bd = level->native_bd_level;
3834 GdCave *cave = level_bd->cave;
3835 GdReplay *replay = level_bd->replay;
3841 // always start with reliable default values
3842 setTapeInfoToDefaults();
3844 tape.level_nr = level_nr; // (currently not used)
3845 tape.random_seed = replay->seed;
3847 TapeSetDateFromIsoDateString(replay->date);
3850 tape.pos[tape.counter].delay = 0;
3852 tape.bd_replay = TRUE;
3854 // all time calculations only used to display approximate tape time
3855 int cave_speed = cave->speed;
3856 int milliseconds_game = 0;
3857 int milliseconds_elapsed = 20;
3859 for (i = 0; i < replay->movements->len; i++)
3861 int replay_action = replay->movements->data[i];
3862 int tape_action = map_action_BD_to_RND(replay_action);
3863 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3864 boolean success = 0;
3868 success = TapeAddAction(action);
3870 milliseconds_game += milliseconds_elapsed;
3872 if (milliseconds_game >= cave_speed)
3874 milliseconds_game -= cave_speed;
3881 tape.pos[tape.counter].delay = 0;
3882 tape.pos[tape.counter].action[0] = 0;
3886 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3892 TapeHaltRecording();
3896 // ----------------------------------------------------------------------------
3897 // functions for loading EM level
3898 // ----------------------------------------------------------------------------
3900 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3902 static int ball_xy[8][2] =
3913 struct LevelInfo_EM *level_em = level->native_em_level;
3914 struct CAVE *cav = level_em->cav;
3917 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3918 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3920 cav->time_seconds = level->time;
3921 cav->gems_needed = level->gems_needed;
3923 cav->emerald_score = level->score[SC_EMERALD];
3924 cav->diamond_score = level->score[SC_DIAMOND];
3925 cav->alien_score = level->score[SC_ROBOT];
3926 cav->tank_score = level->score[SC_SPACESHIP];
3927 cav->bug_score = level->score[SC_BUG];
3928 cav->eater_score = level->score[SC_YAMYAM];
3929 cav->nut_score = level->score[SC_NUT];
3930 cav->dynamite_score = level->score[SC_DYNAMITE];
3931 cav->key_score = level->score[SC_KEY];
3932 cav->exit_score = level->score[SC_TIME_BONUS];
3934 cav->num_eater_arrays = level->num_yamyam_contents;
3936 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3937 for (y = 0; y < 3; y++)
3938 for (x = 0; x < 3; x++)
3939 cav->eater_array[i][y * 3 + x] =
3940 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3942 cav->amoeba_time = level->amoeba_speed;
3943 cav->wonderwall_time = level->time_magic_wall;
3944 cav->wheel_time = level->time_wheel;
3946 cav->android_move_time = level->android_move_time;
3947 cav->android_clone_time = level->android_clone_time;
3948 cav->ball_random = level->ball_random;
3949 cav->ball_active = level->ball_active_initial;
3950 cav->ball_time = level->ball_time;
3951 cav->num_ball_arrays = level->num_ball_contents;
3953 cav->lenses_score = level->lenses_score;
3954 cav->magnify_score = level->magnify_score;
3955 cav->slurp_score = level->slurp_score;
3957 cav->lenses_time = level->lenses_time;
3958 cav->magnify_time = level->magnify_time;
3960 cav->wind_time = 9999;
3961 cav->wind_direction =
3962 map_direction_RND_to_EM(level->wind_direction_initial);
3964 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3965 for (j = 0; j < 8; j++)
3966 cav->ball_array[i][j] =
3967 map_element_RND_to_EM_cave(level->ball_content[i].
3968 e[ball_xy[j][0]][ball_xy[j][1]]);
3970 map_android_clone_elements_RND_to_EM(level);
3972 // first fill the complete playfield with the empty space element
3973 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3974 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3975 cav->cave[x][y] = Cblank;
3977 // then copy the real level contents from level file into the playfield
3978 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3980 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3982 if (level->field[x][y] == EL_AMOEBA_DEAD)
3983 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3985 cav->cave[x][y] = new_element;
3988 for (i = 0; i < MAX_PLAYERS; i++)
3990 cav->player_x[i] = -1;
3991 cav->player_y[i] = -1;
3994 // initialize player positions and delete players from the playfield
3995 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3997 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3999 int player_nr = GET_PLAYER_NR(level->field[x][y]);
4001 cav->player_x[player_nr] = x;
4002 cav->player_y[player_nr] = y;
4004 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4009 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4011 static int ball_xy[8][2] =
4022 struct LevelInfo_EM *level_em = level->native_em_level;
4023 struct CAVE *cav = level_em->cav;
4026 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4027 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4029 level->time = cav->time_seconds;
4030 level->gems_needed = cav->gems_needed;
4032 sprintf(level->name, "Level %d", level->file_info.nr);
4034 level->score[SC_EMERALD] = cav->emerald_score;
4035 level->score[SC_DIAMOND] = cav->diamond_score;
4036 level->score[SC_ROBOT] = cav->alien_score;
4037 level->score[SC_SPACESHIP] = cav->tank_score;
4038 level->score[SC_BUG] = cav->bug_score;
4039 level->score[SC_YAMYAM] = cav->eater_score;
4040 level->score[SC_NUT] = cav->nut_score;
4041 level->score[SC_DYNAMITE] = cav->dynamite_score;
4042 level->score[SC_KEY] = cav->key_score;
4043 level->score[SC_TIME_BONUS] = cav->exit_score;
4045 level->num_yamyam_contents = cav->num_eater_arrays;
4047 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4048 for (y = 0; y < 3; y++)
4049 for (x = 0; x < 3; x++)
4050 level->yamyam_content[i].e[x][y] =
4051 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4053 level->amoeba_speed = cav->amoeba_time;
4054 level->time_magic_wall = cav->wonderwall_time;
4055 level->time_wheel = cav->wheel_time;
4057 level->android_move_time = cav->android_move_time;
4058 level->android_clone_time = cav->android_clone_time;
4059 level->ball_random = cav->ball_random;
4060 level->ball_active_initial = cav->ball_active;
4061 level->ball_time = cav->ball_time;
4062 level->num_ball_contents = cav->num_ball_arrays;
4064 level->lenses_score = cav->lenses_score;
4065 level->magnify_score = cav->magnify_score;
4066 level->slurp_score = cav->slurp_score;
4068 level->lenses_time = cav->lenses_time;
4069 level->magnify_time = cav->magnify_time;
4071 level->wind_direction_initial =
4072 map_direction_EM_to_RND(cav->wind_direction);
4074 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4075 for (j = 0; j < 8; j++)
4076 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4077 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4079 map_android_clone_elements_EM_to_RND(level);
4081 // convert the playfield (some elements need special treatment)
4082 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4084 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4086 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4087 new_element = EL_AMOEBA_DEAD;
4089 level->field[x][y] = new_element;
4092 for (i = 0; i < MAX_PLAYERS; i++)
4094 // in case of all players set to the same field, use the first player
4095 int nr = MAX_PLAYERS - i - 1;
4096 int jx = cav->player_x[nr];
4097 int jy = cav->player_y[nr];
4099 if (jx != -1 && jy != -1)
4100 level->field[jx][jy] = EL_PLAYER_1 + nr;
4103 // time score is counted for each 10 seconds left in Emerald Mine levels
4104 level->time_score_base = 10;
4108 // ----------------------------------------------------------------------------
4109 // functions for loading SP level
4110 // ----------------------------------------------------------------------------
4112 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4114 struct LevelInfo_SP *level_sp = level->native_sp_level;
4115 LevelInfoType *header = &level_sp->header;
4118 level_sp->width = level->fieldx;
4119 level_sp->height = level->fieldy;
4121 for (x = 0; x < level->fieldx; x++)
4122 for (y = 0; y < level->fieldy; y++)
4123 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4125 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4127 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4128 header->LevelTitle[i] = level->name[i];
4129 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4131 header->InfotronsNeeded = level->gems_needed;
4133 header->SpecialPortCount = 0;
4135 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4137 boolean gravity_port_found = FALSE;
4138 boolean gravity_port_valid = FALSE;
4139 int gravity_port_flag;
4140 int gravity_port_base_element;
4141 int element = level->field[x][y];
4143 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4144 element <= EL_SP_GRAVITY_ON_PORT_UP)
4146 gravity_port_found = TRUE;
4147 gravity_port_valid = TRUE;
4148 gravity_port_flag = 1;
4149 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4151 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4152 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4154 gravity_port_found = TRUE;
4155 gravity_port_valid = TRUE;
4156 gravity_port_flag = 0;
4157 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4159 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4160 element <= EL_SP_GRAVITY_PORT_UP)
4162 // change R'n'D style gravity inverting special port to normal port
4163 // (there are no gravity inverting ports in native Supaplex engine)
4165 gravity_port_found = TRUE;
4166 gravity_port_valid = FALSE;
4167 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4170 if (gravity_port_found)
4172 if (gravity_port_valid &&
4173 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4175 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4177 port->PortLocation = (y * level->fieldx + x) * 2;
4178 port->Gravity = gravity_port_flag;
4180 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4182 header->SpecialPortCount++;
4186 // change special gravity port to normal port
4188 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4191 level_sp->playfield[x][y] = element - EL_SP_START;
4196 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4198 struct LevelInfo_SP *level_sp = level->native_sp_level;
4199 LevelInfoType *header = &level_sp->header;
4200 boolean num_invalid_elements = 0;
4203 level->fieldx = level_sp->width;
4204 level->fieldy = level_sp->height;
4206 for (x = 0; x < level->fieldx; x++)
4208 for (y = 0; y < level->fieldy; y++)
4210 int element_old = level_sp->playfield[x][y];
4211 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4213 if (element_new == EL_UNKNOWN)
4215 num_invalid_elements++;
4217 Debug("level:native:SP", "invalid element %d at position %d, %d",
4221 level->field[x][y] = element_new;
4225 if (num_invalid_elements > 0)
4226 Warn("found %d invalid elements%s", num_invalid_elements,
4227 (!options.debug ? " (use '--debug' for more details)" : ""));
4229 for (i = 0; i < MAX_PLAYERS; i++)
4230 level->initial_player_gravity[i] =
4231 (header->InitialGravity == 1 ? TRUE : FALSE);
4233 // skip leading spaces
4234 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4235 if (header->LevelTitle[i] != ' ')
4239 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4240 level->name[j] = header->LevelTitle[i];
4241 level->name[j] = '\0';
4243 // cut trailing spaces
4245 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4246 level->name[j - 1] = '\0';
4248 level->gems_needed = header->InfotronsNeeded;
4250 for (i = 0; i < header->SpecialPortCount; i++)
4252 SpecialPortType *port = &header->SpecialPort[i];
4253 int port_location = port->PortLocation;
4254 int gravity = port->Gravity;
4255 int port_x, port_y, port_element;
4257 port_x = (port_location / 2) % level->fieldx;
4258 port_y = (port_location / 2) / level->fieldx;
4260 if (port_x < 0 || port_x >= level->fieldx ||
4261 port_y < 0 || port_y >= level->fieldy)
4263 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4268 port_element = level->field[port_x][port_y];
4270 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4271 port_element > EL_SP_GRAVITY_PORT_UP)
4273 Warn("no special port at position (%d, %d)", port_x, port_y);
4278 // change previous (wrong) gravity inverting special port to either
4279 // gravity enabling special port or gravity disabling special port
4280 level->field[port_x][port_y] +=
4281 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4282 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4285 // change special gravity ports without database entries to normal ports
4286 for (x = 0; x < level->fieldx; x++)
4287 for (y = 0; y < level->fieldy; y++)
4288 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4289 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4290 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4292 level->time = 0; // no time limit
4293 level->amoeba_speed = 0;
4294 level->time_magic_wall = 0;
4295 level->time_wheel = 0;
4296 level->amoeba_content = EL_EMPTY;
4298 // original Supaplex does not use score values -- rate by playing time
4299 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4300 level->score[i] = 0;
4302 level->rate_time_over_score = TRUE;
4304 // there are no yamyams in supaplex levels
4305 for (i = 0; i < level->num_yamyam_contents; i++)
4306 for (x = 0; x < 3; x++)
4307 for (y = 0; y < 3; y++)
4308 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4311 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4313 struct LevelInfo_SP *level_sp = level->native_sp_level;
4314 struct DemoInfo_SP *demo = &level_sp->demo;
4317 // always start with reliable default values
4318 demo->is_available = FALSE;
4321 if (TAPE_IS_EMPTY(tape))
4324 demo->level_nr = tape.level_nr; // (currently not used)
4326 level_sp->header.DemoRandomSeed = tape.random_seed;
4330 for (i = 0; i < tape.length; i++)
4332 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4333 int demo_repeat = tape.pos[i].delay;
4334 int demo_entries = (demo_repeat + 15) / 16;
4336 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4338 Warn("tape truncated: size exceeds maximum SP demo size %d",
4344 for (j = 0; j < demo_repeat / 16; j++)
4345 demo->data[demo->length++] = 0xf0 | demo_action;
4347 if (demo_repeat % 16)
4348 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4351 demo->is_available = TRUE;
4354 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4356 struct LevelInfo_SP *level_sp = level->native_sp_level;
4357 struct DemoInfo_SP *demo = &level_sp->demo;
4358 char *filename = level->file_info.filename;
4361 // always start with reliable default values
4362 setTapeInfoToDefaults();
4364 if (!demo->is_available)
4367 tape.level_nr = demo->level_nr; // (currently not used)
4368 tape.random_seed = level_sp->header.DemoRandomSeed;
4370 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4373 tape.pos[tape.counter].delay = 0;
4375 for (i = 0; i < demo->length; i++)
4377 int demo_action = demo->data[i] & 0x0f;
4378 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4379 int tape_action = map_key_SP_to_RND(demo_action);
4380 int tape_repeat = demo_repeat + 1;
4381 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4382 boolean success = 0;
4385 for (j = 0; j < tape_repeat; j++)
4386 success = TapeAddAction(action);
4390 Warn("SP demo truncated: size exceeds maximum tape size %d",
4397 TapeHaltRecording();
4401 // ----------------------------------------------------------------------------
4402 // functions for loading MM level
4403 // ----------------------------------------------------------------------------
4405 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4407 struct LevelInfo_MM *level_mm = level->native_mm_level;
4410 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4411 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4413 level_mm->time = level->time;
4414 level_mm->kettles_needed = level->gems_needed;
4415 level_mm->auto_count_kettles = level->auto_count_gems;
4417 level_mm->mm_laser_red = level->mm_laser_red;
4418 level_mm->mm_laser_green = level->mm_laser_green;
4419 level_mm->mm_laser_blue = level->mm_laser_blue;
4421 level_mm->df_laser_red = level->df_laser_red;
4422 level_mm->df_laser_green = level->df_laser_green;
4423 level_mm->df_laser_blue = level->df_laser_blue;
4425 strcpy(level_mm->name, level->name);
4426 strcpy(level_mm->author, level->author);
4428 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4429 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4430 level_mm->score[SC_KEY] = level->score[SC_KEY];
4431 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4432 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4434 level_mm->amoeba_speed = level->amoeba_speed;
4435 level_mm->time_fuse = level->mm_time_fuse;
4436 level_mm->time_bomb = level->mm_time_bomb;
4437 level_mm->time_ball = level->mm_time_ball;
4438 level_mm->time_block = level->mm_time_block;
4440 level_mm->num_ball_contents = level->num_mm_ball_contents;
4441 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4442 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4443 level_mm->explode_ball = level->explode_mm_ball;
4445 for (i = 0; i < level->num_mm_ball_contents; i++)
4446 level_mm->ball_content[i] =
4447 map_element_RND_to_MM(level->mm_ball_content[i]);
4449 for (x = 0; x < level->fieldx; x++)
4450 for (y = 0; y < level->fieldy; y++)
4452 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4455 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4457 struct LevelInfo_MM *level_mm = level->native_mm_level;
4460 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4461 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4463 level->time = level_mm->time;
4464 level->gems_needed = level_mm->kettles_needed;
4465 level->auto_count_gems = level_mm->auto_count_kettles;
4467 level->mm_laser_red = level_mm->mm_laser_red;
4468 level->mm_laser_green = level_mm->mm_laser_green;
4469 level->mm_laser_blue = level_mm->mm_laser_blue;
4471 level->df_laser_red = level_mm->df_laser_red;
4472 level->df_laser_green = level_mm->df_laser_green;
4473 level->df_laser_blue = level_mm->df_laser_blue;
4475 strcpy(level->name, level_mm->name);
4477 // only overwrite author from 'levelinfo.conf' if author defined in level
4478 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4479 strcpy(level->author, level_mm->author);
4481 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4482 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4483 level->score[SC_KEY] = level_mm->score[SC_KEY];
4484 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4485 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4487 level->amoeba_speed = level_mm->amoeba_speed;
4488 level->mm_time_fuse = level_mm->time_fuse;
4489 level->mm_time_bomb = level_mm->time_bomb;
4490 level->mm_time_ball = level_mm->time_ball;
4491 level->mm_time_block = level_mm->time_block;
4493 level->num_mm_ball_contents = level_mm->num_ball_contents;
4494 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4495 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4496 level->explode_mm_ball = level_mm->explode_ball;
4498 for (i = 0; i < level->num_mm_ball_contents; i++)
4499 level->mm_ball_content[i] =
4500 map_element_MM_to_RND(level_mm->ball_content[i]);
4502 for (x = 0; x < level->fieldx; x++)
4503 for (y = 0; y < level->fieldy; y++)
4504 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4508 // ----------------------------------------------------------------------------
4509 // functions for loading DC level
4510 // ----------------------------------------------------------------------------
4512 #define DC_LEVEL_HEADER_SIZE 344
4514 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4517 static int last_data_encoded;
4521 int diff_hi, diff_lo;
4522 int data_hi, data_lo;
4523 unsigned short data_decoded;
4527 last_data_encoded = 0;
4534 diff = data_encoded - last_data_encoded;
4535 diff_hi = diff & ~0xff;
4536 diff_lo = diff & 0xff;
4540 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4541 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4542 data_hi = data_hi & 0xff00;
4544 data_decoded = data_hi | data_lo;
4546 last_data_encoded = data_encoded;
4548 offset1 = (offset1 + 1) % 31;
4549 offset2 = offset2 & 0xff;
4551 return data_decoded;
4554 static int getMappedElement_DC(int element)
4562 // 0x0117 - 0x036e: (?)
4565 // 0x042d - 0x0684: (?)
4581 element = EL_CRYSTAL;
4584 case 0x0e77: // quicksand (boulder)
4585 element = EL_QUICKSAND_FAST_FULL;
4588 case 0x0e99: // slow quicksand (boulder)
4589 element = EL_QUICKSAND_FULL;
4593 element = EL_EM_EXIT_OPEN;
4597 element = EL_EM_EXIT_CLOSED;
4601 element = EL_EM_STEEL_EXIT_OPEN;
4605 element = EL_EM_STEEL_EXIT_CLOSED;
4608 case 0x0f4f: // dynamite (lit 1)
4609 element = EL_EM_DYNAMITE_ACTIVE;
4612 case 0x0f57: // dynamite (lit 2)
4613 element = EL_EM_DYNAMITE_ACTIVE;
4616 case 0x0f5f: // dynamite (lit 3)
4617 element = EL_EM_DYNAMITE_ACTIVE;
4620 case 0x0f67: // dynamite (lit 4)
4621 element = EL_EM_DYNAMITE_ACTIVE;
4628 element = EL_AMOEBA_WET;
4632 element = EL_AMOEBA_DROP;
4636 element = EL_DC_MAGIC_WALL;
4640 element = EL_SPACESHIP_UP;
4644 element = EL_SPACESHIP_DOWN;
4648 element = EL_SPACESHIP_LEFT;
4652 element = EL_SPACESHIP_RIGHT;
4656 element = EL_BUG_UP;
4660 element = EL_BUG_DOWN;
4664 element = EL_BUG_LEFT;
4668 element = EL_BUG_RIGHT;
4672 element = EL_MOLE_UP;
4676 element = EL_MOLE_DOWN;
4680 element = EL_MOLE_LEFT;
4684 element = EL_MOLE_RIGHT;
4692 element = EL_YAMYAM_UP;
4696 element = EL_SWITCHGATE_OPEN;
4700 element = EL_SWITCHGATE_CLOSED;
4704 element = EL_DC_SWITCHGATE_SWITCH_UP;
4708 element = EL_TIMEGATE_CLOSED;
4711 case 0x144c: // conveyor belt switch (green)
4712 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4715 case 0x144f: // conveyor belt switch (red)
4716 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4719 case 0x1452: // conveyor belt switch (blue)
4720 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4724 element = EL_CONVEYOR_BELT_3_MIDDLE;
4728 element = EL_CONVEYOR_BELT_3_LEFT;
4732 element = EL_CONVEYOR_BELT_3_RIGHT;
4736 element = EL_CONVEYOR_BELT_1_MIDDLE;
4740 element = EL_CONVEYOR_BELT_1_LEFT;
4744 element = EL_CONVEYOR_BELT_1_RIGHT;
4748 element = EL_CONVEYOR_BELT_4_MIDDLE;
4752 element = EL_CONVEYOR_BELT_4_LEFT;
4756 element = EL_CONVEYOR_BELT_4_RIGHT;
4760 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4764 element = EL_EXPANDABLE_WALL_VERTICAL;
4768 element = EL_EXPANDABLE_WALL_ANY;
4771 case 0x14ce: // growing steel wall (left/right)
4772 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4775 case 0x14df: // growing steel wall (up/down)
4776 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4779 case 0x14e8: // growing steel wall (up/down/left/right)
4780 element = EL_EXPANDABLE_STEELWALL_ANY;
4784 element = EL_SHIELD_DEADLY;
4788 element = EL_EXTRA_TIME;
4796 element = EL_EMPTY_SPACE;
4799 case 0x1578: // quicksand (empty)
4800 element = EL_QUICKSAND_FAST_EMPTY;
4803 case 0x1579: // slow quicksand (empty)
4804 element = EL_QUICKSAND_EMPTY;
4814 element = EL_EM_DYNAMITE;
4817 case 0x15a1: // key (red)
4818 element = EL_EM_KEY_1;
4821 case 0x15a2: // key (yellow)
4822 element = EL_EM_KEY_2;
4825 case 0x15a3: // key (blue)
4826 element = EL_EM_KEY_4;
4829 case 0x15a4: // key (green)
4830 element = EL_EM_KEY_3;
4833 case 0x15a5: // key (white)
4834 element = EL_DC_KEY_WHITE;
4838 element = EL_WALL_SLIPPERY;
4845 case 0x15a8: // wall (not round)
4849 case 0x15a9: // (blue)
4850 element = EL_CHAR_A;
4853 case 0x15aa: // (blue)
4854 element = EL_CHAR_B;
4857 case 0x15ab: // (blue)
4858 element = EL_CHAR_C;
4861 case 0x15ac: // (blue)
4862 element = EL_CHAR_D;
4865 case 0x15ad: // (blue)
4866 element = EL_CHAR_E;
4869 case 0x15ae: // (blue)
4870 element = EL_CHAR_F;
4873 case 0x15af: // (blue)
4874 element = EL_CHAR_G;
4877 case 0x15b0: // (blue)
4878 element = EL_CHAR_H;
4881 case 0x15b1: // (blue)
4882 element = EL_CHAR_I;
4885 case 0x15b2: // (blue)
4886 element = EL_CHAR_J;
4889 case 0x15b3: // (blue)
4890 element = EL_CHAR_K;
4893 case 0x15b4: // (blue)
4894 element = EL_CHAR_L;
4897 case 0x15b5: // (blue)
4898 element = EL_CHAR_M;
4901 case 0x15b6: // (blue)
4902 element = EL_CHAR_N;
4905 case 0x15b7: // (blue)
4906 element = EL_CHAR_O;
4909 case 0x15b8: // (blue)
4910 element = EL_CHAR_P;
4913 case 0x15b9: // (blue)
4914 element = EL_CHAR_Q;
4917 case 0x15ba: // (blue)
4918 element = EL_CHAR_R;
4921 case 0x15bb: // (blue)
4922 element = EL_CHAR_S;
4925 case 0x15bc: // (blue)
4926 element = EL_CHAR_T;
4929 case 0x15bd: // (blue)
4930 element = EL_CHAR_U;
4933 case 0x15be: // (blue)
4934 element = EL_CHAR_V;
4937 case 0x15bf: // (blue)
4938 element = EL_CHAR_W;
4941 case 0x15c0: // (blue)
4942 element = EL_CHAR_X;
4945 case 0x15c1: // (blue)
4946 element = EL_CHAR_Y;
4949 case 0x15c2: // (blue)
4950 element = EL_CHAR_Z;
4953 case 0x15c3: // (blue)
4954 element = EL_CHAR_AUMLAUT;
4957 case 0x15c4: // (blue)
4958 element = EL_CHAR_OUMLAUT;
4961 case 0x15c5: // (blue)
4962 element = EL_CHAR_UUMLAUT;
4965 case 0x15c6: // (blue)
4966 element = EL_CHAR_0;
4969 case 0x15c7: // (blue)
4970 element = EL_CHAR_1;
4973 case 0x15c8: // (blue)
4974 element = EL_CHAR_2;
4977 case 0x15c9: // (blue)
4978 element = EL_CHAR_3;
4981 case 0x15ca: // (blue)
4982 element = EL_CHAR_4;
4985 case 0x15cb: // (blue)
4986 element = EL_CHAR_5;
4989 case 0x15cc: // (blue)
4990 element = EL_CHAR_6;
4993 case 0x15cd: // (blue)
4994 element = EL_CHAR_7;
4997 case 0x15ce: // (blue)
4998 element = EL_CHAR_8;
5001 case 0x15cf: // (blue)
5002 element = EL_CHAR_9;
5005 case 0x15d0: // (blue)
5006 element = EL_CHAR_PERIOD;
5009 case 0x15d1: // (blue)
5010 element = EL_CHAR_EXCLAM;
5013 case 0x15d2: // (blue)
5014 element = EL_CHAR_COLON;
5017 case 0x15d3: // (blue)
5018 element = EL_CHAR_LESS;
5021 case 0x15d4: // (blue)
5022 element = EL_CHAR_GREATER;
5025 case 0x15d5: // (blue)
5026 element = EL_CHAR_QUESTION;
5029 case 0x15d6: // (blue)
5030 element = EL_CHAR_COPYRIGHT;
5033 case 0x15d7: // (blue)
5034 element = EL_CHAR_UP;
5037 case 0x15d8: // (blue)
5038 element = EL_CHAR_DOWN;
5041 case 0x15d9: // (blue)
5042 element = EL_CHAR_BUTTON;
5045 case 0x15da: // (blue)
5046 element = EL_CHAR_PLUS;
5049 case 0x15db: // (blue)
5050 element = EL_CHAR_MINUS;
5053 case 0x15dc: // (blue)
5054 element = EL_CHAR_APOSTROPHE;
5057 case 0x15dd: // (blue)
5058 element = EL_CHAR_PARENLEFT;
5061 case 0x15de: // (blue)
5062 element = EL_CHAR_PARENRIGHT;
5065 case 0x15df: // (green)
5066 element = EL_CHAR_A;
5069 case 0x15e0: // (green)
5070 element = EL_CHAR_B;
5073 case 0x15e1: // (green)
5074 element = EL_CHAR_C;
5077 case 0x15e2: // (green)
5078 element = EL_CHAR_D;
5081 case 0x15e3: // (green)
5082 element = EL_CHAR_E;
5085 case 0x15e4: // (green)
5086 element = EL_CHAR_F;
5089 case 0x15e5: // (green)
5090 element = EL_CHAR_G;
5093 case 0x15e6: // (green)
5094 element = EL_CHAR_H;
5097 case 0x15e7: // (green)
5098 element = EL_CHAR_I;
5101 case 0x15e8: // (green)
5102 element = EL_CHAR_J;
5105 case 0x15e9: // (green)
5106 element = EL_CHAR_K;
5109 case 0x15ea: // (green)
5110 element = EL_CHAR_L;
5113 case 0x15eb: // (green)
5114 element = EL_CHAR_M;
5117 case 0x15ec: // (green)
5118 element = EL_CHAR_N;
5121 case 0x15ed: // (green)
5122 element = EL_CHAR_O;
5125 case 0x15ee: // (green)
5126 element = EL_CHAR_P;
5129 case 0x15ef: // (green)
5130 element = EL_CHAR_Q;
5133 case 0x15f0: // (green)
5134 element = EL_CHAR_R;
5137 case 0x15f1: // (green)
5138 element = EL_CHAR_S;
5141 case 0x15f2: // (green)
5142 element = EL_CHAR_T;
5145 case 0x15f3: // (green)
5146 element = EL_CHAR_U;
5149 case 0x15f4: // (green)
5150 element = EL_CHAR_V;
5153 case 0x15f5: // (green)
5154 element = EL_CHAR_W;
5157 case 0x15f6: // (green)
5158 element = EL_CHAR_X;
5161 case 0x15f7: // (green)
5162 element = EL_CHAR_Y;
5165 case 0x15f8: // (green)
5166 element = EL_CHAR_Z;
5169 case 0x15f9: // (green)
5170 element = EL_CHAR_AUMLAUT;
5173 case 0x15fa: // (green)
5174 element = EL_CHAR_OUMLAUT;
5177 case 0x15fb: // (green)
5178 element = EL_CHAR_UUMLAUT;
5181 case 0x15fc: // (green)
5182 element = EL_CHAR_0;
5185 case 0x15fd: // (green)
5186 element = EL_CHAR_1;
5189 case 0x15fe: // (green)
5190 element = EL_CHAR_2;
5193 case 0x15ff: // (green)
5194 element = EL_CHAR_3;
5197 case 0x1600: // (green)
5198 element = EL_CHAR_4;
5201 case 0x1601: // (green)
5202 element = EL_CHAR_5;
5205 case 0x1602: // (green)
5206 element = EL_CHAR_6;
5209 case 0x1603: // (green)
5210 element = EL_CHAR_7;
5213 case 0x1604: // (green)
5214 element = EL_CHAR_8;
5217 case 0x1605: // (green)
5218 element = EL_CHAR_9;
5221 case 0x1606: // (green)
5222 element = EL_CHAR_PERIOD;
5225 case 0x1607: // (green)
5226 element = EL_CHAR_EXCLAM;
5229 case 0x1608: // (green)
5230 element = EL_CHAR_COLON;
5233 case 0x1609: // (green)
5234 element = EL_CHAR_LESS;
5237 case 0x160a: // (green)
5238 element = EL_CHAR_GREATER;
5241 case 0x160b: // (green)
5242 element = EL_CHAR_QUESTION;
5245 case 0x160c: // (green)
5246 element = EL_CHAR_COPYRIGHT;
5249 case 0x160d: // (green)
5250 element = EL_CHAR_UP;
5253 case 0x160e: // (green)
5254 element = EL_CHAR_DOWN;
5257 case 0x160f: // (green)
5258 element = EL_CHAR_BUTTON;
5261 case 0x1610: // (green)
5262 element = EL_CHAR_PLUS;
5265 case 0x1611: // (green)
5266 element = EL_CHAR_MINUS;
5269 case 0x1612: // (green)
5270 element = EL_CHAR_APOSTROPHE;
5273 case 0x1613: // (green)
5274 element = EL_CHAR_PARENLEFT;
5277 case 0x1614: // (green)
5278 element = EL_CHAR_PARENRIGHT;
5281 case 0x1615: // (blue steel)
5282 element = EL_STEEL_CHAR_A;
5285 case 0x1616: // (blue steel)
5286 element = EL_STEEL_CHAR_B;
5289 case 0x1617: // (blue steel)
5290 element = EL_STEEL_CHAR_C;
5293 case 0x1618: // (blue steel)
5294 element = EL_STEEL_CHAR_D;
5297 case 0x1619: // (blue steel)
5298 element = EL_STEEL_CHAR_E;
5301 case 0x161a: // (blue steel)
5302 element = EL_STEEL_CHAR_F;
5305 case 0x161b: // (blue steel)
5306 element = EL_STEEL_CHAR_G;
5309 case 0x161c: // (blue steel)
5310 element = EL_STEEL_CHAR_H;
5313 case 0x161d: // (blue steel)
5314 element = EL_STEEL_CHAR_I;
5317 case 0x161e: // (blue steel)
5318 element = EL_STEEL_CHAR_J;
5321 case 0x161f: // (blue steel)
5322 element = EL_STEEL_CHAR_K;
5325 case 0x1620: // (blue steel)
5326 element = EL_STEEL_CHAR_L;
5329 case 0x1621: // (blue steel)
5330 element = EL_STEEL_CHAR_M;
5333 case 0x1622: // (blue steel)
5334 element = EL_STEEL_CHAR_N;
5337 case 0x1623: // (blue steel)
5338 element = EL_STEEL_CHAR_O;
5341 case 0x1624: // (blue steel)
5342 element = EL_STEEL_CHAR_P;
5345 case 0x1625: // (blue steel)
5346 element = EL_STEEL_CHAR_Q;
5349 case 0x1626: // (blue steel)
5350 element = EL_STEEL_CHAR_R;
5353 case 0x1627: // (blue steel)
5354 element = EL_STEEL_CHAR_S;
5357 case 0x1628: // (blue steel)
5358 element = EL_STEEL_CHAR_T;
5361 case 0x1629: // (blue steel)
5362 element = EL_STEEL_CHAR_U;
5365 case 0x162a: // (blue steel)
5366 element = EL_STEEL_CHAR_V;
5369 case 0x162b: // (blue steel)
5370 element = EL_STEEL_CHAR_W;
5373 case 0x162c: // (blue steel)
5374 element = EL_STEEL_CHAR_X;
5377 case 0x162d: // (blue steel)
5378 element = EL_STEEL_CHAR_Y;
5381 case 0x162e: // (blue steel)
5382 element = EL_STEEL_CHAR_Z;
5385 case 0x162f: // (blue steel)
5386 element = EL_STEEL_CHAR_AUMLAUT;
5389 case 0x1630: // (blue steel)
5390 element = EL_STEEL_CHAR_OUMLAUT;
5393 case 0x1631: // (blue steel)
5394 element = EL_STEEL_CHAR_UUMLAUT;
5397 case 0x1632: // (blue steel)
5398 element = EL_STEEL_CHAR_0;
5401 case 0x1633: // (blue steel)
5402 element = EL_STEEL_CHAR_1;
5405 case 0x1634: // (blue steel)
5406 element = EL_STEEL_CHAR_2;
5409 case 0x1635: // (blue steel)
5410 element = EL_STEEL_CHAR_3;
5413 case 0x1636: // (blue steel)
5414 element = EL_STEEL_CHAR_4;
5417 case 0x1637: // (blue steel)
5418 element = EL_STEEL_CHAR_5;
5421 case 0x1638: // (blue steel)
5422 element = EL_STEEL_CHAR_6;
5425 case 0x1639: // (blue steel)
5426 element = EL_STEEL_CHAR_7;
5429 case 0x163a: // (blue steel)
5430 element = EL_STEEL_CHAR_8;
5433 case 0x163b: // (blue steel)
5434 element = EL_STEEL_CHAR_9;
5437 case 0x163c: // (blue steel)
5438 element = EL_STEEL_CHAR_PERIOD;
5441 case 0x163d: // (blue steel)
5442 element = EL_STEEL_CHAR_EXCLAM;
5445 case 0x163e: // (blue steel)
5446 element = EL_STEEL_CHAR_COLON;
5449 case 0x163f: // (blue steel)
5450 element = EL_STEEL_CHAR_LESS;
5453 case 0x1640: // (blue steel)
5454 element = EL_STEEL_CHAR_GREATER;
5457 case 0x1641: // (blue steel)
5458 element = EL_STEEL_CHAR_QUESTION;
5461 case 0x1642: // (blue steel)
5462 element = EL_STEEL_CHAR_COPYRIGHT;
5465 case 0x1643: // (blue steel)
5466 element = EL_STEEL_CHAR_UP;
5469 case 0x1644: // (blue steel)
5470 element = EL_STEEL_CHAR_DOWN;
5473 case 0x1645: // (blue steel)
5474 element = EL_STEEL_CHAR_BUTTON;
5477 case 0x1646: // (blue steel)
5478 element = EL_STEEL_CHAR_PLUS;
5481 case 0x1647: // (blue steel)
5482 element = EL_STEEL_CHAR_MINUS;
5485 case 0x1648: // (blue steel)
5486 element = EL_STEEL_CHAR_APOSTROPHE;
5489 case 0x1649: // (blue steel)
5490 element = EL_STEEL_CHAR_PARENLEFT;
5493 case 0x164a: // (blue steel)
5494 element = EL_STEEL_CHAR_PARENRIGHT;
5497 case 0x164b: // (green steel)
5498 element = EL_STEEL_CHAR_A;
5501 case 0x164c: // (green steel)
5502 element = EL_STEEL_CHAR_B;
5505 case 0x164d: // (green steel)
5506 element = EL_STEEL_CHAR_C;
5509 case 0x164e: // (green steel)
5510 element = EL_STEEL_CHAR_D;
5513 case 0x164f: // (green steel)
5514 element = EL_STEEL_CHAR_E;
5517 case 0x1650: // (green steel)
5518 element = EL_STEEL_CHAR_F;
5521 case 0x1651: // (green steel)
5522 element = EL_STEEL_CHAR_G;
5525 case 0x1652: // (green steel)
5526 element = EL_STEEL_CHAR_H;
5529 case 0x1653: // (green steel)
5530 element = EL_STEEL_CHAR_I;
5533 case 0x1654: // (green steel)
5534 element = EL_STEEL_CHAR_J;
5537 case 0x1655: // (green steel)
5538 element = EL_STEEL_CHAR_K;
5541 case 0x1656: // (green steel)
5542 element = EL_STEEL_CHAR_L;
5545 case 0x1657: // (green steel)
5546 element = EL_STEEL_CHAR_M;
5549 case 0x1658: // (green steel)
5550 element = EL_STEEL_CHAR_N;
5553 case 0x1659: // (green steel)
5554 element = EL_STEEL_CHAR_O;
5557 case 0x165a: // (green steel)
5558 element = EL_STEEL_CHAR_P;
5561 case 0x165b: // (green steel)
5562 element = EL_STEEL_CHAR_Q;
5565 case 0x165c: // (green steel)
5566 element = EL_STEEL_CHAR_R;
5569 case 0x165d: // (green steel)
5570 element = EL_STEEL_CHAR_S;
5573 case 0x165e: // (green steel)
5574 element = EL_STEEL_CHAR_T;
5577 case 0x165f: // (green steel)
5578 element = EL_STEEL_CHAR_U;
5581 case 0x1660: // (green steel)
5582 element = EL_STEEL_CHAR_V;
5585 case 0x1661: // (green steel)
5586 element = EL_STEEL_CHAR_W;
5589 case 0x1662: // (green steel)
5590 element = EL_STEEL_CHAR_X;
5593 case 0x1663: // (green steel)
5594 element = EL_STEEL_CHAR_Y;
5597 case 0x1664: // (green steel)
5598 element = EL_STEEL_CHAR_Z;
5601 case 0x1665: // (green steel)
5602 element = EL_STEEL_CHAR_AUMLAUT;
5605 case 0x1666: // (green steel)
5606 element = EL_STEEL_CHAR_OUMLAUT;
5609 case 0x1667: // (green steel)
5610 element = EL_STEEL_CHAR_UUMLAUT;
5613 case 0x1668: // (green steel)
5614 element = EL_STEEL_CHAR_0;
5617 case 0x1669: // (green steel)
5618 element = EL_STEEL_CHAR_1;
5621 case 0x166a: // (green steel)
5622 element = EL_STEEL_CHAR_2;
5625 case 0x166b: // (green steel)
5626 element = EL_STEEL_CHAR_3;
5629 case 0x166c: // (green steel)
5630 element = EL_STEEL_CHAR_4;
5633 case 0x166d: // (green steel)
5634 element = EL_STEEL_CHAR_5;
5637 case 0x166e: // (green steel)
5638 element = EL_STEEL_CHAR_6;
5641 case 0x166f: // (green steel)
5642 element = EL_STEEL_CHAR_7;
5645 case 0x1670: // (green steel)
5646 element = EL_STEEL_CHAR_8;
5649 case 0x1671: // (green steel)
5650 element = EL_STEEL_CHAR_9;
5653 case 0x1672: // (green steel)
5654 element = EL_STEEL_CHAR_PERIOD;
5657 case 0x1673: // (green steel)
5658 element = EL_STEEL_CHAR_EXCLAM;
5661 case 0x1674: // (green steel)
5662 element = EL_STEEL_CHAR_COLON;
5665 case 0x1675: // (green steel)
5666 element = EL_STEEL_CHAR_LESS;
5669 case 0x1676: // (green steel)
5670 element = EL_STEEL_CHAR_GREATER;
5673 case 0x1677: // (green steel)
5674 element = EL_STEEL_CHAR_QUESTION;
5677 case 0x1678: // (green steel)
5678 element = EL_STEEL_CHAR_COPYRIGHT;
5681 case 0x1679: // (green steel)
5682 element = EL_STEEL_CHAR_UP;
5685 case 0x167a: // (green steel)
5686 element = EL_STEEL_CHAR_DOWN;
5689 case 0x167b: // (green steel)
5690 element = EL_STEEL_CHAR_BUTTON;
5693 case 0x167c: // (green steel)
5694 element = EL_STEEL_CHAR_PLUS;
5697 case 0x167d: // (green steel)
5698 element = EL_STEEL_CHAR_MINUS;
5701 case 0x167e: // (green steel)
5702 element = EL_STEEL_CHAR_APOSTROPHE;
5705 case 0x167f: // (green steel)
5706 element = EL_STEEL_CHAR_PARENLEFT;
5709 case 0x1680: // (green steel)
5710 element = EL_STEEL_CHAR_PARENRIGHT;
5713 case 0x1681: // gate (red)
5714 element = EL_EM_GATE_1;
5717 case 0x1682: // secret gate (red)
5718 element = EL_EM_GATE_1_GRAY;
5721 case 0x1683: // gate (yellow)
5722 element = EL_EM_GATE_2;
5725 case 0x1684: // secret gate (yellow)
5726 element = EL_EM_GATE_2_GRAY;
5729 case 0x1685: // gate (blue)
5730 element = EL_EM_GATE_4;
5733 case 0x1686: // secret gate (blue)
5734 element = EL_EM_GATE_4_GRAY;
5737 case 0x1687: // gate (green)
5738 element = EL_EM_GATE_3;
5741 case 0x1688: // secret gate (green)
5742 element = EL_EM_GATE_3_GRAY;
5745 case 0x1689: // gate (white)
5746 element = EL_DC_GATE_WHITE;
5749 case 0x168a: // secret gate (white)
5750 element = EL_DC_GATE_WHITE_GRAY;
5753 case 0x168b: // secret gate (no key)
5754 element = EL_DC_GATE_FAKE_GRAY;
5758 element = EL_ROBOT_WHEEL;
5762 element = EL_DC_TIMEGATE_SWITCH;
5766 element = EL_ACID_POOL_BOTTOM;
5770 element = EL_ACID_POOL_TOPLEFT;
5774 element = EL_ACID_POOL_TOPRIGHT;
5778 element = EL_ACID_POOL_BOTTOMLEFT;
5782 element = EL_ACID_POOL_BOTTOMRIGHT;
5786 element = EL_STEELWALL;
5790 element = EL_STEELWALL_SLIPPERY;
5793 case 0x1695: // steel wall (not round)
5794 element = EL_STEELWALL;
5797 case 0x1696: // steel wall (left)
5798 element = EL_DC_STEELWALL_1_LEFT;
5801 case 0x1697: // steel wall (bottom)
5802 element = EL_DC_STEELWALL_1_BOTTOM;
5805 case 0x1698: // steel wall (right)
5806 element = EL_DC_STEELWALL_1_RIGHT;
5809 case 0x1699: // steel wall (top)
5810 element = EL_DC_STEELWALL_1_TOP;
5813 case 0x169a: // steel wall (left/bottom)
5814 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5817 case 0x169b: // steel wall (right/bottom)
5818 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5821 case 0x169c: // steel wall (right/top)
5822 element = EL_DC_STEELWALL_1_TOPRIGHT;
5825 case 0x169d: // steel wall (left/top)
5826 element = EL_DC_STEELWALL_1_TOPLEFT;
5829 case 0x169e: // steel wall (right/bottom small)
5830 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5833 case 0x169f: // steel wall (left/bottom small)
5834 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5837 case 0x16a0: // steel wall (right/top small)
5838 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5841 case 0x16a1: // steel wall (left/top small)
5842 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5845 case 0x16a2: // steel wall (left/right)
5846 element = EL_DC_STEELWALL_1_VERTICAL;
5849 case 0x16a3: // steel wall (top/bottom)
5850 element = EL_DC_STEELWALL_1_HORIZONTAL;
5853 case 0x16a4: // steel wall 2 (left end)
5854 element = EL_DC_STEELWALL_2_LEFT;
5857 case 0x16a5: // steel wall 2 (right end)
5858 element = EL_DC_STEELWALL_2_RIGHT;
5861 case 0x16a6: // steel wall 2 (top end)
5862 element = EL_DC_STEELWALL_2_TOP;
5865 case 0x16a7: // steel wall 2 (bottom end)
5866 element = EL_DC_STEELWALL_2_BOTTOM;
5869 case 0x16a8: // steel wall 2 (left/right)
5870 element = EL_DC_STEELWALL_2_HORIZONTAL;
5873 case 0x16a9: // steel wall 2 (up/down)
5874 element = EL_DC_STEELWALL_2_VERTICAL;
5877 case 0x16aa: // steel wall 2 (mid)
5878 element = EL_DC_STEELWALL_2_MIDDLE;
5882 element = EL_SIGN_EXCLAMATION;
5886 element = EL_SIGN_RADIOACTIVITY;
5890 element = EL_SIGN_STOP;
5894 element = EL_SIGN_WHEELCHAIR;
5898 element = EL_SIGN_PARKING;
5902 element = EL_SIGN_NO_ENTRY;
5906 element = EL_SIGN_HEART;
5910 element = EL_SIGN_GIVE_WAY;
5914 element = EL_SIGN_ENTRY_FORBIDDEN;
5918 element = EL_SIGN_EMERGENCY_EXIT;
5922 element = EL_SIGN_YIN_YANG;
5926 element = EL_WALL_EMERALD;
5930 element = EL_WALL_DIAMOND;
5934 element = EL_WALL_PEARL;
5938 element = EL_WALL_CRYSTAL;
5942 element = EL_INVISIBLE_WALL;
5946 element = EL_INVISIBLE_STEELWALL;
5950 // EL_INVISIBLE_SAND
5953 element = EL_LIGHT_SWITCH;
5957 element = EL_ENVELOPE_1;
5961 if (element >= 0x0117 && element <= 0x036e) // (?)
5962 element = EL_DIAMOND;
5963 else if (element >= 0x042d && element <= 0x0684) // (?)
5964 element = EL_EMERALD;
5965 else if (element >= 0x157c && element <= 0x158b)
5967 else if (element >= 0x1590 && element <= 0x159f)
5968 element = EL_DC_LANDMINE;
5969 else if (element >= 0x16bc && element <= 0x16cb)
5970 element = EL_INVISIBLE_SAND;
5973 Warn("unknown Diamond Caves element 0x%04x", element);
5975 element = EL_UNKNOWN;
5980 return getMappedElement(element);
5983 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5985 byte header[DC_LEVEL_HEADER_SIZE];
5987 int envelope_header_pos = 62;
5988 int envelope_content_pos = 94;
5989 int level_name_pos = 251;
5990 int level_author_pos = 292;
5991 int envelope_header_len;
5992 int envelope_content_len;
5994 int level_author_len;
5996 int num_yamyam_contents;
5999 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
6001 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6003 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6005 header[i * 2 + 0] = header_word >> 8;
6006 header[i * 2 + 1] = header_word & 0xff;
6009 // read some values from level header to check level decoding integrity
6010 fieldx = header[6] | (header[7] << 8);
6011 fieldy = header[8] | (header[9] << 8);
6012 num_yamyam_contents = header[60] | (header[61] << 8);
6014 // do some simple sanity checks to ensure that level was correctly decoded
6015 if (fieldx < 1 || fieldx > 256 ||
6016 fieldy < 1 || fieldy > 256 ||
6017 num_yamyam_contents < 1 || num_yamyam_contents > 8)
6019 level->no_valid_file = TRUE;
6021 Warn("cannot decode level from stream -- using empty level");
6026 // maximum envelope header size is 31 bytes
6027 envelope_header_len = header[envelope_header_pos];
6028 // maximum envelope content size is 110 (156?) bytes
6029 envelope_content_len = header[envelope_content_pos];
6031 // maximum level title size is 40 bytes
6032 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6033 // maximum level author size is 30 (51?) bytes
6034 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6038 for (i = 0; i < envelope_header_len; i++)
6039 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6040 level->envelope[0].text[envelope_size++] =
6041 header[envelope_header_pos + 1 + i];
6043 if (envelope_header_len > 0 && envelope_content_len > 0)
6045 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6046 level->envelope[0].text[envelope_size++] = '\n';
6047 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6048 level->envelope[0].text[envelope_size++] = '\n';
6051 for (i = 0; i < envelope_content_len; i++)
6052 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6053 level->envelope[0].text[envelope_size++] =
6054 header[envelope_content_pos + 1 + i];
6056 level->envelope[0].text[envelope_size] = '\0';
6058 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6059 level->envelope[0].ysize = 10;
6060 level->envelope[0].autowrap = TRUE;
6061 level->envelope[0].centered = TRUE;
6063 for (i = 0; i < level_name_len; i++)
6064 level->name[i] = header[level_name_pos + 1 + i];
6065 level->name[level_name_len] = '\0';
6067 for (i = 0; i < level_author_len; i++)
6068 level->author[i] = header[level_author_pos + 1 + i];
6069 level->author[level_author_len] = '\0';
6071 num_yamyam_contents = header[60] | (header[61] << 8);
6072 level->num_yamyam_contents =
6073 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6075 for (i = 0; i < num_yamyam_contents; i++)
6077 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6079 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6080 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6082 if (i < MAX_ELEMENT_CONTENTS)
6083 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6087 fieldx = header[6] | (header[7] << 8);
6088 fieldy = header[8] | (header[9] << 8);
6089 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6090 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6092 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6094 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6095 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6097 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6098 level->field[x][y] = getMappedElement_DC(element_dc);
6101 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6102 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6103 level->field[x][y] = EL_PLAYER_1;
6105 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6106 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6107 level->field[x][y] = EL_PLAYER_2;
6109 level->gems_needed = header[18] | (header[19] << 8);
6111 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6112 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6113 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6114 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6115 level->score[SC_NUT] = header[28] | (header[29] << 8);
6116 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6117 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6118 level->score[SC_BUG] = header[34] | (header[35] << 8);
6119 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6120 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6121 level->score[SC_KEY] = header[40] | (header[41] << 8);
6122 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6124 level->time = header[44] | (header[45] << 8);
6126 level->amoeba_speed = header[46] | (header[47] << 8);
6127 level->time_light = header[48] | (header[49] << 8);
6128 level->time_timegate = header[50] | (header[51] << 8);
6129 level->time_wheel = header[52] | (header[53] << 8);
6130 level->time_magic_wall = header[54] | (header[55] << 8);
6131 level->extra_time = header[56] | (header[57] << 8);
6132 level->shield_normal_time = header[58] | (header[59] << 8);
6134 // shield and extra time elements do not have a score
6135 level->score[SC_SHIELD] = 0;
6136 level->extra_time_score = 0;
6138 // set time for normal and deadly shields to the same value
6139 level->shield_deadly_time = level->shield_normal_time;
6141 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6142 // can slip down from flat walls, like normal walls and steel walls
6143 level->em_slippery_gems = TRUE;
6145 // time score is counted for each 10 seconds left in Diamond Caves levels
6146 level->time_score_base = 10;
6149 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6150 struct LevelFileInfo *level_file_info,
6151 boolean level_info_only)
6153 char *filename = level_file_info->filename;
6155 int num_magic_bytes = 8;
6156 char magic_bytes[num_magic_bytes + 1];
6157 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6159 if (!(file = openFile(filename, MODE_READ)))
6161 level->no_valid_file = TRUE;
6163 if (!level_info_only)
6164 Warn("cannot read level '%s' -- using empty level", filename);
6169 // fseek(file, 0x0000, SEEK_SET);
6171 if (level_file_info->packed)
6173 // read "magic bytes" from start of file
6174 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6175 magic_bytes[0] = '\0';
6177 // check "magic bytes" for correct file format
6178 if (!strPrefix(magic_bytes, "DC2"))
6180 level->no_valid_file = TRUE;
6182 Warn("unknown DC level file '%s' -- using empty level", filename);
6187 if (strPrefix(magic_bytes, "DC2Win95") ||
6188 strPrefix(magic_bytes, "DC2Win98"))
6190 int position_first_level = 0x00fa;
6191 int extra_bytes = 4;
6194 // advance file stream to first level inside the level package
6195 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6197 // each block of level data is followed by block of non-level data
6198 num_levels_to_skip *= 2;
6200 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6201 while (num_levels_to_skip >= 0)
6203 // advance file stream to next level inside the level package
6204 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6206 level->no_valid_file = TRUE;
6208 Warn("cannot fseek in file '%s' -- using empty level", filename);
6213 // skip apparently unused extra bytes following each level
6214 ReadUnusedBytesFromFile(file, extra_bytes);
6216 // read size of next level in level package
6217 skip_bytes = getFile32BitLE(file);
6219 num_levels_to_skip--;
6224 level->no_valid_file = TRUE;
6226 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6232 LoadLevelFromFileStream_DC(file, level);
6238 // ----------------------------------------------------------------------------
6239 // functions for loading SB level
6240 // ----------------------------------------------------------------------------
6242 int getMappedElement_SB(int element_ascii, boolean use_ces)
6250 sb_element_mapping[] =
6252 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6253 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6254 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6255 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6256 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6257 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6258 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6259 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6266 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6267 if (element_ascii == sb_element_mapping[i].ascii)
6268 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6270 return EL_UNDEFINED;
6273 static void SetLevelSettings_SB(struct LevelInfo *level)
6277 level->use_step_counter = TRUE;
6280 level->score[SC_TIME_BONUS] = 0;
6281 level->time_score_base = 1;
6282 level->rate_time_over_score = TRUE;
6285 level->auto_exit_sokoban = TRUE;
6288 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6289 struct LevelFileInfo *level_file_info,
6290 boolean level_info_only)
6292 char *filename = level_file_info->filename;
6293 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6294 char last_comment[MAX_LINE_LEN];
6295 char level_name[MAX_LINE_LEN];
6298 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6299 boolean read_continued_line = FALSE;
6300 boolean reading_playfield = FALSE;
6301 boolean got_valid_playfield_line = FALSE;
6302 boolean invalid_playfield_char = FALSE;
6303 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6304 int file_level_nr = 0;
6305 int x = 0, y = 0; // initialized to make compilers happy
6307 last_comment[0] = '\0';
6308 level_name[0] = '\0';
6310 if (!(file = openFile(filename, MODE_READ)))
6312 level->no_valid_file = TRUE;
6314 if (!level_info_only)
6315 Warn("cannot read level '%s' -- using empty level", filename);
6320 while (!checkEndOfFile(file))
6322 // level successfully read, but next level may follow here
6323 if (!got_valid_playfield_line && reading_playfield)
6325 // read playfield from single level file -- skip remaining file
6326 if (!level_file_info->packed)
6329 if (file_level_nr >= num_levels_to_skip)
6334 last_comment[0] = '\0';
6335 level_name[0] = '\0';
6337 reading_playfield = FALSE;
6340 got_valid_playfield_line = FALSE;
6342 // read next line of input file
6343 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6346 // cut trailing line break (this can be newline and/or carriage return)
6347 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6348 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6351 // copy raw input line for later use (mainly debugging output)
6352 strcpy(line_raw, line);
6354 if (read_continued_line)
6356 // append new line to existing line, if there is enough space
6357 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6358 strcat(previous_line, line_ptr);
6360 strcpy(line, previous_line); // copy storage buffer to line
6362 read_continued_line = FALSE;
6365 // if the last character is '\', continue at next line
6366 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6368 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6369 strcpy(previous_line, line); // copy line to storage buffer
6371 read_continued_line = TRUE;
6377 if (line[0] == '\0')
6380 // extract comment text from comment line
6383 for (line_ptr = line; *line_ptr; line_ptr++)
6384 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6387 strcpy(last_comment, line_ptr);
6392 // extract level title text from line containing level title
6393 if (line[0] == '\'')
6395 strcpy(level_name, &line[1]);
6397 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6398 level_name[strlen(level_name) - 1] = '\0';
6403 // skip lines containing only spaces (or empty lines)
6404 for (line_ptr = line; *line_ptr; line_ptr++)
6405 if (*line_ptr != ' ')
6407 if (*line_ptr == '\0')
6410 // at this point, we have found a line containing part of a playfield
6412 got_valid_playfield_line = TRUE;
6414 if (!reading_playfield)
6416 reading_playfield = TRUE;
6417 invalid_playfield_char = FALSE;
6419 for (x = 0; x < MAX_LEV_FIELDX; x++)
6420 for (y = 0; y < MAX_LEV_FIELDY; y++)
6421 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6426 // start with topmost tile row
6430 // skip playfield line if larger row than allowed
6431 if (y >= MAX_LEV_FIELDY)
6434 // start with leftmost tile column
6437 // read playfield elements from line
6438 for (line_ptr = line; *line_ptr; line_ptr++)
6440 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6442 // stop parsing playfield line if larger column than allowed
6443 if (x >= MAX_LEV_FIELDX)
6446 if (mapped_sb_element == EL_UNDEFINED)
6448 invalid_playfield_char = TRUE;
6453 level->field[x][y] = mapped_sb_element;
6455 // continue with next tile column
6458 level->fieldx = MAX(x, level->fieldx);
6461 if (invalid_playfield_char)
6463 // if first playfield line, treat invalid lines as comment lines
6465 reading_playfield = FALSE;
6470 // continue with next tile row
6478 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6479 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6481 if (!reading_playfield)
6483 level->no_valid_file = TRUE;
6485 Warn("cannot read level '%s' -- using empty level", filename);
6490 if (*level_name != '\0')
6492 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6493 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6495 else if (*last_comment != '\0')
6497 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6498 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6502 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6505 // set all empty fields beyond the border walls to invisible steel wall
6506 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6508 if ((x == 0 || x == level->fieldx - 1 ||
6509 y == 0 || y == level->fieldy - 1) &&
6510 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6511 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6512 level->field, level->fieldx, level->fieldy);
6515 // set special level settings for Sokoban levels
6516 SetLevelSettings_SB(level);
6518 if (load_xsb_to_ces)
6520 // special global settings can now be set in level template
6521 level->use_custom_template = TRUE;
6526 // -------------------------------------------------------------------------
6527 // functions for handling native levels
6528 // -------------------------------------------------------------------------
6530 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6531 struct LevelFileInfo *level_file_info,
6532 boolean level_info_only)
6536 // determine position of requested level inside level package
6537 if (level_file_info->packed)
6538 pos = level_file_info->nr - leveldir_current->first_level;
6540 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6541 level->no_valid_file = TRUE;
6544 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6545 struct LevelFileInfo *level_file_info,
6546 boolean level_info_only)
6548 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6549 level->no_valid_file = TRUE;
6552 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6553 struct LevelFileInfo *level_file_info,
6554 boolean level_info_only)
6558 // determine position of requested level inside level package
6559 if (level_file_info->packed)
6560 pos = level_file_info->nr - leveldir_current->first_level;
6562 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6563 level->no_valid_file = TRUE;
6566 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6567 struct LevelFileInfo *level_file_info,
6568 boolean level_info_only)
6570 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6571 level->no_valid_file = TRUE;
6574 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6576 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6577 CopyNativeLevel_RND_to_BD(level);
6578 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6579 CopyNativeLevel_RND_to_EM(level);
6580 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6581 CopyNativeLevel_RND_to_SP(level);
6582 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6583 CopyNativeLevel_RND_to_MM(level);
6586 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6588 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6589 CopyNativeLevel_BD_to_RND(level);
6590 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6591 CopyNativeLevel_EM_to_RND(level);
6592 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6593 CopyNativeLevel_SP_to_RND(level);
6594 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6595 CopyNativeLevel_MM_to_RND(level);
6598 void SaveNativeLevel(struct LevelInfo *level)
6600 // saving native level files only supported for some game engines
6601 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6602 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6605 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6606 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6607 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6608 char *filename = getLevelFilenameFromBasename(basename);
6610 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6613 boolean success = FALSE;
6615 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6617 CopyNativeLevel_RND_to_BD(level);
6618 // CopyNativeTape_RND_to_BD(level);
6620 success = SaveNativeLevel_BD(filename);
6622 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6624 CopyNativeLevel_RND_to_SP(level);
6625 CopyNativeTape_RND_to_SP(level);
6627 success = SaveNativeLevel_SP(filename);
6631 Request("Native level file saved!", REQ_CONFIRM);
6633 Request("Failed to save native level file!", REQ_CONFIRM);
6637 // ----------------------------------------------------------------------------
6638 // functions for loading generic level
6639 // ----------------------------------------------------------------------------
6641 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6642 struct LevelFileInfo *level_file_info,
6643 boolean level_info_only)
6645 // always start with reliable default values
6646 setLevelInfoToDefaults(level, level_info_only, TRUE);
6648 switch (level_file_info->type)
6650 case LEVEL_FILE_TYPE_RND:
6651 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6654 case LEVEL_FILE_TYPE_BD:
6655 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6656 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6659 case LEVEL_FILE_TYPE_EM:
6660 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6661 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6664 case LEVEL_FILE_TYPE_SP:
6665 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6666 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6669 case LEVEL_FILE_TYPE_MM:
6670 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6671 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6674 case LEVEL_FILE_TYPE_DC:
6675 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6678 case LEVEL_FILE_TYPE_SB:
6679 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6683 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6687 // if level file is invalid, restore level structure to default values
6688 if (level->no_valid_file)
6689 setLevelInfoToDefaults(level, level_info_only, FALSE);
6691 if (check_special_flags("use_native_bd_game_engine"))
6692 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6694 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6695 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6697 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6698 CopyNativeLevel_Native_to_RND(level);
6701 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6703 static struct LevelFileInfo level_file_info;
6705 // always start with reliable default values
6706 setFileInfoToDefaults(&level_file_info);
6708 level_file_info.nr = 0; // unknown level number
6709 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6711 setString(&level_file_info.filename, filename);
6713 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6716 static void LoadLevel_InitVersion(struct LevelInfo *level)
6720 if (leveldir_current == NULL) // only when dumping level
6723 // all engine modifications also valid for levels which use latest engine
6724 if (level->game_version < VERSION_IDENT(3,2,0,5))
6726 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6727 level->time_score_base = 10;
6730 if (leveldir_current->latest_engine)
6732 // ---------- use latest game engine --------------------------------------
6734 /* For all levels which are forced to use the latest game engine version
6735 (normally all but user contributed, private and undefined levels), set
6736 the game engine version to the actual version; this allows for actual
6737 corrections in the game engine to take effect for existing, converted
6738 levels (from "classic" or other existing games) to make the emulation
6739 of the corresponding game more accurate, while (hopefully) not breaking
6740 existing levels created from other players. */
6742 level->game_version = GAME_VERSION_ACTUAL;
6744 /* Set special EM style gems behaviour: EM style gems slip down from
6745 normal, steel and growing wall. As this is a more fundamental change,
6746 it seems better to set the default behaviour to "off" (as it is more
6747 natural) and make it configurable in the level editor (as a property
6748 of gem style elements). Already existing converted levels (neither
6749 private nor contributed levels) are changed to the new behaviour. */
6751 if (level->file_version < FILE_VERSION_2_0)
6752 level->em_slippery_gems = TRUE;
6757 // ---------- use game engine the level was created with --------------------
6759 /* For all levels which are not forced to use the latest game engine
6760 version (normally user contributed, private and undefined levels),
6761 use the version of the game engine the levels were created for.
6763 Since 2.0.1, the game engine version is now directly stored
6764 in the level file (chunk "VERS"), so there is no need anymore
6765 to set the game version from the file version (except for old,
6766 pre-2.0 levels, where the game version is still taken from the
6767 file format version used to store the level -- see above). */
6769 // player was faster than enemies in 1.0.0 and before
6770 if (level->file_version == FILE_VERSION_1_0)
6771 for (i = 0; i < MAX_PLAYERS; i++)
6772 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6774 // default behaviour for EM style gems was "slippery" only in 2.0.1
6775 if (level->game_version == VERSION_IDENT(2,0,1,0))
6776 level->em_slippery_gems = TRUE;
6778 // springs could be pushed over pits before (pre-release version) 2.2.0
6779 if (level->game_version < VERSION_IDENT(2,2,0,0))
6780 level->use_spring_bug = TRUE;
6782 if (level->game_version < VERSION_IDENT(3,2,0,5))
6784 // time orb caused limited time in endless time levels before 3.2.0-5
6785 level->use_time_orb_bug = TRUE;
6787 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6788 level->block_snap_field = FALSE;
6790 // extra time score was same value as time left score before 3.2.0-5
6791 level->extra_time_score = level->score[SC_TIME_BONUS];
6794 if (level->game_version < VERSION_IDENT(3,2,0,7))
6796 // default behaviour for snapping was "not continuous" before 3.2.0-7
6797 level->continuous_snapping = FALSE;
6800 // only few elements were able to actively move into acid before 3.1.0
6801 // trigger settings did not exist before 3.1.0; set to default "any"
6802 if (level->game_version < VERSION_IDENT(3,1,0,0))
6804 // correct "can move into acid" settings (all zero in old levels)
6806 level->can_move_into_acid_bits = 0; // nothing can move into acid
6807 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6809 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6810 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6811 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6812 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6814 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6815 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6817 // correct trigger settings (stored as zero == "none" in old levels)
6819 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6821 int element = EL_CUSTOM_START + i;
6822 struct ElementInfo *ei = &element_info[element];
6824 for (j = 0; j < ei->num_change_pages; j++)
6826 struct ElementChangeInfo *change = &ei->change_page[j];
6828 change->trigger_player = CH_PLAYER_ANY;
6829 change->trigger_page = CH_PAGE_ANY;
6834 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6836 int element = EL_CUSTOM_256;
6837 struct ElementInfo *ei = &element_info[element];
6838 struct ElementChangeInfo *change = &ei->change_page[0];
6840 /* This is needed to fix a problem that was caused by a bugfix in function
6841 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6842 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6843 not replace walkable elements, but instead just placed the player on it,
6844 without placing the Sokoban field under the player). Unfortunately, this
6845 breaks "Snake Bite" style levels when the snake is halfway through a door
6846 that just closes (the snake head is still alive and can be moved in this
6847 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6848 player (without Sokoban element) which then gets killed as designed). */
6850 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6851 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6852 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6853 change->target_element = EL_PLAYER_1;
6856 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6857 if (level->game_version < VERSION_IDENT(3,2,5,0))
6859 /* This is needed to fix a problem that was caused by a bugfix in function
6860 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6861 corrects the behaviour when a custom element changes to another custom
6862 element with a higher element number that has change actions defined.
6863 Normally, only one change per frame is allowed for custom elements.
6864 Therefore, it is checked if a custom element already changed in the
6865 current frame; if it did, subsequent changes are suppressed.
6866 Unfortunately, this is only checked for element changes, but not for
6867 change actions, which are still executed. As the function above loops
6868 through all custom elements from lower to higher, an element change
6869 resulting in a lower CE number won't be checked again, while a target
6870 element with a higher number will also be checked, and potential change
6871 actions will get executed for this CE, too (which is wrong), while
6872 further changes are ignored (which is correct). As this bugfix breaks
6873 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6874 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6875 behaviour for existing levels and tapes that make use of this bug */
6877 level->use_action_after_change_bug = TRUE;
6880 // not centering level after relocating player was default only in 3.2.3
6881 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6882 level->shifted_relocation = TRUE;
6884 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6885 if (level->game_version < VERSION_IDENT(3,2,6,0))
6886 level->em_explodes_by_fire = TRUE;
6888 // levels were solved by the first player entering an exit up to 4.1.0.0
6889 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6890 level->solved_by_one_player = TRUE;
6892 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6893 if (level->game_version < VERSION_IDENT(4,1,1,1))
6894 level->use_life_bugs = TRUE;
6896 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6897 if (level->game_version < VERSION_IDENT(4,1,1,1))
6898 level->sb_objects_needed = FALSE;
6900 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6901 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6902 level->finish_dig_collect = FALSE;
6904 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6905 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6906 level->keep_walkable_ce = TRUE;
6909 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6911 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6914 // check if this level is (not) a Sokoban level
6915 for (y = 0; y < level->fieldy; y++)
6916 for (x = 0; x < level->fieldx; x++)
6917 if (!IS_SB_ELEMENT(Tile[x][y]))
6918 is_sokoban_level = FALSE;
6920 if (is_sokoban_level)
6922 // set special level settings for Sokoban levels
6923 SetLevelSettings_SB(level);
6927 static void LoadLevel_InitSettings(struct LevelInfo *level)
6929 // adjust level settings for (non-native) Sokoban-style levels
6930 LoadLevel_InitSettings_SB(level);
6932 // rename levels with title "nameless level" or if renaming is forced
6933 if (leveldir_current->empty_level_name != NULL &&
6934 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6935 leveldir_current->force_level_name))
6936 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6937 leveldir_current->empty_level_name, level_nr);
6940 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6944 // map elements that have changed in newer versions
6945 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6946 level->game_version);
6947 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6948 for (x = 0; x < 3; x++)
6949 for (y = 0; y < 3; y++)
6950 level->yamyam_content[i].e[x][y] =
6951 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6952 level->game_version);
6956 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6960 // map custom element change events that have changed in newer versions
6961 // (these following values were accidentally changed in version 3.0.1)
6962 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6963 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6965 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6967 int element = EL_CUSTOM_START + i;
6969 // order of checking and copying events to be mapped is important
6970 // (do not change the start and end value -- they are constant)
6971 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6973 if (HAS_CHANGE_EVENT(element, j - 2))
6975 SET_CHANGE_EVENT(element, j - 2, FALSE);
6976 SET_CHANGE_EVENT(element, j, TRUE);
6980 // order of checking and copying events to be mapped is important
6981 // (do not change the start and end value -- they are constant)
6982 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6984 if (HAS_CHANGE_EVENT(element, j - 1))
6986 SET_CHANGE_EVENT(element, j - 1, FALSE);
6987 SET_CHANGE_EVENT(element, j, TRUE);
6993 // initialize "can_change" field for old levels with only one change page
6994 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6996 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6998 int element = EL_CUSTOM_START + i;
7000 if (CAN_CHANGE(element))
7001 element_info[element].change->can_change = TRUE;
7005 // correct custom element values (for old levels without these options)
7006 if (level->game_version < VERSION_IDENT(3,1,1,0))
7008 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7010 int element = EL_CUSTOM_START + i;
7011 struct ElementInfo *ei = &element_info[element];
7013 if (ei->access_direction == MV_NO_DIRECTION)
7014 ei->access_direction = MV_ALL_DIRECTIONS;
7018 // correct custom element values (fix invalid values for all versions)
7021 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7023 int element = EL_CUSTOM_START + i;
7024 struct ElementInfo *ei = &element_info[element];
7026 for (j = 0; j < ei->num_change_pages; j++)
7028 struct ElementChangeInfo *change = &ei->change_page[j];
7030 if (change->trigger_player == CH_PLAYER_NONE)
7031 change->trigger_player = CH_PLAYER_ANY;
7033 if (change->trigger_side == CH_SIDE_NONE)
7034 change->trigger_side = CH_SIDE_ANY;
7039 // initialize "can_explode" field for old levels which did not store this
7040 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7041 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7043 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7045 int element = EL_CUSTOM_START + i;
7047 if (EXPLODES_1X1_OLD(element))
7048 element_info[element].explosion_type = EXPLODES_1X1;
7050 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7051 EXPLODES_SMASHED(element) ||
7052 EXPLODES_IMPACT(element)));
7056 // correct previously hard-coded move delay values for maze runner style
7057 if (level->game_version < VERSION_IDENT(3,1,1,0))
7059 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7061 int element = EL_CUSTOM_START + i;
7063 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7065 // previously hard-coded and therefore ignored
7066 element_info[element].move_delay_fixed = 9;
7067 element_info[element].move_delay_random = 0;
7072 // set some other uninitialized values of custom elements in older levels
7073 if (level->game_version < VERSION_IDENT(3,1,0,0))
7075 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7077 int element = EL_CUSTOM_START + i;
7079 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7081 element_info[element].explosion_delay = 17;
7082 element_info[element].ignition_delay = 8;
7086 // set mouse click change events to work for left/middle/right mouse button
7087 if (level->game_version < VERSION_IDENT(4,2,3,0))
7089 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7091 int element = EL_CUSTOM_START + i;
7092 struct ElementInfo *ei = &element_info[element];
7094 for (j = 0; j < ei->num_change_pages; j++)
7096 struct ElementChangeInfo *change = &ei->change_page[j];
7098 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7099 change->has_event[CE_PRESSED_BY_MOUSE] ||
7100 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7101 change->has_event[CE_MOUSE_PRESSED_ON_X])
7102 change->trigger_side = CH_SIDE_ANY;
7108 static void LoadLevel_InitElements(struct LevelInfo *level)
7110 LoadLevel_InitStandardElements(level);
7112 if (level->file_has_custom_elements)
7113 LoadLevel_InitCustomElements(level);
7115 // initialize element properties for level editor etc.
7116 InitElementPropertiesEngine(level->game_version);
7117 InitElementPropertiesGfxElement();
7120 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7124 // map elements that have changed in newer versions
7125 for (y = 0; y < level->fieldy; y++)
7126 for (x = 0; x < level->fieldx; x++)
7127 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7128 level->game_version);
7130 // clear unused playfield data (nicer if level gets resized in editor)
7131 for (x = 0; x < MAX_LEV_FIELDX; x++)
7132 for (y = 0; y < MAX_LEV_FIELDY; y++)
7133 if (x >= level->fieldx || y >= level->fieldy)
7134 level->field[x][y] = EL_EMPTY;
7136 // copy elements to runtime playfield array
7137 for (x = 0; x < MAX_LEV_FIELDX; x++)
7138 for (y = 0; y < MAX_LEV_FIELDY; y++)
7139 Tile[x][y] = level->field[x][y];
7141 // initialize level size variables for faster access
7142 lev_fieldx = level->fieldx;
7143 lev_fieldy = level->fieldy;
7145 // determine border element for this level
7146 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7147 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7152 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7154 struct LevelFileInfo *level_file_info = &level->file_info;
7156 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7157 CopyNativeLevel_RND_to_Native(level);
7160 static void LoadLevelTemplate_LoadAndInit(void)
7162 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7164 LoadLevel_InitVersion(&level_template);
7165 LoadLevel_InitElements(&level_template);
7166 LoadLevel_InitSettings(&level_template);
7168 ActivateLevelTemplate();
7171 void LoadLevelTemplate(int nr)
7173 if (!fileExists(getGlobalLevelTemplateFilename()))
7175 Warn("no level template found for this level");
7180 setLevelFileInfo(&level_template.file_info, nr);
7182 LoadLevelTemplate_LoadAndInit();
7185 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7187 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7189 LoadLevelTemplate_LoadAndInit();
7192 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7194 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7196 if (level.use_custom_template)
7198 if (network_level != NULL)
7199 LoadNetworkLevelTemplate(network_level);
7201 LoadLevelTemplate(-1);
7204 LoadLevel_InitVersion(&level);
7205 LoadLevel_InitElements(&level);
7206 LoadLevel_InitPlayfield(&level);
7207 LoadLevel_InitSettings(&level);
7209 LoadLevel_InitNativeEngines(&level);
7212 void LoadLevel(int nr)
7214 SetLevelSetInfo(leveldir_current->identifier, nr);
7216 setLevelFileInfo(&level.file_info, nr);
7218 LoadLevel_LoadAndInit(NULL);
7221 void LoadLevelInfoOnly(int nr)
7223 setLevelFileInfo(&level.file_info, nr);
7225 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7228 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7230 SetLevelSetInfo(network_level->leveldir_identifier,
7231 network_level->file_info.nr);
7233 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7235 LoadLevel_LoadAndInit(network_level);
7238 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7242 chunk_size += putFileVersion(file, level->file_version);
7243 chunk_size += putFileVersion(file, level->game_version);
7248 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7252 chunk_size += putFile16BitBE(file, level->creation_date.year);
7253 chunk_size += putFile8Bit(file, level->creation_date.month);
7254 chunk_size += putFile8Bit(file, level->creation_date.day);
7259 #if ENABLE_HISTORIC_CHUNKS
7260 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7264 putFile8Bit(file, level->fieldx);
7265 putFile8Bit(file, level->fieldy);
7267 putFile16BitBE(file, level->time);
7268 putFile16BitBE(file, level->gems_needed);
7270 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7271 putFile8Bit(file, level->name[i]);
7273 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7274 putFile8Bit(file, level->score[i]);
7276 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7277 for (y = 0; y < 3; y++)
7278 for (x = 0; x < 3; x++)
7279 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7280 level->yamyam_content[i].e[x][y]));
7281 putFile8Bit(file, level->amoeba_speed);
7282 putFile8Bit(file, level->time_magic_wall);
7283 putFile8Bit(file, level->time_wheel);
7284 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7285 level->amoeba_content));
7286 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7287 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7288 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7289 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7291 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7293 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7294 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7295 putFile32BitBE(file, level->can_move_into_acid_bits);
7296 putFile8Bit(file, level->dont_collide_with_bits);
7298 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7299 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7301 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7302 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7303 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7305 putFile8Bit(file, level->game_engine_type);
7307 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7311 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7316 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7317 chunk_size += putFile8Bit(file, level->name[i]);
7322 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7327 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7328 chunk_size += putFile8Bit(file, level->author[i]);
7333 #if ENABLE_HISTORIC_CHUNKS
7334 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7339 for (y = 0; y < level->fieldy; y++)
7340 for (x = 0; x < level->fieldx; x++)
7341 if (level->encoding_16bit_field)
7342 chunk_size += putFile16BitBE(file, level->field[x][y]);
7344 chunk_size += putFile8Bit(file, level->field[x][y]);
7350 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7355 for (y = 0; y < level->fieldy; y++)
7356 for (x = 0; x < level->fieldx; x++)
7357 chunk_size += putFile16BitBE(file, level->field[x][y]);
7362 #if ENABLE_HISTORIC_CHUNKS
7363 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7367 putFile8Bit(file, EL_YAMYAM);
7368 putFile8Bit(file, level->num_yamyam_contents);
7369 putFile8Bit(file, 0);
7370 putFile8Bit(file, 0);
7372 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7373 for (y = 0; y < 3; y++)
7374 for (x = 0; x < 3; x++)
7375 if (level->encoding_16bit_field)
7376 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7378 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7382 #if ENABLE_HISTORIC_CHUNKS
7383 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7386 int num_contents, content_xsize, content_ysize;
7387 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7389 if (element == EL_YAMYAM)
7391 num_contents = level->num_yamyam_contents;
7395 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7396 for (y = 0; y < 3; y++)
7397 for (x = 0; x < 3; x++)
7398 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7400 else if (element == EL_BD_AMOEBA)
7406 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7407 for (y = 0; y < 3; y++)
7408 for (x = 0; x < 3; x++)
7409 content_array[i][x][y] = EL_EMPTY;
7410 content_array[0][0][0] = level->amoeba_content;
7414 // chunk header already written -- write empty chunk data
7415 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7417 Warn("cannot save content for element '%d'", element);
7422 putFile16BitBE(file, element);
7423 putFile8Bit(file, num_contents);
7424 putFile8Bit(file, content_xsize);
7425 putFile8Bit(file, content_ysize);
7427 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7429 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7430 for (y = 0; y < 3; y++)
7431 for (x = 0; x < 3; x++)
7432 putFile16BitBE(file, content_array[i][x][y]);
7436 #if ENABLE_HISTORIC_CHUNKS
7437 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7439 int envelope_nr = element - EL_ENVELOPE_1;
7440 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7444 chunk_size += putFile16BitBE(file, element);
7445 chunk_size += putFile16BitBE(file, envelope_len);
7446 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7447 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7449 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7450 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7452 for (i = 0; i < envelope_len; i++)
7453 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7459 #if ENABLE_HISTORIC_CHUNKS
7460 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7461 int num_changed_custom_elements)
7465 putFile16BitBE(file, num_changed_custom_elements);
7467 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7469 int element = EL_CUSTOM_START + i;
7471 struct ElementInfo *ei = &element_info[element];
7473 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7475 if (check < num_changed_custom_elements)
7477 putFile16BitBE(file, element);
7478 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7485 if (check != num_changed_custom_elements) // should not happen
7486 Warn("inconsistent number of custom element properties");
7490 #if ENABLE_HISTORIC_CHUNKS
7491 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7492 int num_changed_custom_elements)
7496 putFile16BitBE(file, num_changed_custom_elements);
7498 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7500 int element = EL_CUSTOM_START + i;
7502 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7504 if (check < num_changed_custom_elements)
7506 putFile16BitBE(file, element);
7507 putFile16BitBE(file, element_info[element].change->target_element);
7514 if (check != num_changed_custom_elements) // should not happen
7515 Warn("inconsistent number of custom target elements");
7519 #if ENABLE_HISTORIC_CHUNKS
7520 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7521 int num_changed_custom_elements)
7523 int i, j, x, y, check = 0;
7525 putFile16BitBE(file, num_changed_custom_elements);
7527 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7529 int element = EL_CUSTOM_START + i;
7530 struct ElementInfo *ei = &element_info[element];
7532 if (ei->modified_settings)
7534 if (check < num_changed_custom_elements)
7536 putFile16BitBE(file, element);
7538 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7539 putFile8Bit(file, ei->description[j]);
7541 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7543 // some free bytes for future properties and padding
7544 WriteUnusedBytesToFile(file, 7);
7546 putFile8Bit(file, ei->use_gfx_element);
7547 putFile16BitBE(file, ei->gfx_element_initial);
7549 putFile8Bit(file, ei->collect_score_initial);
7550 putFile8Bit(file, ei->collect_count_initial);
7552 putFile16BitBE(file, ei->push_delay_fixed);
7553 putFile16BitBE(file, ei->push_delay_random);
7554 putFile16BitBE(file, ei->move_delay_fixed);
7555 putFile16BitBE(file, ei->move_delay_random);
7557 putFile16BitBE(file, ei->move_pattern);
7558 putFile8Bit(file, ei->move_direction_initial);
7559 putFile8Bit(file, ei->move_stepsize);
7561 for (y = 0; y < 3; y++)
7562 for (x = 0; x < 3; x++)
7563 putFile16BitBE(file, ei->content.e[x][y]);
7565 putFile32BitBE(file, ei->change->events);
7567 putFile16BitBE(file, ei->change->target_element);
7569 putFile16BitBE(file, ei->change->delay_fixed);
7570 putFile16BitBE(file, ei->change->delay_random);
7571 putFile16BitBE(file, ei->change->delay_frames);
7573 putFile16BitBE(file, ei->change->initial_trigger_element);
7575 putFile8Bit(file, ei->change->explode);
7576 putFile8Bit(file, ei->change->use_target_content);
7577 putFile8Bit(file, ei->change->only_if_complete);
7578 putFile8Bit(file, ei->change->use_random_replace);
7580 putFile8Bit(file, ei->change->random_percentage);
7581 putFile8Bit(file, ei->change->replace_when);
7583 for (y = 0; y < 3; y++)
7584 for (x = 0; x < 3; x++)
7585 putFile16BitBE(file, ei->change->content.e[x][y]);
7587 putFile8Bit(file, ei->slippery_type);
7589 // some free bytes for future properties and padding
7590 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7597 if (check != num_changed_custom_elements) // should not happen
7598 Warn("inconsistent number of custom element properties");
7602 #if ENABLE_HISTORIC_CHUNKS
7603 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7605 struct ElementInfo *ei = &element_info[element];
7608 // ---------- custom element base property values (96 bytes) ----------------
7610 putFile16BitBE(file, element);
7612 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7613 putFile8Bit(file, ei->description[i]);
7615 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7617 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7619 putFile8Bit(file, ei->num_change_pages);
7621 putFile16BitBE(file, ei->ce_value_fixed_initial);
7622 putFile16BitBE(file, ei->ce_value_random_initial);
7623 putFile8Bit(file, ei->use_last_ce_value);
7625 putFile8Bit(file, ei->use_gfx_element);
7626 putFile16BitBE(file, ei->gfx_element_initial);
7628 putFile8Bit(file, ei->collect_score_initial);
7629 putFile8Bit(file, ei->collect_count_initial);
7631 putFile8Bit(file, ei->drop_delay_fixed);
7632 putFile8Bit(file, ei->push_delay_fixed);
7633 putFile8Bit(file, ei->drop_delay_random);
7634 putFile8Bit(file, ei->push_delay_random);
7635 putFile16BitBE(file, ei->move_delay_fixed);
7636 putFile16BitBE(file, ei->move_delay_random);
7638 // bits 0 - 15 of "move_pattern" ...
7639 putFile16BitBE(file, ei->move_pattern & 0xffff);
7640 putFile8Bit(file, ei->move_direction_initial);
7641 putFile8Bit(file, ei->move_stepsize);
7643 putFile8Bit(file, ei->slippery_type);
7645 for (y = 0; y < 3; y++)
7646 for (x = 0; x < 3; x++)
7647 putFile16BitBE(file, ei->content.e[x][y]);
7649 putFile16BitBE(file, ei->move_enter_element);
7650 putFile16BitBE(file, ei->move_leave_element);
7651 putFile8Bit(file, ei->move_leave_type);
7653 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7654 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7656 putFile8Bit(file, ei->access_direction);
7658 putFile8Bit(file, ei->explosion_delay);
7659 putFile8Bit(file, ei->ignition_delay);
7660 putFile8Bit(file, ei->explosion_type);
7662 // some free bytes for future custom property values and padding
7663 WriteUnusedBytesToFile(file, 1);
7665 // ---------- change page property values (48 bytes) ------------------------
7667 for (i = 0; i < ei->num_change_pages; i++)
7669 struct ElementChangeInfo *change = &ei->change_page[i];
7670 unsigned int event_bits;
7672 // bits 0 - 31 of "has_event[]" ...
7674 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7675 if (change->has_event[j])
7676 event_bits |= (1u << j);
7677 putFile32BitBE(file, event_bits);
7679 putFile16BitBE(file, change->target_element);
7681 putFile16BitBE(file, change->delay_fixed);
7682 putFile16BitBE(file, change->delay_random);
7683 putFile16BitBE(file, change->delay_frames);
7685 putFile16BitBE(file, change->initial_trigger_element);
7687 putFile8Bit(file, change->explode);
7688 putFile8Bit(file, change->use_target_content);
7689 putFile8Bit(file, change->only_if_complete);
7690 putFile8Bit(file, change->use_random_replace);
7692 putFile8Bit(file, change->random_percentage);
7693 putFile8Bit(file, change->replace_when);
7695 for (y = 0; y < 3; y++)
7696 for (x = 0; x < 3; x++)
7697 putFile16BitBE(file, change->target_content.e[x][y]);
7699 putFile8Bit(file, change->can_change);
7701 putFile8Bit(file, change->trigger_side);
7703 putFile8Bit(file, change->trigger_player);
7704 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7705 log_2(change->trigger_page)));
7707 putFile8Bit(file, change->has_action);
7708 putFile8Bit(file, change->action_type);
7709 putFile8Bit(file, change->action_mode);
7710 putFile16BitBE(file, change->action_arg);
7712 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7714 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7715 if (change->has_event[j])
7716 event_bits |= (1u << (j - 32));
7717 putFile8Bit(file, event_bits);
7722 #if ENABLE_HISTORIC_CHUNKS
7723 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7725 struct ElementInfo *ei = &element_info[element];
7726 struct ElementGroupInfo *group = ei->group;
7729 putFile16BitBE(file, element);
7731 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7732 putFile8Bit(file, ei->description[i]);
7734 putFile8Bit(file, group->num_elements);
7736 putFile8Bit(file, ei->use_gfx_element);
7737 putFile16BitBE(file, ei->gfx_element_initial);
7739 putFile8Bit(file, group->choice_mode);
7741 // some free bytes for future values and padding
7742 WriteUnusedBytesToFile(file, 3);
7744 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7745 putFile16BitBE(file, group->element[i]);
7749 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7750 boolean write_element)
7752 int save_type = entry->save_type;
7753 int data_type = entry->data_type;
7754 int conf_type = entry->conf_type;
7755 int byte_mask = conf_type & CONF_MASK_BYTES;
7756 int element = entry->element;
7757 int default_value = entry->default_value;
7759 boolean modified = FALSE;
7761 if (byte_mask != CONF_MASK_MULTI_BYTES)
7763 void *value_ptr = entry->value;
7764 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7767 // check if any settings have been modified before saving them
7768 if (value != default_value)
7771 // do not save if explicitly told or if unmodified default settings
7772 if ((save_type == SAVE_CONF_NEVER) ||
7773 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7777 num_bytes += putFile16BitBE(file, element);
7779 num_bytes += putFile8Bit(file, conf_type);
7780 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7781 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7782 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7785 else if (data_type == TYPE_STRING)
7787 char *default_string = entry->default_string;
7788 char *string = (char *)(entry->value);
7789 int string_length = strlen(string);
7792 // check if any settings have been modified before saving them
7793 if (!strEqual(string, default_string))
7796 // do not save if explicitly told or if unmodified default settings
7797 if ((save_type == SAVE_CONF_NEVER) ||
7798 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7802 num_bytes += putFile16BitBE(file, element);
7804 num_bytes += putFile8Bit(file, conf_type);
7805 num_bytes += putFile16BitBE(file, string_length);
7807 for (i = 0; i < string_length; i++)
7808 num_bytes += putFile8Bit(file, string[i]);
7810 else if (data_type == TYPE_ELEMENT_LIST)
7812 int *element_array = (int *)(entry->value);
7813 int num_elements = *(int *)(entry->num_entities);
7816 // check if any settings have been modified before saving them
7817 for (i = 0; i < num_elements; i++)
7818 if (element_array[i] != default_value)
7821 // do not save if explicitly told or if unmodified default settings
7822 if ((save_type == SAVE_CONF_NEVER) ||
7823 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7827 num_bytes += putFile16BitBE(file, element);
7829 num_bytes += putFile8Bit(file, conf_type);
7830 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7832 for (i = 0; i < num_elements; i++)
7833 num_bytes += putFile16BitBE(file, element_array[i]);
7835 else if (data_type == TYPE_CONTENT_LIST)
7837 struct Content *content = (struct Content *)(entry->value);
7838 int num_contents = *(int *)(entry->num_entities);
7841 // check if any settings have been modified before saving them
7842 for (i = 0; i < num_contents; i++)
7843 for (y = 0; y < 3; y++)
7844 for (x = 0; x < 3; x++)
7845 if (content[i].e[x][y] != default_value)
7848 // do not save if explicitly told or if unmodified default settings
7849 if ((save_type == SAVE_CONF_NEVER) ||
7850 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7854 num_bytes += putFile16BitBE(file, element);
7856 num_bytes += putFile8Bit(file, conf_type);
7857 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7859 for (i = 0; i < num_contents; i++)
7860 for (y = 0; y < 3; y++)
7861 for (x = 0; x < 3; x++)
7862 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7868 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7873 li = *level; // copy level data into temporary buffer
7875 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7876 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7881 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7886 li = *level; // copy level data into temporary buffer
7888 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7889 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7894 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7896 int envelope_nr = element - EL_ENVELOPE_1;
7900 chunk_size += putFile16BitBE(file, element);
7902 // copy envelope data into temporary buffer
7903 xx_envelope = level->envelope[envelope_nr];
7905 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7906 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7911 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7913 struct ElementInfo *ei = &element_info[element];
7917 chunk_size += putFile16BitBE(file, element);
7919 xx_ei = *ei; // copy element data into temporary buffer
7921 // set default description string for this specific element
7922 strcpy(xx_default_description, getDefaultElementDescription(ei));
7924 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7925 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7927 for (i = 0; i < ei->num_change_pages; i++)
7929 struct ElementChangeInfo *change = &ei->change_page[i];
7931 xx_current_change_page = i;
7933 xx_change = *change; // copy change data into temporary buffer
7936 setEventBitsFromEventFlags(change);
7938 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7939 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7946 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7948 struct ElementInfo *ei = &element_info[element];
7949 struct ElementGroupInfo *group = ei->group;
7953 chunk_size += putFile16BitBE(file, element);
7955 xx_ei = *ei; // copy element data into temporary buffer
7956 xx_group = *group; // copy group data into temporary buffer
7958 // set default description string for this specific element
7959 strcpy(xx_default_description, getDefaultElementDescription(ei));
7961 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7962 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7967 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7969 struct ElementInfo *ei = &element_info[element];
7973 chunk_size += putFile16BitBE(file, element);
7975 xx_ei = *ei; // copy element data into temporary buffer
7977 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7978 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7983 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7984 boolean save_as_template)
7990 if (!(file = fopen(filename, MODE_WRITE)))
7992 Warn("cannot save level file '%s'", filename);
7997 level->file_version = FILE_VERSION_ACTUAL;
7998 level->game_version = GAME_VERSION_ACTUAL;
8000 level->creation_date = getCurrentDate();
8002 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8003 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8005 chunk_size = SaveLevel_VERS(NULL, level);
8006 putFileChunkBE(file, "VERS", chunk_size);
8007 SaveLevel_VERS(file, level);
8009 chunk_size = SaveLevel_DATE(NULL, level);
8010 putFileChunkBE(file, "DATE", chunk_size);
8011 SaveLevel_DATE(file, level);
8013 chunk_size = SaveLevel_NAME(NULL, level);
8014 putFileChunkBE(file, "NAME", chunk_size);
8015 SaveLevel_NAME(file, level);
8017 chunk_size = SaveLevel_AUTH(NULL, level);
8018 putFileChunkBE(file, "AUTH", chunk_size);
8019 SaveLevel_AUTH(file, level);
8021 chunk_size = SaveLevel_INFO(NULL, level);
8022 putFileChunkBE(file, "INFO", chunk_size);
8023 SaveLevel_INFO(file, level);
8025 chunk_size = SaveLevel_BODY(NULL, level);
8026 putFileChunkBE(file, "BODY", chunk_size);
8027 SaveLevel_BODY(file, level);
8029 chunk_size = SaveLevel_ELEM(NULL, level);
8030 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8032 putFileChunkBE(file, "ELEM", chunk_size);
8033 SaveLevel_ELEM(file, level);
8036 for (i = 0; i < NUM_ENVELOPES; i++)
8038 int element = EL_ENVELOPE_1 + i;
8040 chunk_size = SaveLevel_NOTE(NULL, level, element);
8041 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8043 putFileChunkBE(file, "NOTE", chunk_size);
8044 SaveLevel_NOTE(file, level, element);
8048 // if not using template level, check for non-default custom/group elements
8049 if (!level->use_custom_template || save_as_template)
8051 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8053 int element = EL_CUSTOM_START + i;
8055 chunk_size = SaveLevel_CUSX(NULL, level, element);
8056 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8058 putFileChunkBE(file, "CUSX", chunk_size);
8059 SaveLevel_CUSX(file, level, element);
8063 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8065 int element = EL_GROUP_START + i;
8067 chunk_size = SaveLevel_GRPX(NULL, level, element);
8068 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8070 putFileChunkBE(file, "GRPX", chunk_size);
8071 SaveLevel_GRPX(file, level, element);
8075 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8077 int element = GET_EMPTY_ELEMENT(i);
8079 chunk_size = SaveLevel_EMPX(NULL, level, element);
8080 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8082 putFileChunkBE(file, "EMPX", chunk_size);
8083 SaveLevel_EMPX(file, level, element);
8090 SetFilePermissions(filename, PERMS_PRIVATE);
8093 void SaveLevel(int nr)
8095 char *filename = getDefaultLevelFilename(nr);
8097 SaveLevelFromFilename(&level, filename, FALSE);
8100 void SaveLevelTemplate(void)
8102 char *filename = getLocalLevelTemplateFilename();
8104 SaveLevelFromFilename(&level, filename, TRUE);
8107 boolean SaveLevelChecked(int nr)
8109 char *filename = getDefaultLevelFilename(nr);
8110 boolean new_level = !fileExists(filename);
8111 boolean level_saved = FALSE;
8113 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8118 Request("Level saved!", REQ_CONFIRM);
8126 void DumpLevel(struct LevelInfo *level)
8128 if (level->no_level_file || level->no_valid_file)
8130 Warn("cannot dump -- no valid level file found");
8136 Print("Level xxx (file version %08d, game version %08d)\n",
8137 level->file_version, level->game_version);
8140 Print("Level author: '%s'\n", level->author);
8141 Print("Level title: '%s'\n", level->name);
8143 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8145 Print("Level time: %d seconds\n", level->time);
8146 Print("Gems needed: %d\n", level->gems_needed);
8148 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8149 Print("Time for wheel: %d seconds\n", level->time_wheel);
8150 Print("Time for light: %d seconds\n", level->time_light);
8151 Print("Time for timegate: %d seconds\n", level->time_timegate);
8153 Print("Amoeba speed: %d\n", level->amoeba_speed);
8156 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8157 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8158 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8159 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8160 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8161 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8167 for (i = 0; i < NUM_ENVELOPES; i++)
8169 char *text = level->envelope[i].text;
8170 int text_len = strlen(text);
8171 boolean has_text = FALSE;
8173 for (j = 0; j < text_len; j++)
8174 if (text[j] != ' ' && text[j] != '\n')
8180 Print("Envelope %d:\n'%s'\n", i + 1, text);
8188 void DumpLevels(void)
8190 static LevelDirTree *dumplevel_leveldir = NULL;
8192 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8193 global.dumplevel_leveldir);
8195 if (dumplevel_leveldir == NULL)
8196 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8198 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8199 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8200 Fail("no such level number: %d", global.dumplevel_level_nr);
8202 leveldir_current = dumplevel_leveldir;
8204 LoadLevel(global.dumplevel_level_nr);
8211 // ============================================================================
8212 // tape file functions
8213 // ============================================================================
8215 static void setTapeInfoToDefaults(void)
8219 // always start with reliable default values (empty tape)
8222 // default values (also for pre-1.2 tapes) with only the first player
8223 tape.player_participates[0] = TRUE;
8224 for (i = 1; i < MAX_PLAYERS; i++)
8225 tape.player_participates[i] = FALSE;
8227 // at least one (default: the first) player participates in every tape
8228 tape.num_participating_players = 1;
8230 tape.property_bits = TAPE_PROPERTY_NONE;
8232 tape.level_nr = level_nr;
8234 tape.changed = FALSE;
8235 tape.solved = FALSE;
8237 tape.recording = FALSE;
8238 tape.playing = FALSE;
8239 tape.pausing = FALSE;
8241 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8242 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8244 tape.no_info_chunk = TRUE;
8245 tape.no_valid_file = FALSE;
8248 static int getTapePosSize(struct TapeInfo *tape)
8250 int tape_pos_size = 0;
8252 if (tape->use_key_actions)
8253 tape_pos_size += tape->num_participating_players;
8255 if (tape->use_mouse_actions)
8256 tape_pos_size += 3; // x and y position and mouse button mask
8258 tape_pos_size += 1; // tape action delay value
8260 return tape_pos_size;
8263 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8265 tape->use_key_actions = FALSE;
8266 tape->use_mouse_actions = FALSE;
8268 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8269 tape->use_key_actions = TRUE;
8271 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8272 tape->use_mouse_actions = TRUE;
8275 static int getTapeActionValue(struct TapeInfo *tape)
8277 return (tape->use_key_actions &&
8278 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8279 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8280 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8281 TAPE_ACTIONS_DEFAULT);
8284 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8286 tape->file_version = getFileVersion(file);
8287 tape->game_version = getFileVersion(file);
8292 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8296 tape->random_seed = getFile32BitBE(file);
8297 tape->date = getFile32BitBE(file);
8298 tape->length = getFile32BitBE(file);
8300 // read header fields that are new since version 1.2
8301 if (tape->file_version >= FILE_VERSION_1_2)
8303 byte store_participating_players = getFile8Bit(file);
8306 // since version 1.2, tapes store which players participate in the tape
8307 tape->num_participating_players = 0;
8308 for (i = 0; i < MAX_PLAYERS; i++)
8310 tape->player_participates[i] = FALSE;
8312 if (store_participating_players & (1 << i))
8314 tape->player_participates[i] = TRUE;
8315 tape->num_participating_players++;
8319 setTapeActionFlags(tape, getFile8Bit(file));
8321 tape->property_bits = getFile8Bit(file);
8322 tape->solved = getFile8Bit(file);
8324 engine_version = getFileVersion(file);
8325 if (engine_version > 0)
8326 tape->engine_version = engine_version;
8328 tape->engine_version = tape->game_version;
8334 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8336 tape->scr_fieldx = getFile8Bit(file);
8337 tape->scr_fieldy = getFile8Bit(file);
8342 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8344 char *level_identifier = NULL;
8345 int level_identifier_size;
8348 tape->no_info_chunk = FALSE;
8350 level_identifier_size = getFile16BitBE(file);
8352 level_identifier = checked_malloc(level_identifier_size);
8354 for (i = 0; i < level_identifier_size; i++)
8355 level_identifier[i] = getFile8Bit(file);
8357 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8358 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8360 checked_free(level_identifier);
8362 tape->level_nr = getFile16BitBE(file);
8364 chunk_size = 2 + level_identifier_size + 2;
8369 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8372 int tape_pos_size = getTapePosSize(tape);
8373 int chunk_size_expected = tape_pos_size * tape->length;
8375 if (chunk_size_expected != chunk_size)
8377 ReadUnusedBytesFromFile(file, chunk_size);
8378 return chunk_size_expected;
8381 for (i = 0; i < tape->length; i++)
8383 if (i >= MAX_TAPE_LEN)
8385 Warn("tape truncated -- size exceeds maximum tape size %d",
8388 // tape too large; read and ignore remaining tape data from this chunk
8389 for (;i < tape->length; i++)
8390 ReadUnusedBytesFromFile(file, tape_pos_size);
8395 if (tape->use_key_actions)
8397 for (j = 0; j < MAX_PLAYERS; j++)
8399 tape->pos[i].action[j] = MV_NONE;
8401 if (tape->player_participates[j])
8402 tape->pos[i].action[j] = getFile8Bit(file);
8406 if (tape->use_mouse_actions)
8408 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8409 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8410 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8413 tape->pos[i].delay = getFile8Bit(file);
8415 if (tape->file_version == FILE_VERSION_1_0)
8417 // eliminate possible diagonal moves in old tapes
8418 // this is only for backward compatibility
8420 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8421 byte action = tape->pos[i].action[0];
8422 int k, num_moves = 0;
8424 for (k = 0; k < 4; k++)
8426 if (action & joy_dir[k])
8428 tape->pos[i + num_moves].action[0] = joy_dir[k];
8430 tape->pos[i + num_moves].delay = 0;
8439 tape->length += num_moves;
8442 else if (tape->file_version < FILE_VERSION_2_0)
8444 // convert pre-2.0 tapes to new tape format
8446 if (tape->pos[i].delay > 1)
8449 tape->pos[i + 1] = tape->pos[i];
8450 tape->pos[i + 1].delay = 1;
8453 for (j = 0; j < MAX_PLAYERS; j++)
8454 tape->pos[i].action[j] = MV_NONE;
8455 tape->pos[i].delay--;
8462 if (checkEndOfFile(file))
8466 if (i != tape->length)
8467 chunk_size = tape_pos_size * i;
8472 static void LoadTape_SokobanSolution(char *filename)
8475 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8477 if (!(file = openFile(filename, MODE_READ)))
8479 tape.no_valid_file = TRUE;
8484 while (!checkEndOfFile(file))
8486 unsigned char c = getByteFromFile(file);
8488 if (checkEndOfFile(file))
8495 tape.pos[tape.length].action[0] = MV_UP;
8496 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8502 tape.pos[tape.length].action[0] = MV_DOWN;
8503 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8509 tape.pos[tape.length].action[0] = MV_LEFT;
8510 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8516 tape.pos[tape.length].action[0] = MV_RIGHT;
8517 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8525 // ignore white-space characters
8529 tape.no_valid_file = TRUE;
8531 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8539 if (tape.no_valid_file)
8542 tape.length_frames = GetTapeLengthFrames();
8543 tape.length_seconds = GetTapeLengthSeconds();
8546 void LoadTapeFromFilename(char *filename)
8548 char cookie[MAX_LINE_LEN];
8549 char chunk_name[CHUNK_ID_LEN + 1];
8553 // always start with reliable default values
8554 setTapeInfoToDefaults();
8556 if (strSuffix(filename, ".sln"))
8558 LoadTape_SokobanSolution(filename);
8563 if (!(file = openFile(filename, MODE_READ)))
8565 tape.no_valid_file = TRUE;
8570 getFileChunkBE(file, chunk_name, NULL);
8571 if (strEqual(chunk_name, "RND1"))
8573 getFile32BitBE(file); // not used
8575 getFileChunkBE(file, chunk_name, NULL);
8576 if (!strEqual(chunk_name, "TAPE"))
8578 tape.no_valid_file = TRUE;
8580 Warn("unknown format of tape file '%s'", filename);
8587 else // check for pre-2.0 file format with cookie string
8589 strcpy(cookie, chunk_name);
8590 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8592 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8593 cookie[strlen(cookie) - 1] = '\0';
8595 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8597 tape.no_valid_file = TRUE;
8599 Warn("unknown format of tape file '%s'", filename);
8606 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8608 tape.no_valid_file = TRUE;
8610 Warn("unsupported version of tape file '%s'", filename);
8617 // pre-2.0 tape files have no game version, so use file version here
8618 tape.game_version = tape.file_version;
8621 if (tape.file_version < FILE_VERSION_1_2)
8623 // tape files from versions before 1.2.0 without chunk structure
8624 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8625 LoadTape_BODY(file, 2 * tape.length, &tape);
8633 int (*loader)(File *, int, struct TapeInfo *);
8637 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8638 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8639 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8640 { "INFO", -1, LoadTape_INFO },
8641 { "BODY", -1, LoadTape_BODY },
8645 while (getFileChunkBE(file, chunk_name, &chunk_size))
8649 while (chunk_info[i].name != NULL &&
8650 !strEqual(chunk_name, chunk_info[i].name))
8653 if (chunk_info[i].name == NULL)
8655 Warn("unknown chunk '%s' in tape file '%s'",
8656 chunk_name, filename);
8658 ReadUnusedBytesFromFile(file, chunk_size);
8660 else if (chunk_info[i].size != -1 &&
8661 chunk_info[i].size != chunk_size)
8663 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8664 chunk_size, chunk_name, filename);
8666 ReadUnusedBytesFromFile(file, chunk_size);
8670 // call function to load this tape chunk
8671 int chunk_size_expected =
8672 (chunk_info[i].loader)(file, chunk_size, &tape);
8674 // the size of some chunks cannot be checked before reading other
8675 // chunks first (like "HEAD" and "BODY") that contain some header
8676 // information, so check them here
8677 if (chunk_size_expected != chunk_size)
8679 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8680 chunk_size, chunk_name, filename);
8688 tape.length_frames = GetTapeLengthFrames();
8689 tape.length_seconds = GetTapeLengthSeconds();
8692 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8694 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8696 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8697 tape.engine_version);
8701 void LoadTape(int nr)
8703 char *filename = getTapeFilename(nr);
8705 LoadTapeFromFilename(filename);
8708 void LoadSolutionTape(int nr)
8710 char *filename = getSolutionTapeFilename(nr);
8712 LoadTapeFromFilename(filename);
8714 if (TAPE_IS_EMPTY(tape))
8716 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8717 level.native_bd_level->replay != NULL)
8718 CopyNativeTape_BD_to_RND(&level);
8719 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8720 level.native_sp_level->demo.is_available)
8721 CopyNativeTape_SP_to_RND(&level);
8725 void LoadScoreTape(char *score_tape_basename, int nr)
8727 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8729 LoadTapeFromFilename(filename);
8732 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8734 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8736 LoadTapeFromFilename(filename);
8739 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8741 // chunk required for team mode tapes with non-default screen size
8742 return (tape->num_participating_players > 1 &&
8743 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8744 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8747 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8749 putFileVersion(file, tape->file_version);
8750 putFileVersion(file, tape->game_version);
8753 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8756 byte store_participating_players = 0;
8758 // set bits for participating players for compact storage
8759 for (i = 0; i < MAX_PLAYERS; i++)
8760 if (tape->player_participates[i])
8761 store_participating_players |= (1 << i);
8763 putFile32BitBE(file, tape->random_seed);
8764 putFile32BitBE(file, tape->date);
8765 putFile32BitBE(file, tape->length);
8767 putFile8Bit(file, store_participating_players);
8769 putFile8Bit(file, getTapeActionValue(tape));
8771 putFile8Bit(file, tape->property_bits);
8772 putFile8Bit(file, tape->solved);
8774 putFileVersion(file, tape->engine_version);
8777 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8779 putFile8Bit(file, tape->scr_fieldx);
8780 putFile8Bit(file, tape->scr_fieldy);
8783 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8785 int level_identifier_size = strlen(tape->level_identifier) + 1;
8788 putFile16BitBE(file, level_identifier_size);
8790 for (i = 0; i < level_identifier_size; i++)
8791 putFile8Bit(file, tape->level_identifier[i]);
8793 putFile16BitBE(file, tape->level_nr);
8796 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8800 for (i = 0; i < tape->length; i++)
8802 if (tape->use_key_actions)
8804 for (j = 0; j < MAX_PLAYERS; j++)
8805 if (tape->player_participates[j])
8806 putFile8Bit(file, tape->pos[i].action[j]);
8809 if (tape->use_mouse_actions)
8811 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8812 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8813 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8816 putFile8Bit(file, tape->pos[i].delay);
8820 void SaveTapeToFilename(char *filename)
8824 int info_chunk_size;
8825 int body_chunk_size;
8827 if (!(file = fopen(filename, MODE_WRITE)))
8829 Warn("cannot save level recording file '%s'", filename);
8834 tape_pos_size = getTapePosSize(&tape);
8836 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8837 body_chunk_size = tape_pos_size * tape.length;
8839 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8840 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8842 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8843 SaveTape_VERS(file, &tape);
8845 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8846 SaveTape_HEAD(file, &tape);
8848 if (checkSaveTape_SCRN(&tape))
8850 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8851 SaveTape_SCRN(file, &tape);
8854 putFileChunkBE(file, "INFO", info_chunk_size);
8855 SaveTape_INFO(file, &tape);
8857 putFileChunkBE(file, "BODY", body_chunk_size);
8858 SaveTape_BODY(file, &tape);
8862 SetFilePermissions(filename, PERMS_PRIVATE);
8865 static void SaveTapeExt(char *filename)
8869 tape.file_version = FILE_VERSION_ACTUAL;
8870 tape.game_version = GAME_VERSION_ACTUAL;
8872 tape.num_participating_players = 0;
8874 // count number of participating players
8875 for (i = 0; i < MAX_PLAYERS; i++)
8876 if (tape.player_participates[i])
8877 tape.num_participating_players++;
8879 SaveTapeToFilename(filename);
8881 tape.changed = FALSE;
8884 void SaveTape(int nr)
8886 char *filename = getTapeFilename(nr);
8888 InitTapeDirectory(leveldir_current->subdir);
8890 SaveTapeExt(filename);
8893 void SaveScoreTape(int nr)
8895 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8897 // used instead of "leveldir_current->subdir" (for network games)
8898 InitScoreTapeDirectory(levelset.identifier, nr);
8900 SaveTapeExt(filename);
8903 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8904 unsigned int req_state_added)
8906 char *filename = getTapeFilename(nr);
8907 boolean new_tape = !fileExists(filename);
8908 boolean tape_saved = FALSE;
8910 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8915 Request(msg_saved, REQ_CONFIRM | req_state_added);
8923 boolean SaveTapeChecked(int nr)
8925 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8928 boolean SaveTapeChecked_LevelSolved(int nr)
8930 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8931 "Level solved! Tape saved!", REQ_STAY_OPEN);
8934 void DumpTape(struct TapeInfo *tape)
8936 int tape_frame_counter;
8939 if (tape->no_valid_file)
8941 Warn("cannot dump -- no valid tape file found");
8948 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8949 tape->level_nr, tape->file_version, tape->game_version);
8950 Print(" (effective engine version %08d)\n",
8951 tape->engine_version);
8952 Print("Level series identifier: '%s'\n", tape->level_identifier);
8954 Print("Solution tape: %s\n",
8955 tape->solved ? "yes" :
8956 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8958 Print("Special tape properties: ");
8959 if (tape->property_bits == TAPE_PROPERTY_NONE)
8961 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8962 Print("[em_random_bug]");
8963 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8964 Print("[game_speed]");
8965 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8967 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8968 Print("[single_step]");
8969 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8970 Print("[snapshot]");
8971 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8972 Print("[replayed]");
8973 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8974 Print("[tas_keys]");
8975 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8976 Print("[small_graphics]");
8979 int year2 = tape->date / 10000;
8980 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8981 int month_index_raw = (tape->date / 100) % 100;
8982 int month_index = month_index_raw % 12; // prevent invalid index
8983 int month = month_index + 1;
8984 int day = tape->date % 100;
8986 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8990 tape_frame_counter = 0;
8992 for (i = 0; i < tape->length; i++)
8994 if (i >= MAX_TAPE_LEN)
8999 for (j = 0; j < MAX_PLAYERS; j++)
9001 if (tape->player_participates[j])
9003 int action = tape->pos[i].action[j];
9005 Print("%d:%02x ", j, action);
9006 Print("[%c%c%c%c|%c%c] - ",
9007 (action & JOY_LEFT ? '<' : ' '),
9008 (action & JOY_RIGHT ? '>' : ' '),
9009 (action & JOY_UP ? '^' : ' '),
9010 (action & JOY_DOWN ? 'v' : ' '),
9011 (action & JOY_BUTTON_1 ? '1' : ' '),
9012 (action & JOY_BUTTON_2 ? '2' : ' '));
9016 Print("(%03d) ", tape->pos[i].delay);
9017 Print("[%05d]\n", tape_frame_counter);
9019 tape_frame_counter += tape->pos[i].delay;
9025 void DumpTapes(void)
9027 static LevelDirTree *dumptape_leveldir = NULL;
9029 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9030 global.dumptape_leveldir);
9032 if (dumptape_leveldir == NULL)
9033 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9035 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9036 global.dumptape_level_nr > dumptape_leveldir->last_level)
9037 Fail("no such level number: %d", global.dumptape_level_nr);
9039 leveldir_current = dumptape_leveldir;
9041 if (options.mytapes)
9042 LoadTape(global.dumptape_level_nr);
9044 LoadSolutionTape(global.dumptape_level_nr);
9052 // ============================================================================
9053 // score file functions
9054 // ============================================================================
9056 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9060 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9062 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9063 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9064 scores->entry[i].score = 0;
9065 scores->entry[i].time = 0;
9067 scores->entry[i].id = -1;
9068 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9069 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9070 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9071 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9072 strcpy(scores->entry[i].country_code, "??");
9075 scores->num_entries = 0;
9076 scores->last_added = -1;
9077 scores->last_added_local = -1;
9079 scores->updated = FALSE;
9080 scores->uploaded = FALSE;
9081 scores->tape_downloaded = FALSE;
9082 scores->force_last_added = FALSE;
9084 // The following values are intentionally not reset here:
9088 // - continue_playing
9089 // - continue_on_return
9092 static void setScoreInfoToDefaults(void)
9094 setScoreInfoToDefaultsExt(&scores);
9097 static void setServerScoreInfoToDefaults(void)
9099 setScoreInfoToDefaultsExt(&server_scores);
9102 static void LoadScore_OLD(int nr)
9105 char *filename = getScoreFilename(nr);
9106 char cookie[MAX_LINE_LEN];
9107 char line[MAX_LINE_LEN];
9111 if (!(file = fopen(filename, MODE_READ)))
9114 // check file identifier
9115 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9117 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9118 cookie[strlen(cookie) - 1] = '\0';
9120 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9122 Warn("unknown format of score file '%s'", filename);
9129 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9131 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9132 Warn("fscanf() failed; %s", strerror(errno));
9134 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9137 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9138 line[strlen(line) - 1] = '\0';
9140 for (line_ptr = line; *line_ptr; line_ptr++)
9142 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9144 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9145 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9154 static void ConvertScore_OLD(void)
9156 // only convert score to time for levels that rate playing time over score
9157 if (!level.rate_time_over_score)
9160 // convert old score to playing time for score-less levels (like Supaplex)
9161 int time_final_max = 999;
9164 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9166 int score = scores.entry[i].score;
9168 if (score > 0 && score < time_final_max)
9169 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9173 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9175 scores->file_version = getFileVersion(file);
9176 scores->game_version = getFileVersion(file);
9181 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9183 char *level_identifier = NULL;
9184 int level_identifier_size;
9187 level_identifier_size = getFile16BitBE(file);
9189 level_identifier = checked_malloc(level_identifier_size);
9191 for (i = 0; i < level_identifier_size; i++)
9192 level_identifier[i] = getFile8Bit(file);
9194 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9195 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9197 checked_free(level_identifier);
9199 scores->level_nr = getFile16BitBE(file);
9200 scores->num_entries = getFile16BitBE(file);
9202 chunk_size = 2 + level_identifier_size + 2 + 2;
9207 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9211 for (i = 0; i < scores->num_entries; i++)
9213 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9214 scores->entry[i].name[j] = getFile8Bit(file);
9216 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9219 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9224 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9228 for (i = 0; i < scores->num_entries; i++)
9229 scores->entry[i].score = getFile16BitBE(file);
9231 chunk_size = scores->num_entries * 2;
9236 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9240 for (i = 0; i < scores->num_entries; i++)
9241 scores->entry[i].score = getFile32BitBE(file);
9243 chunk_size = scores->num_entries * 4;
9248 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9252 for (i = 0; i < scores->num_entries; i++)
9253 scores->entry[i].time = getFile32BitBE(file);
9255 chunk_size = scores->num_entries * 4;
9260 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9264 for (i = 0; i < scores->num_entries; i++)
9266 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9267 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9269 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9272 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9277 void LoadScore(int nr)
9279 char *filename = getScoreFilename(nr);
9280 char cookie[MAX_LINE_LEN];
9281 char chunk_name[CHUNK_ID_LEN + 1];
9283 boolean old_score_file_format = FALSE;
9286 // always start with reliable default values
9287 setScoreInfoToDefaults();
9289 if (!(file = openFile(filename, MODE_READ)))
9292 getFileChunkBE(file, chunk_name, NULL);
9293 if (strEqual(chunk_name, "RND1"))
9295 getFile32BitBE(file); // not used
9297 getFileChunkBE(file, chunk_name, NULL);
9298 if (!strEqual(chunk_name, "SCOR"))
9300 Warn("unknown format of score file '%s'", filename);
9307 else // check for old file format with cookie string
9309 strcpy(cookie, chunk_name);
9310 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9312 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9313 cookie[strlen(cookie) - 1] = '\0';
9315 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9317 Warn("unknown format of score file '%s'", filename);
9324 old_score_file_format = TRUE;
9327 if (old_score_file_format)
9329 // score files from versions before 4.2.4.0 without chunk structure
9332 // convert score to time, if possible (mainly for Supaplex levels)
9341 int (*loader)(File *, int, struct ScoreInfo *);
9345 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9346 { "INFO", -1, LoadScore_INFO },
9347 { "NAME", -1, LoadScore_NAME },
9348 { "SCOR", -1, LoadScore_SCOR },
9349 { "SC4R", -1, LoadScore_SC4R },
9350 { "TIME", -1, LoadScore_TIME },
9351 { "TAPE", -1, LoadScore_TAPE },
9356 while (getFileChunkBE(file, chunk_name, &chunk_size))
9360 while (chunk_info[i].name != NULL &&
9361 !strEqual(chunk_name, chunk_info[i].name))
9364 if (chunk_info[i].name == NULL)
9366 Warn("unknown chunk '%s' in score file '%s'",
9367 chunk_name, filename);
9369 ReadUnusedBytesFromFile(file, chunk_size);
9371 else if (chunk_info[i].size != -1 &&
9372 chunk_info[i].size != chunk_size)
9374 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9375 chunk_size, chunk_name, filename);
9377 ReadUnusedBytesFromFile(file, chunk_size);
9381 // call function to load this score chunk
9382 int chunk_size_expected =
9383 (chunk_info[i].loader)(file, chunk_size, &scores);
9385 // the size of some chunks cannot be checked before reading other
9386 // chunks first (like "HEAD" and "BODY") that contain some header
9387 // information, so check them here
9388 if (chunk_size_expected != chunk_size)
9390 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9391 chunk_size, chunk_name, filename);
9400 #if ENABLE_HISTORIC_CHUNKS
9401 void SaveScore_OLD(int nr)
9404 char *filename = getScoreFilename(nr);
9407 // used instead of "leveldir_current->subdir" (for network games)
9408 InitScoreDirectory(levelset.identifier);
9410 if (!(file = fopen(filename, MODE_WRITE)))
9412 Warn("cannot save score for level %d", nr);
9417 fprintf(file, "%s\n\n", SCORE_COOKIE);
9419 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9420 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9424 SetFilePermissions(filename, PERMS_PRIVATE);
9428 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9430 putFileVersion(file, scores->file_version);
9431 putFileVersion(file, scores->game_version);
9434 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9436 int level_identifier_size = strlen(scores->level_identifier) + 1;
9439 putFile16BitBE(file, level_identifier_size);
9441 for (i = 0; i < level_identifier_size; i++)
9442 putFile8Bit(file, scores->level_identifier[i]);
9444 putFile16BitBE(file, scores->level_nr);
9445 putFile16BitBE(file, scores->num_entries);
9448 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9452 for (i = 0; i < scores->num_entries; i++)
9454 int name_size = strlen(scores->entry[i].name);
9456 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9457 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9461 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9465 for (i = 0; i < scores->num_entries; i++)
9466 putFile16BitBE(file, scores->entry[i].score);
9469 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9473 for (i = 0; i < scores->num_entries; i++)
9474 putFile32BitBE(file, scores->entry[i].score);
9477 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9481 for (i = 0; i < scores->num_entries; i++)
9482 putFile32BitBE(file, scores->entry[i].time);
9485 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9489 for (i = 0; i < scores->num_entries; i++)
9491 int size = strlen(scores->entry[i].tape_basename);
9493 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9494 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9498 static void SaveScoreToFilename(char *filename)
9501 int info_chunk_size;
9502 int name_chunk_size;
9503 int scor_chunk_size;
9504 int sc4r_chunk_size;
9505 int time_chunk_size;
9506 int tape_chunk_size;
9507 boolean has_large_score_values;
9510 if (!(file = fopen(filename, MODE_WRITE)))
9512 Warn("cannot save score file '%s'", filename);
9517 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9518 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9519 scor_chunk_size = scores.num_entries * 2;
9520 sc4r_chunk_size = scores.num_entries * 4;
9521 time_chunk_size = scores.num_entries * 4;
9522 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9524 has_large_score_values = FALSE;
9525 for (i = 0; i < scores.num_entries; i++)
9526 if (scores.entry[i].score > 0xffff)
9527 has_large_score_values = TRUE;
9529 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9530 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9532 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9533 SaveScore_VERS(file, &scores);
9535 putFileChunkBE(file, "INFO", info_chunk_size);
9536 SaveScore_INFO(file, &scores);
9538 putFileChunkBE(file, "NAME", name_chunk_size);
9539 SaveScore_NAME(file, &scores);
9541 if (has_large_score_values)
9543 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9544 SaveScore_SC4R(file, &scores);
9548 putFileChunkBE(file, "SCOR", scor_chunk_size);
9549 SaveScore_SCOR(file, &scores);
9552 putFileChunkBE(file, "TIME", time_chunk_size);
9553 SaveScore_TIME(file, &scores);
9555 putFileChunkBE(file, "TAPE", tape_chunk_size);
9556 SaveScore_TAPE(file, &scores);
9560 SetFilePermissions(filename, PERMS_PRIVATE);
9563 void SaveScore(int nr)
9565 char *filename = getScoreFilename(nr);
9568 // used instead of "leveldir_current->subdir" (for network games)
9569 InitScoreDirectory(levelset.identifier);
9571 scores.file_version = FILE_VERSION_ACTUAL;
9572 scores.game_version = GAME_VERSION_ACTUAL;
9574 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9575 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9576 scores.level_nr = level_nr;
9578 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9579 if (scores.entry[i].score == 0 &&
9580 scores.entry[i].time == 0 &&
9581 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9584 scores.num_entries = i;
9586 if (scores.num_entries == 0)
9589 SaveScoreToFilename(filename);
9592 static void LoadServerScoreFromCache(int nr)
9594 struct ScoreEntry score_entry;
9603 { &score_entry.score, FALSE, 0 },
9604 { &score_entry.time, FALSE, 0 },
9605 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9606 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9607 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9608 { &score_entry.id, FALSE, 0 },
9609 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9610 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9611 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9612 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9616 char *filename = getScoreCacheFilename(nr);
9617 SetupFileHash *score_hash = loadSetupFileHash(filename);
9620 server_scores.num_entries = 0;
9622 if (score_hash == NULL)
9625 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9627 score_entry = server_scores.entry[i];
9629 for (j = 0; score_mapping[j].value != NULL; j++)
9633 sprintf(token, "%02d.%d", i, j);
9635 char *value = getHashEntry(score_hash, token);
9640 if (score_mapping[j].is_string)
9642 char *score_value = (char *)score_mapping[j].value;
9643 int value_size = score_mapping[j].string_size;
9645 strncpy(score_value, value, value_size);
9646 score_value[value_size] = '\0';
9650 int *score_value = (int *)score_mapping[j].value;
9652 *score_value = atoi(value);
9655 server_scores.num_entries = i + 1;
9658 server_scores.entry[i] = score_entry;
9661 freeSetupFileHash(score_hash);
9664 void LoadServerScore(int nr, boolean download_score)
9666 if (!setup.use_api_server)
9669 // always start with reliable default values
9670 setServerScoreInfoToDefaults();
9672 // 1st step: load server scores from cache file (which may not exist)
9673 // (this should prevent reading it while the thread is writing to it)
9674 LoadServerScoreFromCache(nr);
9676 if (download_score && runtime.use_api_server)
9678 // 2nd step: download server scores from score server to cache file
9679 // (as thread, as it might time out if the server is not reachable)
9680 ApiGetScoreAsThread(nr);
9684 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9686 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9688 // if score tape not uploaded, ask for uploading missing tapes later
9689 if (!setup.has_remaining_tapes)
9690 setup.ask_for_remaining_tapes = TRUE;
9692 setup.provide_uploading_tapes = TRUE;
9693 setup.has_remaining_tapes = TRUE;
9695 SaveSetup_ServerSetup();
9698 void SaveServerScore(int nr, boolean tape_saved)
9700 if (!runtime.use_api_server)
9702 PrepareScoreTapesForUpload(leveldir_current->subdir);
9707 ApiAddScoreAsThread(nr, tape_saved, NULL);
9710 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9711 char *score_tape_filename)
9713 if (!runtime.use_api_server)
9716 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9719 void LoadLocalAndServerScore(int nr, boolean download_score)
9721 int last_added_local = scores.last_added_local;
9722 boolean force_last_added = scores.force_last_added;
9724 // needed if only showing server scores
9725 setScoreInfoToDefaults();
9727 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9730 // restore last added local score entry (before merging server scores)
9731 scores.last_added = scores.last_added_local = last_added_local;
9733 if (setup.use_api_server &&
9734 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9736 // load server scores from cache file and trigger update from server
9737 LoadServerScore(nr, download_score);
9739 // merge local scores with scores from server
9743 if (force_last_added)
9744 scores.force_last_added = force_last_added;
9748 // ============================================================================
9749 // setup file functions
9750 // ============================================================================
9752 #define TOKEN_STR_PLAYER_PREFIX "player_"
9755 static struct TokenInfo global_setup_tokens[] =
9759 &setup.player_name, "player_name"
9763 &setup.multiple_users, "multiple_users"
9767 &setup.sound, "sound"
9771 &setup.sound_loops, "repeating_sound_loops"
9775 &setup.sound_music, "background_music"
9779 &setup.sound_simple, "simple_sound_effects"
9783 &setup.toons, "toons"
9787 &setup.global_animations, "global_animations"
9791 &setup.scroll_delay, "scroll_delay"
9795 &setup.forced_scroll_delay, "forced_scroll_delay"
9799 &setup.scroll_delay_value, "scroll_delay_value"
9803 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9807 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9811 &setup.fade_screens, "fade_screens"
9815 &setup.autorecord, "automatic_tape_recording"
9819 &setup.autorecord_after_replay, "autorecord_after_replay"
9823 &setup.auto_pause_on_start, "auto_pause_on_start"
9827 &setup.show_titlescreen, "show_titlescreen"
9831 &setup.quick_doors, "quick_doors"
9835 &setup.team_mode, "team_mode"
9839 &setup.handicap, "handicap"
9843 &setup.skip_levels, "skip_levels"
9847 &setup.increment_levels, "increment_levels"
9851 &setup.auto_play_next_level, "auto_play_next_level"
9855 &setup.count_score_after_game, "count_score_after_game"
9859 &setup.show_scores_after_game, "show_scores_after_game"
9863 &setup.time_limit, "time_limit"
9867 &setup.fullscreen, "fullscreen"
9871 &setup.window_scaling_percent, "window_scaling_percent"
9875 &setup.window_scaling_quality, "window_scaling_quality"
9879 &setup.screen_rendering_mode, "screen_rendering_mode"
9883 &setup.vsync_mode, "vsync_mode"
9887 &setup.ask_on_escape, "ask_on_escape"
9891 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9895 &setup.ask_on_game_over, "ask_on_game_over"
9899 &setup.ask_on_quit_game, "ask_on_quit_game"
9903 &setup.ask_on_quit_program, "ask_on_quit_program"
9907 &setup.quick_switch, "quick_player_switch"
9911 &setup.input_on_focus, "input_on_focus"
9915 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9919 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9923 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9927 &setup.game_speed_extended, "game_speed_extended"
9931 &setup.game_frame_delay, "game_frame_delay"
9935 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9939 &setup.bd_skip_hatching, "bd_skip_hatching"
9943 &setup.bd_scroll_delay, "bd_scroll_delay"
9947 &setup.bd_smooth_movements, "bd_smooth_movements"
9951 &setup.sp_show_border_elements, "sp_show_border_elements"
9955 &setup.small_game_graphics, "small_game_graphics"
9959 &setup.show_load_save_buttons, "show_load_save_buttons"
9963 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9967 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9971 &setup.graphics_set, "graphics_set"
9975 &setup.sounds_set, "sounds_set"
9979 &setup.music_set, "music_set"
9983 &setup.override_level_graphics, "override_level_graphics"
9987 &setup.override_level_sounds, "override_level_sounds"
9991 &setup.override_level_music, "override_level_music"
9995 &setup.volume_simple, "volume_simple"
9999 &setup.volume_loops, "volume_loops"
10003 &setup.volume_music, "volume_music"
10007 &setup.network_mode, "network_mode"
10011 &setup.network_player_nr, "network_player"
10015 &setup.network_server_hostname, "network_server_hostname"
10019 &setup.touch.control_type, "touch.control_type"
10023 &setup.touch.move_distance, "touch.move_distance"
10027 &setup.touch.drop_distance, "touch.drop_distance"
10031 &setup.touch.transparency, "touch.transparency"
10035 &setup.touch.draw_outlined, "touch.draw_outlined"
10039 &setup.touch.draw_pressed, "touch.draw_pressed"
10043 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10047 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10051 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10055 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10059 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10063 static struct TokenInfo auto_setup_tokens[] =
10067 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10071 static struct TokenInfo server_setup_tokens[] =
10075 &setup.player_uuid, "player_uuid"
10079 &setup.player_version, "player_version"
10083 &setup.use_api_server, TEST_PREFIX "use_api_server"
10087 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10091 &setup.api_server_password, TEST_PREFIX "api_server_password"
10095 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10099 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10103 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10107 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10111 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10115 static struct TokenInfo editor_setup_tokens[] =
10119 &setup.editor.el_classic, "editor.el_classic"
10123 &setup.editor.el_custom, "editor.el_custom"
10127 &setup.editor.el_user_defined, "editor.el_user_defined"
10131 &setup.editor.el_dynamic, "editor.el_dynamic"
10135 &setup.editor.el_headlines, "editor.el_headlines"
10139 &setup.editor.show_element_token, "editor.show_element_token"
10143 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10147 static struct TokenInfo editor_cascade_setup_tokens[] =
10151 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10155 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10159 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10163 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10167 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10171 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10175 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10179 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10183 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10187 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10191 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10195 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10199 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10203 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10207 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10211 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10215 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10219 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10223 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10227 static struct TokenInfo shortcut_setup_tokens[] =
10231 &setup.shortcut.save_game, "shortcut.save_game"
10235 &setup.shortcut.load_game, "shortcut.load_game"
10239 &setup.shortcut.restart_game, "shortcut.restart_game"
10243 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10247 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10251 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10255 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10259 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10263 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10267 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10271 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10275 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10279 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10283 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10287 &setup.shortcut.tape_record, "shortcut.tape_record"
10291 &setup.shortcut.tape_play, "shortcut.tape_play"
10295 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10299 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10303 &setup.shortcut.sound_music, "shortcut.sound_music"
10307 &setup.shortcut.snap_left, "shortcut.snap_left"
10311 &setup.shortcut.snap_right, "shortcut.snap_right"
10315 &setup.shortcut.snap_up, "shortcut.snap_up"
10319 &setup.shortcut.snap_down, "shortcut.snap_down"
10323 static struct SetupInputInfo setup_input;
10324 static struct TokenInfo player_setup_tokens[] =
10328 &setup_input.use_joystick, ".use_joystick"
10332 &setup_input.joy.device_name, ".joy.device_name"
10336 &setup_input.joy.xleft, ".joy.xleft"
10340 &setup_input.joy.xmiddle, ".joy.xmiddle"
10344 &setup_input.joy.xright, ".joy.xright"
10348 &setup_input.joy.yupper, ".joy.yupper"
10352 &setup_input.joy.ymiddle, ".joy.ymiddle"
10356 &setup_input.joy.ylower, ".joy.ylower"
10360 &setup_input.joy.snap, ".joy.snap_field"
10364 &setup_input.joy.drop, ".joy.place_bomb"
10368 &setup_input.key.left, ".key.move_left"
10372 &setup_input.key.right, ".key.move_right"
10376 &setup_input.key.up, ".key.move_up"
10380 &setup_input.key.down, ".key.move_down"
10384 &setup_input.key.snap, ".key.snap_field"
10388 &setup_input.key.drop, ".key.place_bomb"
10392 static struct TokenInfo system_setup_tokens[] =
10396 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10400 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10404 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10408 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10412 static struct TokenInfo internal_setup_tokens[] =
10416 &setup.internal.program_title, "program_title"
10420 &setup.internal.program_version, "program_version"
10424 &setup.internal.program_author, "program_author"
10428 &setup.internal.program_email, "program_email"
10432 &setup.internal.program_website, "program_website"
10436 &setup.internal.program_copyright, "program_copyright"
10440 &setup.internal.program_company, "program_company"
10444 &setup.internal.program_icon_file, "program_icon_file"
10448 &setup.internal.default_graphics_set, "default_graphics_set"
10452 &setup.internal.default_sounds_set, "default_sounds_set"
10456 &setup.internal.default_music_set, "default_music_set"
10460 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10464 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10468 &setup.internal.fallback_music_file, "fallback_music_file"
10472 &setup.internal.default_level_series, "default_level_series"
10476 &setup.internal.default_window_width, "default_window_width"
10480 &setup.internal.default_window_height, "default_window_height"
10484 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10488 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10492 &setup.internal.create_user_levelset, "create_user_levelset"
10496 &setup.internal.info_screens_from_main, "info_screens_from_main"
10500 &setup.internal.menu_game, "menu_game"
10504 &setup.internal.menu_engines, "menu_engines"
10508 &setup.internal.menu_editor, "menu_editor"
10512 &setup.internal.menu_graphics, "menu_graphics"
10516 &setup.internal.menu_sound, "menu_sound"
10520 &setup.internal.menu_artwork, "menu_artwork"
10524 &setup.internal.menu_input, "menu_input"
10528 &setup.internal.menu_touch, "menu_touch"
10532 &setup.internal.menu_shortcuts, "menu_shortcuts"
10536 &setup.internal.menu_exit, "menu_exit"
10540 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10544 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10548 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10552 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10556 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10560 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10564 &setup.internal.info_title, "info_title"
10568 &setup.internal.info_elements, "info_elements"
10572 &setup.internal.info_music, "info_music"
10576 &setup.internal.info_credits, "info_credits"
10580 &setup.internal.info_program, "info_program"
10584 &setup.internal.info_version, "info_version"
10588 &setup.internal.info_levelset, "info_levelset"
10592 &setup.internal.info_exit, "info_exit"
10596 static struct TokenInfo debug_setup_tokens[] =
10600 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10604 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10608 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10612 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10616 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10620 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10624 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10628 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10632 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10636 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10640 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10644 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10648 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10652 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10656 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10660 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10664 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10668 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10672 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10676 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10680 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10683 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10687 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10691 &setup.debug.xsn_mode, "debug.xsn_mode"
10695 &setup.debug.xsn_percent, "debug.xsn_percent"
10699 static struct TokenInfo options_setup_tokens[] =
10703 &setup.options.verbose, "options.verbose"
10707 &setup.options.debug, "options.debug"
10711 &setup.options.debug_mode, "options.debug_mode"
10715 static void setSetupInfoToDefaults(struct SetupInfo *si)
10719 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10721 si->multiple_users = TRUE;
10724 si->sound_loops = TRUE;
10725 si->sound_music = TRUE;
10726 si->sound_simple = TRUE;
10728 si->global_animations = TRUE;
10729 si->scroll_delay = TRUE;
10730 si->forced_scroll_delay = FALSE;
10731 si->scroll_delay_value = STD_SCROLL_DELAY;
10732 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10733 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10734 si->fade_screens = TRUE;
10735 si->autorecord = TRUE;
10736 si->autorecord_after_replay = TRUE;
10737 si->auto_pause_on_start = FALSE;
10738 si->show_titlescreen = TRUE;
10739 si->quick_doors = FALSE;
10740 si->team_mode = FALSE;
10741 si->handicap = TRUE;
10742 si->skip_levels = TRUE;
10743 si->increment_levels = TRUE;
10744 si->auto_play_next_level = TRUE;
10745 si->count_score_after_game = TRUE;
10746 si->show_scores_after_game = TRUE;
10747 si->time_limit = TRUE;
10748 si->fullscreen = FALSE;
10749 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10750 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10751 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10752 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10753 si->ask_on_escape = TRUE;
10754 si->ask_on_escape_editor = TRUE;
10755 si->ask_on_game_over = TRUE;
10756 si->ask_on_quit_game = TRUE;
10757 si->ask_on_quit_program = TRUE;
10758 si->quick_switch = FALSE;
10759 si->input_on_focus = FALSE;
10760 si->prefer_aga_graphics = TRUE;
10761 si->prefer_lowpass_sounds = FALSE;
10762 si->prefer_extra_panel_items = TRUE;
10763 si->game_speed_extended = FALSE;
10764 si->game_frame_delay = GAME_FRAME_DELAY;
10765 si->bd_skip_uncovering = FALSE;
10766 si->bd_skip_hatching = FALSE;
10767 si->bd_scroll_delay = TRUE;
10768 si->bd_smooth_movements = AUTO;
10769 si->sp_show_border_elements = FALSE;
10770 si->small_game_graphics = FALSE;
10771 si->show_load_save_buttons = FALSE;
10772 si->show_undo_redo_buttons = FALSE;
10773 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10775 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10776 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10777 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10779 si->override_level_graphics = FALSE;
10780 si->override_level_sounds = FALSE;
10781 si->override_level_music = FALSE;
10783 si->volume_simple = 100; // percent
10784 si->volume_loops = 100; // percent
10785 si->volume_music = 100; // percent
10787 si->network_mode = FALSE;
10788 si->network_player_nr = 0; // first player
10789 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10791 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10792 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10793 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10794 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10795 si->touch.draw_outlined = TRUE;
10796 si->touch.draw_pressed = TRUE;
10798 for (i = 0; i < 2; i++)
10800 char *default_grid_button[6][2] =
10806 { "111222", " vv " },
10807 { "111222", " vv " }
10809 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10810 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10811 int min_xsize = MIN(6, grid_xsize);
10812 int min_ysize = MIN(6, grid_ysize);
10813 int startx = grid_xsize - min_xsize;
10814 int starty = grid_ysize - min_ysize;
10817 // virtual buttons grid can only be set to defaults if video is initialized
10818 // (this will be repeated if virtual buttons are not loaded from setup file)
10819 if (video.initialized)
10821 si->touch.grid_xsize[i] = grid_xsize;
10822 si->touch.grid_ysize[i] = grid_ysize;
10826 si->touch.grid_xsize[i] = -1;
10827 si->touch.grid_ysize[i] = -1;
10830 for (x = 0; x < MAX_GRID_XSIZE; x++)
10831 for (y = 0; y < MAX_GRID_YSIZE; y++)
10832 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10834 for (x = 0; x < min_xsize; x++)
10835 for (y = 0; y < min_ysize; y++)
10836 si->touch.grid_button[i][x][starty + y] =
10837 default_grid_button[y][0][x];
10839 for (x = 0; x < min_xsize; x++)
10840 for (y = 0; y < min_ysize; y++)
10841 si->touch.grid_button[i][startx + x][starty + y] =
10842 default_grid_button[y][1][x];
10845 si->touch.grid_initialized = video.initialized;
10847 si->touch.overlay_buttons = FALSE;
10849 si->editor.el_boulderdash = TRUE;
10850 si->editor.el_boulderdash_native = TRUE;
10851 si->editor.el_emerald_mine = TRUE;
10852 si->editor.el_emerald_mine_club = TRUE;
10853 si->editor.el_more = TRUE;
10854 si->editor.el_sokoban = TRUE;
10855 si->editor.el_supaplex = TRUE;
10856 si->editor.el_diamond_caves = TRUE;
10857 si->editor.el_dx_boulderdash = TRUE;
10859 si->editor.el_mirror_magic = TRUE;
10860 si->editor.el_deflektor = TRUE;
10862 si->editor.el_chars = TRUE;
10863 si->editor.el_steel_chars = TRUE;
10865 si->editor.el_classic = TRUE;
10866 si->editor.el_custom = TRUE;
10868 si->editor.el_user_defined = FALSE;
10869 si->editor.el_dynamic = TRUE;
10871 si->editor.el_headlines = TRUE;
10873 si->editor.show_element_token = FALSE;
10875 si->editor.show_read_only_warning = TRUE;
10877 si->editor.use_template_for_new_levels = TRUE;
10879 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10880 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10881 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10882 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10883 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10885 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10886 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10887 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10888 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10889 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10891 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10892 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10893 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10894 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10895 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10896 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10898 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10899 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10900 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10902 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10903 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10904 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10905 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10907 for (i = 0; i < MAX_PLAYERS; i++)
10909 si->input[i].use_joystick = FALSE;
10910 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10911 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10912 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10913 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10914 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10915 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10916 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10917 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10918 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10919 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10920 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10921 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10922 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10923 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10924 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10927 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10928 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10929 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10930 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10932 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10933 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10934 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10935 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10936 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10937 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10938 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10940 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10942 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10943 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10944 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10946 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10947 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10948 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10950 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10951 si->internal.choose_from_top_leveldir = FALSE;
10952 si->internal.show_scaling_in_title = TRUE;
10953 si->internal.create_user_levelset = TRUE;
10954 si->internal.info_screens_from_main = FALSE;
10956 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10957 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10959 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10960 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10961 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10962 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10963 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10964 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10965 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10966 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10967 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10968 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10970 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10971 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10972 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10973 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10974 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10975 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10976 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10977 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10978 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10979 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10981 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10982 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10984 si->debug.show_frames_per_second = FALSE;
10986 si->debug.xsn_mode = AUTO;
10987 si->debug.xsn_percent = 0;
10989 si->options.verbose = FALSE;
10990 si->options.debug = FALSE;
10991 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10993 #if defined(PLATFORM_ANDROID)
10994 si->fullscreen = TRUE;
10995 si->touch.overlay_buttons = TRUE;
10998 setHideSetupEntry(&setup.debug.xsn_mode);
11001 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11003 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11006 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11008 si->player_uuid = NULL; // (will be set later)
11009 si->player_version = 1; // (will be set later)
11011 si->use_api_server = TRUE;
11012 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11013 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11014 si->ask_for_uploading_tapes = TRUE;
11015 si->ask_for_remaining_tapes = FALSE;
11016 si->provide_uploading_tapes = TRUE;
11017 si->ask_for_using_api_server = TRUE;
11018 si->has_remaining_tapes = FALSE;
11021 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11023 si->editor_cascade.el_bd = TRUE;
11024 si->editor_cascade.el_bd_native = TRUE;
11025 si->editor_cascade.el_em = TRUE;
11026 si->editor_cascade.el_emc = TRUE;
11027 si->editor_cascade.el_rnd = TRUE;
11028 si->editor_cascade.el_sb = TRUE;
11029 si->editor_cascade.el_sp = TRUE;
11030 si->editor_cascade.el_dc = TRUE;
11031 si->editor_cascade.el_dx = TRUE;
11033 si->editor_cascade.el_mm = TRUE;
11034 si->editor_cascade.el_df = TRUE;
11036 si->editor_cascade.el_chars = FALSE;
11037 si->editor_cascade.el_steel_chars = FALSE;
11038 si->editor_cascade.el_ce = FALSE;
11039 si->editor_cascade.el_ge = FALSE;
11040 si->editor_cascade.el_es = FALSE;
11041 si->editor_cascade.el_ref = FALSE;
11042 si->editor_cascade.el_user = FALSE;
11043 si->editor_cascade.el_dynamic = FALSE;
11046 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11048 static char *getHideSetupToken(void *setup_value)
11050 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11052 if (setup_value != NULL)
11053 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11055 return hide_setup_token;
11058 void setHideSetupEntry(void *setup_value)
11060 char *hide_setup_token = getHideSetupToken(setup_value);
11062 if (hide_setup_hash == NULL)
11063 hide_setup_hash = newSetupFileHash();
11065 if (setup_value != NULL)
11066 setHashEntry(hide_setup_hash, hide_setup_token, "");
11069 void removeHideSetupEntry(void *setup_value)
11071 char *hide_setup_token = getHideSetupToken(setup_value);
11073 if (setup_value != NULL)
11074 removeHashEntry(hide_setup_hash, hide_setup_token);
11077 boolean hideSetupEntry(void *setup_value)
11079 char *hide_setup_token = getHideSetupToken(setup_value);
11081 return (setup_value != NULL &&
11082 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11085 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11086 struct TokenInfo *token_info,
11087 int token_nr, char *token_text)
11089 char *token_hide_text = getStringCat2(token_text, ".hide");
11090 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11092 // set the value of this setup option in the setup option structure
11093 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11095 // check if this setup option should be hidden in the setup menu
11096 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11097 setHideSetupEntry(token_info[token_nr].value);
11099 free(token_hide_text);
11102 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11103 struct TokenInfo *token_info,
11106 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11107 token_info[token_nr].text);
11110 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11114 if (!setup_file_hash)
11117 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11118 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11120 setup.touch.grid_initialized = TRUE;
11121 for (i = 0; i < 2; i++)
11123 int grid_xsize = setup.touch.grid_xsize[i];
11124 int grid_ysize = setup.touch.grid_ysize[i];
11127 // if virtual buttons are not loaded from setup file, repeat initializing
11128 // virtual buttons grid with default values later when video is initialized
11129 if (grid_xsize == -1 ||
11132 setup.touch.grid_initialized = FALSE;
11137 for (y = 0; y < grid_ysize; y++)
11139 char token_string[MAX_LINE_LEN];
11141 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11143 char *value_string = getHashEntry(setup_file_hash, token_string);
11145 if (value_string == NULL)
11148 for (x = 0; x < grid_xsize; x++)
11150 char c = value_string[x];
11152 setup.touch.grid_button[i][x][y] =
11153 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11158 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11159 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11161 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11162 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11164 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11168 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11170 setup_input = setup.input[pnr];
11171 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11173 char full_token[100];
11175 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11176 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11179 setup.input[pnr] = setup_input;
11182 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11183 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11185 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11186 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11188 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11189 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11191 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11192 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11194 setHideRelatedSetupEntries();
11197 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11201 if (!setup_file_hash)
11204 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11205 setSetupInfo(auto_setup_tokens, i,
11206 getHashEntry(setup_file_hash,
11207 auto_setup_tokens[i].text));
11210 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11214 if (!setup_file_hash)
11217 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11218 setSetupInfo(server_setup_tokens, i,
11219 getHashEntry(setup_file_hash,
11220 server_setup_tokens[i].text));
11223 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11227 if (!setup_file_hash)
11230 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11231 setSetupInfo(editor_cascade_setup_tokens, i,
11232 getHashEntry(setup_file_hash,
11233 editor_cascade_setup_tokens[i].text));
11236 void LoadUserNames(void)
11238 int last_user_nr = user.nr;
11241 if (global.user_names != NULL)
11243 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11244 checked_free(global.user_names[i]);
11246 checked_free(global.user_names);
11249 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11251 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11255 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11257 if (setup_file_hash)
11259 char *player_name = getHashEntry(setup_file_hash, "player_name");
11261 global.user_names[i] = getFixedUserName(player_name);
11263 freeSetupFileHash(setup_file_hash);
11266 if (global.user_names[i] == NULL)
11267 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11270 user.nr = last_user_nr;
11273 void LoadSetupFromFilename(char *filename)
11275 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11277 if (setup_file_hash)
11279 decodeSetupFileHash_Default(setup_file_hash);
11281 freeSetupFileHash(setup_file_hash);
11285 Debug("setup", "using default setup values");
11289 static void LoadSetup_SpecialPostProcessing(void)
11291 char *player_name_new;
11293 // needed to work around problems with fixed length strings
11294 player_name_new = getFixedUserName(setup.player_name);
11295 free(setup.player_name);
11296 setup.player_name = player_name_new;
11298 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11299 if (setup.scroll_delay == FALSE)
11301 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11302 setup.scroll_delay = TRUE; // now always "on"
11305 // make sure that scroll delay value stays inside valid range
11306 setup.scroll_delay_value =
11307 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11310 void LoadSetup_Default(void)
11314 // always start with reliable default values
11315 setSetupInfoToDefaults(&setup);
11317 // try to load setup values from default setup file
11318 filename = getDefaultSetupFilename();
11320 if (fileExists(filename))
11321 LoadSetupFromFilename(filename);
11323 // try to load setup values from platform setup file
11324 filename = getPlatformSetupFilename();
11326 if (fileExists(filename))
11327 LoadSetupFromFilename(filename);
11329 // try to load setup values from user setup file
11330 filename = getSetupFilename();
11332 LoadSetupFromFilename(filename);
11334 LoadSetup_SpecialPostProcessing();
11337 void LoadSetup_AutoSetup(void)
11339 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11340 SetupFileHash *setup_file_hash = NULL;
11342 // always start with reliable default values
11343 setSetupInfoToDefaults_AutoSetup(&setup);
11345 setup_file_hash = loadSetupFileHash(filename);
11347 if (setup_file_hash)
11349 decodeSetupFileHash_AutoSetup(setup_file_hash);
11351 freeSetupFileHash(setup_file_hash);
11357 void LoadSetup_ServerSetup(void)
11359 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11360 SetupFileHash *setup_file_hash = NULL;
11362 // always start with reliable default values
11363 setSetupInfoToDefaults_ServerSetup(&setup);
11365 setup_file_hash = loadSetupFileHash(filename);
11367 if (setup_file_hash)
11369 decodeSetupFileHash_ServerSetup(setup_file_hash);
11371 freeSetupFileHash(setup_file_hash);
11376 if (setup.player_uuid == NULL)
11378 // player UUID does not yet exist in setup file
11379 setup.player_uuid = getStringCopy(getUUID());
11380 setup.player_version = 2;
11382 SaveSetup_ServerSetup();
11386 void LoadSetup_EditorCascade(void)
11388 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11389 SetupFileHash *setup_file_hash = NULL;
11391 // always start with reliable default values
11392 setSetupInfoToDefaults_EditorCascade(&setup);
11394 setup_file_hash = loadSetupFileHash(filename);
11396 if (setup_file_hash)
11398 decodeSetupFileHash_EditorCascade(setup_file_hash);
11400 freeSetupFileHash(setup_file_hash);
11406 void LoadSetup(void)
11408 LoadSetup_Default();
11409 LoadSetup_AutoSetup();
11410 LoadSetup_ServerSetup();
11411 LoadSetup_EditorCascade();
11414 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11415 char *mapping_line)
11417 char mapping_guid[MAX_LINE_LEN];
11418 char *mapping_start, *mapping_end;
11420 // get GUID from game controller mapping line: copy complete line
11421 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11422 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11424 // get GUID from game controller mapping line: cut after GUID part
11425 mapping_start = strchr(mapping_guid, ',');
11426 if (mapping_start != NULL)
11427 *mapping_start = '\0';
11429 // cut newline from game controller mapping line
11430 mapping_end = strchr(mapping_line, '\n');
11431 if (mapping_end != NULL)
11432 *mapping_end = '\0';
11434 // add mapping entry to game controller mappings hash
11435 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11438 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11443 if (!(file = fopen(filename, MODE_READ)))
11445 Warn("cannot read game controller mappings file '%s'", filename);
11450 while (!feof(file))
11452 char line[MAX_LINE_LEN];
11454 if (!fgets(line, MAX_LINE_LEN, file))
11457 addGameControllerMappingToHash(mappings_hash, line);
11463 void SaveSetup_Default(void)
11465 char *filename = getSetupFilename();
11469 InitUserDataDirectory();
11471 if (!(file = fopen(filename, MODE_WRITE)))
11473 Warn("cannot write setup file '%s'", filename);
11478 fprintFileHeader(file, SETUP_FILENAME);
11480 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11482 // just to make things nicer :)
11483 if (global_setup_tokens[i].value == &setup.multiple_users ||
11484 global_setup_tokens[i].value == &setup.sound ||
11485 global_setup_tokens[i].value == &setup.graphics_set ||
11486 global_setup_tokens[i].value == &setup.volume_simple ||
11487 global_setup_tokens[i].value == &setup.network_mode ||
11488 global_setup_tokens[i].value == &setup.touch.control_type ||
11489 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11490 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11491 fprintf(file, "\n");
11493 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11496 for (i = 0; i < 2; i++)
11498 int grid_xsize = setup.touch.grid_xsize[i];
11499 int grid_ysize = setup.touch.grid_ysize[i];
11502 fprintf(file, "\n");
11504 for (y = 0; y < grid_ysize; y++)
11506 char token_string[MAX_LINE_LEN];
11507 char value_string[MAX_LINE_LEN];
11509 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11511 for (x = 0; x < grid_xsize; x++)
11513 char c = setup.touch.grid_button[i][x][y];
11515 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11518 value_string[grid_xsize] = '\0';
11520 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11524 fprintf(file, "\n");
11525 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11526 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11528 fprintf(file, "\n");
11529 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11530 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11532 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11536 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11537 fprintf(file, "\n");
11539 setup_input = setup.input[pnr];
11540 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11541 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11544 fprintf(file, "\n");
11545 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11546 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11548 // (internal setup values not saved to user setup file)
11550 fprintf(file, "\n");
11551 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11552 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11553 setup.debug.xsn_mode != AUTO)
11554 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11556 fprintf(file, "\n");
11557 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11558 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11562 SetFilePermissions(filename, PERMS_PRIVATE);
11565 void SaveSetup_AutoSetup(void)
11567 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11571 InitUserDataDirectory();
11573 if (!(file = fopen(filename, MODE_WRITE)))
11575 Warn("cannot write auto setup file '%s'", filename);
11582 fprintFileHeader(file, AUTOSETUP_FILENAME);
11584 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11585 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11589 SetFilePermissions(filename, PERMS_PRIVATE);
11594 void SaveSetup_ServerSetup(void)
11596 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11600 InitUserDataDirectory();
11602 if (!(file = fopen(filename, MODE_WRITE)))
11604 Warn("cannot write server setup file '%s'", filename);
11611 fprintFileHeader(file, SERVERSETUP_FILENAME);
11613 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11615 // just to make things nicer :)
11616 if (server_setup_tokens[i].value == &setup.use_api_server)
11617 fprintf(file, "\n");
11619 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11624 SetFilePermissions(filename, PERMS_PRIVATE);
11629 void SaveSetup_EditorCascade(void)
11631 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11635 InitUserDataDirectory();
11637 if (!(file = fopen(filename, MODE_WRITE)))
11639 Warn("cannot write editor cascade state file '%s'", filename);
11646 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11648 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11649 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11653 SetFilePermissions(filename, PERMS_PRIVATE);
11658 void SaveSetup(void)
11660 SaveSetup_Default();
11661 SaveSetup_AutoSetup();
11662 SaveSetup_ServerSetup();
11663 SaveSetup_EditorCascade();
11666 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11671 if (!(file = fopen(filename, MODE_WRITE)))
11673 Warn("cannot write game controller mappings file '%s'", filename);
11678 BEGIN_HASH_ITERATION(mappings_hash, itr)
11680 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11682 END_HASH_ITERATION(mappings_hash, itr)
11687 void SaveSetup_AddGameControllerMapping(char *mapping)
11689 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11690 SetupFileHash *mappings_hash = newSetupFileHash();
11692 InitUserDataDirectory();
11694 // load existing personal game controller mappings
11695 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11697 // add new mapping to personal game controller mappings
11698 addGameControllerMappingToHash(mappings_hash, mapping);
11700 // save updated personal game controller mappings
11701 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11703 freeSetupFileHash(mappings_hash);
11707 void LoadCustomElementDescriptions(void)
11709 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11710 SetupFileHash *setup_file_hash;
11713 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11715 if (element_info[i].custom_description != NULL)
11717 free(element_info[i].custom_description);
11718 element_info[i].custom_description = NULL;
11722 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11725 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11727 char *token = getStringCat2(element_info[i].token_name, ".name");
11728 char *value = getHashEntry(setup_file_hash, token);
11731 element_info[i].custom_description = getStringCopy(value);
11736 freeSetupFileHash(setup_file_hash);
11739 static int getElementFromToken(char *token)
11741 char *value = getHashEntry(element_token_hash, token);
11744 return atoi(value);
11746 Warn("unknown element token '%s'", token);
11748 return EL_UNDEFINED;
11751 void FreeGlobalAnimEventInfo(void)
11753 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11755 if (gaei->event_list == NULL)
11760 for (i = 0; i < gaei->num_event_lists; i++)
11762 checked_free(gaei->event_list[i]->event_value);
11763 checked_free(gaei->event_list[i]);
11766 checked_free(gaei->event_list);
11768 gaei->event_list = NULL;
11769 gaei->num_event_lists = 0;
11772 static int AddGlobalAnimEventList(void)
11774 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11775 int list_pos = gaei->num_event_lists++;
11777 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11778 sizeof(struct GlobalAnimEventListInfo *));
11780 gaei->event_list[list_pos] =
11781 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11783 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11785 gaeli->event_value = NULL;
11786 gaeli->num_event_values = 0;
11791 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11793 // do not add empty global animation events
11794 if (event_value == ANIM_EVENT_NONE)
11797 // if list position is undefined, create new list
11798 if (list_pos == ANIM_EVENT_UNDEFINED)
11799 list_pos = AddGlobalAnimEventList();
11801 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11802 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11803 int value_pos = gaeli->num_event_values++;
11805 gaeli->event_value = checked_realloc(gaeli->event_value,
11806 gaeli->num_event_values * sizeof(int *));
11808 gaeli->event_value[value_pos] = event_value;
11813 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11815 if (list_pos == ANIM_EVENT_UNDEFINED)
11816 return ANIM_EVENT_NONE;
11818 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11819 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11821 return gaeli->event_value[value_pos];
11824 int GetGlobalAnimEventValueCount(int list_pos)
11826 if (list_pos == ANIM_EVENT_UNDEFINED)
11829 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11830 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11832 return gaeli->num_event_values;
11835 // This function checks if a string <s> of the format "string1, string2, ..."
11836 // exactly contains a string <s_contained>.
11838 static boolean string_has_parameter(char *s, char *s_contained)
11842 if (s == NULL || s_contained == NULL)
11845 if (strlen(s_contained) > strlen(s))
11848 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11850 char next_char = s[strlen(s_contained)];
11852 // check if next character is delimiter or whitespace
11853 if (next_char == ',' || next_char == '\0' ||
11854 next_char == ' ' || next_char == '\t')
11858 // check if string contains another parameter string after a comma
11859 substring = strchr(s, ',');
11860 if (substring == NULL) // string does not contain a comma
11863 // advance string pointer to next character after the comma
11866 // skip potential whitespaces after the comma
11867 while (*substring == ' ' || *substring == '\t')
11870 return string_has_parameter(substring, s_contained);
11873 static int get_anim_parameter_value_ce(char *s)
11876 char *pattern_1 = "ce_change:custom_";
11877 char *pattern_2 = ".page_";
11878 int pattern_1_len = strlen(pattern_1);
11879 char *matching_char = strstr(s_ptr, pattern_1);
11880 int result = ANIM_EVENT_NONE;
11882 if (matching_char == NULL)
11883 return ANIM_EVENT_NONE;
11885 result = ANIM_EVENT_CE_CHANGE;
11887 s_ptr = matching_char + pattern_1_len;
11889 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11890 if (*s_ptr >= '0' && *s_ptr <= '9')
11892 int gic_ce_nr = (*s_ptr++ - '0');
11894 if (*s_ptr >= '0' && *s_ptr <= '9')
11896 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11898 if (*s_ptr >= '0' && *s_ptr <= '9')
11899 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11902 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11903 return ANIM_EVENT_NONE;
11905 // custom element stored as 0 to 255
11908 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11912 // invalid custom element number specified
11914 return ANIM_EVENT_NONE;
11917 // check for change page number ("page_X" or "page_XX") (optional)
11918 if (strPrefix(s_ptr, pattern_2))
11920 s_ptr += strlen(pattern_2);
11922 if (*s_ptr >= '0' && *s_ptr <= '9')
11924 int gic_page_nr = (*s_ptr++ - '0');
11926 if (*s_ptr >= '0' && *s_ptr <= '9')
11927 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11929 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11930 return ANIM_EVENT_NONE;
11932 // change page stored as 1 to 32 (0 means "all change pages")
11934 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11938 // invalid animation part number specified
11940 return ANIM_EVENT_NONE;
11944 // discard result if next character is neither delimiter nor whitespace
11945 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11946 *s_ptr == ' ' || *s_ptr == '\t'))
11947 return ANIM_EVENT_NONE;
11952 static int get_anim_parameter_value(char *s)
11954 int event_value[] =
11962 char *pattern_1[] =
11970 char *pattern_2 = ".part_";
11971 char *matching_char = NULL;
11973 int pattern_1_len = 0;
11974 int result = ANIM_EVENT_NONE;
11977 result = get_anim_parameter_value_ce(s);
11979 if (result != ANIM_EVENT_NONE)
11982 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11984 matching_char = strstr(s_ptr, pattern_1[i]);
11985 pattern_1_len = strlen(pattern_1[i]);
11986 result = event_value[i];
11988 if (matching_char != NULL)
11992 if (matching_char == NULL)
11993 return ANIM_EVENT_NONE;
11995 s_ptr = matching_char + pattern_1_len;
11997 // check for main animation number ("anim_X" or "anim_XX")
11998 if (*s_ptr >= '0' && *s_ptr <= '9')
12000 int gic_anim_nr = (*s_ptr++ - '0');
12002 if (*s_ptr >= '0' && *s_ptr <= '9')
12003 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12005 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12006 return ANIM_EVENT_NONE;
12008 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12012 // invalid main animation number specified
12014 return ANIM_EVENT_NONE;
12017 // check for animation part number ("part_X" or "part_XX") (optional)
12018 if (strPrefix(s_ptr, pattern_2))
12020 s_ptr += strlen(pattern_2);
12022 if (*s_ptr >= '0' && *s_ptr <= '9')
12024 int gic_part_nr = (*s_ptr++ - '0');
12026 if (*s_ptr >= '0' && *s_ptr <= '9')
12027 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12029 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12030 return ANIM_EVENT_NONE;
12032 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12036 // invalid animation part number specified
12038 return ANIM_EVENT_NONE;
12042 // discard result if next character is neither delimiter nor whitespace
12043 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12044 *s_ptr == ' ' || *s_ptr == '\t'))
12045 return ANIM_EVENT_NONE;
12050 static int get_anim_parameter_values(char *s)
12052 int list_pos = ANIM_EVENT_UNDEFINED;
12053 int event_value = ANIM_EVENT_DEFAULT;
12055 if (string_has_parameter(s, "any"))
12056 event_value |= ANIM_EVENT_ANY;
12058 if (string_has_parameter(s, "click:self") ||
12059 string_has_parameter(s, "click") ||
12060 string_has_parameter(s, "self"))
12061 event_value |= ANIM_EVENT_SELF;
12063 if (string_has_parameter(s, "unclick:any"))
12064 event_value |= ANIM_EVENT_UNCLICK_ANY;
12066 // if animation event found, add it to global animation event list
12067 if (event_value != ANIM_EVENT_NONE)
12068 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12072 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12073 event_value = get_anim_parameter_value(s);
12075 // if animation event found, add it to global animation event list
12076 if (event_value != ANIM_EVENT_NONE)
12077 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12079 // continue with next part of the string, starting with next comma
12080 s = strchr(s + 1, ',');
12086 static int get_anim_action_parameter_value(char *token)
12088 // check most common default case first to massively speed things up
12089 if (strEqual(token, ARG_UNDEFINED))
12090 return ANIM_EVENT_ACTION_NONE;
12092 int result = getImageIDFromToken(token);
12096 char *gfx_token = getStringCat2("gfx.", token);
12098 result = getImageIDFromToken(gfx_token);
12100 checked_free(gfx_token);
12105 Key key = getKeyFromX11KeyName(token);
12107 if (key != KSYM_UNDEFINED)
12108 result = -(int)key;
12115 result = get_hash_from_string(token); // unsigned int => int
12116 result = ABS(result); // may be negative now
12117 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12119 setHashEntry(anim_url_hash, int2str(result, 0), token);
12124 result = ANIM_EVENT_ACTION_NONE;
12129 int get_parameter_value(char *value_raw, char *suffix, int type)
12131 char *value = getStringToLower(value_raw);
12132 int result = 0; // probably a save default value
12134 if (strEqual(suffix, ".direction"))
12136 result = (strEqual(value, "left") ? MV_LEFT :
12137 strEqual(value, "right") ? MV_RIGHT :
12138 strEqual(value, "up") ? MV_UP :
12139 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12141 else if (strEqual(suffix, ".position"))
12143 result = (strEqual(value, "left") ? POS_LEFT :
12144 strEqual(value, "right") ? POS_RIGHT :
12145 strEqual(value, "top") ? POS_TOP :
12146 strEqual(value, "upper") ? POS_UPPER :
12147 strEqual(value, "middle") ? POS_MIDDLE :
12148 strEqual(value, "lower") ? POS_LOWER :
12149 strEqual(value, "bottom") ? POS_BOTTOM :
12150 strEqual(value, "any") ? POS_ANY :
12151 strEqual(value, "ce") ? POS_CE :
12152 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12153 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12155 else if (strEqual(suffix, ".align"))
12157 result = (strEqual(value, "left") ? ALIGN_LEFT :
12158 strEqual(value, "right") ? ALIGN_RIGHT :
12159 strEqual(value, "center") ? ALIGN_CENTER :
12160 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12162 else if (strEqual(suffix, ".valign"))
12164 result = (strEqual(value, "top") ? VALIGN_TOP :
12165 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12166 strEqual(value, "middle") ? VALIGN_MIDDLE :
12167 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12169 else if (strEqual(suffix, ".anim_mode"))
12171 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12172 string_has_parameter(value, "loop") ? ANIM_LOOP :
12173 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12174 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12175 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12176 string_has_parameter(value, "random") ? ANIM_RANDOM :
12177 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12178 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12179 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12180 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12181 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12182 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12183 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12184 string_has_parameter(value, "all") ? ANIM_ALL :
12185 string_has_parameter(value, "tiled") ? ANIM_TILED :
12186 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12189 if (string_has_parameter(value, "once"))
12190 result |= ANIM_ONCE;
12192 if (string_has_parameter(value, "reverse"))
12193 result |= ANIM_REVERSE;
12195 if (string_has_parameter(value, "opaque_player"))
12196 result |= ANIM_OPAQUE_PLAYER;
12198 if (string_has_parameter(value, "static_panel"))
12199 result |= ANIM_STATIC_PANEL;
12201 else if (strEqual(suffix, ".init_event") ||
12202 strEqual(suffix, ".anim_event"))
12204 result = get_anim_parameter_values(value);
12206 else if (strEqual(suffix, ".init_delay_action") ||
12207 strEqual(suffix, ".anim_delay_action") ||
12208 strEqual(suffix, ".post_delay_action") ||
12209 strEqual(suffix, ".init_event_action") ||
12210 strEqual(suffix, ".anim_event_action"))
12212 result = get_anim_action_parameter_value(value_raw);
12214 else if (strEqual(suffix, ".class"))
12216 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12217 get_hash_from_string(value));
12219 else if (strEqual(suffix, ".style"))
12221 result = STYLE_DEFAULT;
12223 if (string_has_parameter(value, "accurate_borders"))
12224 result |= STYLE_ACCURATE_BORDERS;
12226 if (string_has_parameter(value, "inner_corners"))
12227 result |= STYLE_INNER_CORNERS;
12229 if (string_has_parameter(value, "reverse"))
12230 result |= STYLE_REVERSE;
12232 if (string_has_parameter(value, "leftmost_position"))
12233 result |= STYLE_LEFTMOST_POSITION;
12235 if (string_has_parameter(value, "block_clicks"))
12236 result |= STYLE_BLOCK;
12238 if (string_has_parameter(value, "passthrough_clicks"))
12239 result |= STYLE_PASSTHROUGH;
12241 if (string_has_parameter(value, "multiple_actions"))
12242 result |= STYLE_MULTIPLE_ACTIONS;
12244 if (string_has_parameter(value, "consume_ce_event"))
12245 result |= STYLE_CONSUME_CE_EVENT;
12247 else if (strEqual(suffix, ".fade_mode"))
12249 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12250 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12251 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12252 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12253 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12254 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12255 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12256 FADE_MODE_DEFAULT);
12258 else if (strEqual(suffix, ".auto_delay_unit"))
12260 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12261 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12262 AUTO_DELAY_UNIT_DEFAULT);
12264 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12266 result = gfx.get_font_from_token_function(value);
12268 else // generic parameter of type integer or boolean
12270 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12271 type == TYPE_INTEGER ? get_integer_from_string(value) :
12272 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12273 ARG_UNDEFINED_VALUE);
12281 static int get_token_parameter_value(char *token, char *value_raw)
12285 if (token == NULL || value_raw == NULL)
12286 return ARG_UNDEFINED_VALUE;
12288 suffix = strrchr(token, '.');
12289 if (suffix == NULL)
12292 if (strEqual(suffix, ".element"))
12293 return getElementFromToken(value_raw);
12295 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12296 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12299 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12300 boolean ignore_defaults)
12304 for (i = 0; image_config_vars[i].token != NULL; i++)
12306 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12308 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12309 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12313 *image_config_vars[i].value =
12314 get_token_parameter_value(image_config_vars[i].token, value);
12318 void InitMenuDesignSettings_Static(void)
12320 // always start with reliable default values from static default config
12321 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12324 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12328 // the following initializes hierarchical values from static configuration
12330 // special case: initialize "ARG_DEFAULT" values in static default config
12331 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12332 titlescreen_initial_first_default.fade_mode =
12333 title_initial_first_default.fade_mode;
12334 titlescreen_initial_first_default.fade_delay =
12335 title_initial_first_default.fade_delay;
12336 titlescreen_initial_first_default.post_delay =
12337 title_initial_first_default.post_delay;
12338 titlescreen_initial_first_default.auto_delay =
12339 title_initial_first_default.auto_delay;
12340 titlescreen_initial_first_default.auto_delay_unit =
12341 title_initial_first_default.auto_delay_unit;
12342 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12343 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12344 titlescreen_first_default.post_delay = title_first_default.post_delay;
12345 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12346 titlescreen_first_default.auto_delay_unit =
12347 title_first_default.auto_delay_unit;
12348 titlemessage_initial_first_default.fade_mode =
12349 title_initial_first_default.fade_mode;
12350 titlemessage_initial_first_default.fade_delay =
12351 title_initial_first_default.fade_delay;
12352 titlemessage_initial_first_default.post_delay =
12353 title_initial_first_default.post_delay;
12354 titlemessage_initial_first_default.auto_delay =
12355 title_initial_first_default.auto_delay;
12356 titlemessage_initial_first_default.auto_delay_unit =
12357 title_initial_first_default.auto_delay_unit;
12358 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12359 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12360 titlemessage_first_default.post_delay = title_first_default.post_delay;
12361 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12362 titlemessage_first_default.auto_delay_unit =
12363 title_first_default.auto_delay_unit;
12365 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12366 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12367 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12368 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12369 titlescreen_initial_default.auto_delay_unit =
12370 title_initial_default.auto_delay_unit;
12371 titlescreen_default.fade_mode = title_default.fade_mode;
12372 titlescreen_default.fade_delay = title_default.fade_delay;
12373 titlescreen_default.post_delay = title_default.post_delay;
12374 titlescreen_default.auto_delay = title_default.auto_delay;
12375 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12376 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12377 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12378 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12379 titlemessage_initial_default.auto_delay_unit =
12380 title_initial_default.auto_delay_unit;
12381 titlemessage_default.fade_mode = title_default.fade_mode;
12382 titlemessage_default.fade_delay = title_default.fade_delay;
12383 titlemessage_default.post_delay = title_default.post_delay;
12384 titlemessage_default.auto_delay = title_default.auto_delay;
12385 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12387 // special case: initialize "ARG_DEFAULT" values in static default config
12388 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12389 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12391 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12392 titlescreen_first[i] = titlescreen_first_default;
12393 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12394 titlemessage_first[i] = titlemessage_first_default;
12396 titlescreen_initial[i] = titlescreen_initial_default;
12397 titlescreen[i] = titlescreen_default;
12398 titlemessage_initial[i] = titlemessage_initial_default;
12399 titlemessage[i] = titlemessage_default;
12402 // special case: initialize "ARG_DEFAULT" values in static default config
12403 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12404 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12406 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12409 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12410 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12411 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12414 // special case: initialize "ARG_DEFAULT" values in static default config
12415 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12416 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12418 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12419 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12420 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12422 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12425 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12429 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12433 struct XY *dst, *src;
12435 game_buttons_xy[] =
12437 { &game.button.save, &game.button.stop },
12438 { &game.button.pause2, &game.button.pause },
12439 { &game.button.load, &game.button.play },
12440 { &game.button.undo, &game.button.stop },
12441 { &game.button.redo, &game.button.play },
12447 // special case: initialize later added SETUP list size from LEVELS value
12448 if (menu.list_size[GAME_MODE_SETUP] == -1)
12449 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12451 // set default position for snapshot buttons to stop/pause/play buttons
12452 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12453 if ((*game_buttons_xy[i].dst).x == -1 &&
12454 (*game_buttons_xy[i].dst).y == -1)
12455 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12457 // --------------------------------------------------------------------------
12458 // dynamic viewports (including playfield margins, borders and alignments)
12459 // --------------------------------------------------------------------------
12461 // dynamic viewports currently only supported for landscape mode
12462 int display_width = MAX(video.display_width, video.display_height);
12463 int display_height = MIN(video.display_width, video.display_height);
12465 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12467 struct RectWithBorder *vp_window = &viewport.window[i];
12468 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12469 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12470 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12471 boolean dynamic_window_width = (vp_window->min_width != -1);
12472 boolean dynamic_window_height = (vp_window->min_height != -1);
12473 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12474 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12476 // adjust window size if min/max width/height is specified
12478 if (vp_window->min_width != -1)
12480 int window_width = display_width;
12482 // when using static window height, use aspect ratio of display
12483 if (vp_window->min_height == -1)
12484 window_width = vp_window->height * display_width / display_height;
12486 vp_window->width = MAX(vp_window->min_width, window_width);
12489 if (vp_window->min_height != -1)
12491 int window_height = display_height;
12493 // when using static window width, use aspect ratio of display
12494 if (vp_window->min_width == -1)
12495 window_height = vp_window->width * display_height / display_width;
12497 vp_window->height = MAX(vp_window->min_height, window_height);
12500 if (vp_window->max_width != -1)
12501 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12503 if (vp_window->max_height != -1)
12504 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12506 int playfield_width = vp_window->width;
12507 int playfield_height = vp_window->height;
12509 // adjust playfield size and position according to specified margins
12511 playfield_width -= vp_playfield->margin_left;
12512 playfield_width -= vp_playfield->margin_right;
12514 playfield_height -= vp_playfield->margin_top;
12515 playfield_height -= vp_playfield->margin_bottom;
12517 // adjust playfield size if min/max width/height is specified
12519 if (vp_playfield->min_width != -1)
12520 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12522 if (vp_playfield->min_height != -1)
12523 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12525 if (vp_playfield->max_width != -1)
12526 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12528 if (vp_playfield->max_height != -1)
12529 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12531 // adjust playfield position according to specified alignment
12533 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12534 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12535 else if (vp_playfield->align == ALIGN_CENTER)
12536 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12537 else if (vp_playfield->align == ALIGN_RIGHT)
12538 vp_playfield->x += playfield_width - vp_playfield->width;
12540 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12541 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12542 else if (vp_playfield->valign == VALIGN_MIDDLE)
12543 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12544 else if (vp_playfield->valign == VALIGN_BOTTOM)
12545 vp_playfield->y += playfield_height - vp_playfield->height;
12547 vp_playfield->x += vp_playfield->margin_left;
12548 vp_playfield->y += vp_playfield->margin_top;
12550 // adjust individual playfield borders if only default border is specified
12552 if (vp_playfield->border_left == -1)
12553 vp_playfield->border_left = vp_playfield->border_size;
12554 if (vp_playfield->border_right == -1)
12555 vp_playfield->border_right = vp_playfield->border_size;
12556 if (vp_playfield->border_top == -1)
12557 vp_playfield->border_top = vp_playfield->border_size;
12558 if (vp_playfield->border_bottom == -1)
12559 vp_playfield->border_bottom = vp_playfield->border_size;
12561 // set dynamic playfield borders if borders are specified as undefined
12562 // (but only if window size was dynamic and playfield size was static)
12564 if (dynamic_window_width && !dynamic_playfield_width)
12566 if (vp_playfield->border_left == -1)
12568 vp_playfield->border_left = (vp_playfield->x -
12569 vp_playfield->margin_left);
12570 vp_playfield->x -= vp_playfield->border_left;
12571 vp_playfield->width += vp_playfield->border_left;
12574 if (vp_playfield->border_right == -1)
12576 vp_playfield->border_right = (vp_window->width -
12578 vp_playfield->width -
12579 vp_playfield->margin_right);
12580 vp_playfield->width += vp_playfield->border_right;
12584 if (dynamic_window_height && !dynamic_playfield_height)
12586 if (vp_playfield->border_top == -1)
12588 vp_playfield->border_top = (vp_playfield->y -
12589 vp_playfield->margin_top);
12590 vp_playfield->y -= vp_playfield->border_top;
12591 vp_playfield->height += vp_playfield->border_top;
12594 if (vp_playfield->border_bottom == -1)
12596 vp_playfield->border_bottom = (vp_window->height -
12598 vp_playfield->height -
12599 vp_playfield->margin_bottom);
12600 vp_playfield->height += vp_playfield->border_bottom;
12604 // adjust playfield size to be a multiple of a defined alignment tile size
12606 int align_size = vp_playfield->align_size;
12607 int playfield_xtiles = vp_playfield->width / align_size;
12608 int playfield_ytiles = vp_playfield->height / align_size;
12609 int playfield_width_corrected = playfield_xtiles * align_size;
12610 int playfield_height_corrected = playfield_ytiles * align_size;
12611 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12612 i == GFX_SPECIAL_ARG_EDITOR);
12614 if (is_playfield_mode &&
12615 dynamic_playfield_width &&
12616 vp_playfield->width != playfield_width_corrected)
12618 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12620 vp_playfield->width = playfield_width_corrected;
12622 if (vp_playfield->align == ALIGN_LEFT)
12624 vp_playfield->border_left += playfield_xdiff;
12626 else if (vp_playfield->align == ALIGN_RIGHT)
12628 vp_playfield->border_right += playfield_xdiff;
12630 else if (vp_playfield->align == ALIGN_CENTER)
12632 int border_left_diff = playfield_xdiff / 2;
12633 int border_right_diff = playfield_xdiff - border_left_diff;
12635 vp_playfield->border_left += border_left_diff;
12636 vp_playfield->border_right += border_right_diff;
12640 if (is_playfield_mode &&
12641 dynamic_playfield_height &&
12642 vp_playfield->height != playfield_height_corrected)
12644 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12646 vp_playfield->height = playfield_height_corrected;
12648 if (vp_playfield->valign == VALIGN_TOP)
12650 vp_playfield->border_top += playfield_ydiff;
12652 else if (vp_playfield->align == VALIGN_BOTTOM)
12654 vp_playfield->border_right += playfield_ydiff;
12656 else if (vp_playfield->align == VALIGN_MIDDLE)
12658 int border_top_diff = playfield_ydiff / 2;
12659 int border_bottom_diff = playfield_ydiff - border_top_diff;
12661 vp_playfield->border_top += border_top_diff;
12662 vp_playfield->border_bottom += border_bottom_diff;
12666 // adjust door positions according to specified alignment
12668 for (j = 0; j < 2; j++)
12670 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12672 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12673 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12674 else if (vp_door->align == ALIGN_CENTER)
12675 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12676 else if (vp_door->align == ALIGN_RIGHT)
12677 vp_door->x += vp_window->width - vp_door->width;
12679 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12680 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12681 else if (vp_door->valign == VALIGN_MIDDLE)
12682 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12683 else if (vp_door->valign == VALIGN_BOTTOM)
12684 vp_door->y += vp_window->height - vp_door->height;
12689 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12693 struct XYTileSize *dst, *src;
12696 editor_buttons_xy[] =
12699 &editor.button.element_left, &editor.palette.element_left,
12700 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12703 &editor.button.element_middle, &editor.palette.element_middle,
12704 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12707 &editor.button.element_right, &editor.palette.element_right,
12708 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12715 // set default position for element buttons to element graphics
12716 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12718 if ((*editor_buttons_xy[i].dst).x == -1 &&
12719 (*editor_buttons_xy[i].dst).y == -1)
12721 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12723 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12725 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12729 // adjust editor palette rows and columns if specified to be dynamic
12731 if (editor.palette.cols == -1)
12733 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12734 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12735 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12737 editor.palette.cols = (vp_width - sc_width) / bt_width;
12739 if (editor.palette.x == -1)
12741 int palette_width = editor.palette.cols * bt_width + sc_width;
12743 editor.palette.x = (vp_width - palette_width) / 2;
12747 if (editor.palette.rows == -1)
12749 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12750 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12751 int tx_height = getFontHeight(FONT_TEXT_2);
12753 editor.palette.rows = (vp_height - tx_height) / bt_height;
12755 if (editor.palette.y == -1)
12757 int palette_height = editor.palette.rows * bt_height + tx_height;
12759 editor.palette.y = (vp_height - palette_height) / 2;
12764 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12765 boolean initialize)
12767 // special case: check if network and preview player positions are redefined,
12768 // to compare this later against the main menu level preview being redefined
12769 struct TokenIntPtrInfo menu_config_players[] =
12771 { "main.network_players.x", &menu.main.network_players.redefined },
12772 { "main.network_players.y", &menu.main.network_players.redefined },
12773 { "main.preview_players.x", &menu.main.preview_players.redefined },
12774 { "main.preview_players.y", &menu.main.preview_players.redefined },
12775 { "preview.x", &preview.redefined },
12776 { "preview.y", &preview.redefined }
12782 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12783 *menu_config_players[i].value = FALSE;
12787 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12788 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12789 *menu_config_players[i].value = TRUE;
12793 static void InitMenuDesignSettings_PreviewPlayers(void)
12795 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12798 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12800 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12803 static void LoadMenuDesignSettingsFromFilename(char *filename)
12805 static struct TitleFadingInfo tfi;
12806 static struct TitleMessageInfo tmi;
12807 static struct TokenInfo title_tokens[] =
12809 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12810 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12811 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12812 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12813 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12817 static struct TokenInfo titlemessage_tokens[] =
12819 { TYPE_INTEGER, &tmi.x, ".x" },
12820 { TYPE_INTEGER, &tmi.y, ".y" },
12821 { TYPE_INTEGER, &tmi.width, ".width" },
12822 { TYPE_INTEGER, &tmi.height, ".height" },
12823 { TYPE_INTEGER, &tmi.chars, ".chars" },
12824 { TYPE_INTEGER, &tmi.lines, ".lines" },
12825 { TYPE_INTEGER, &tmi.align, ".align" },
12826 { TYPE_INTEGER, &tmi.valign, ".valign" },
12827 { TYPE_INTEGER, &tmi.font, ".font" },
12828 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12829 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12830 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12831 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12832 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12833 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12834 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12835 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12836 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12842 struct TitleFadingInfo *info;
12847 // initialize first titles from "enter screen" definitions, if defined
12848 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12849 { &title_first_default, "menu.enter_screen.TITLE" },
12851 // initialize title screens from "next screen" definitions, if defined
12852 { &title_initial_default, "menu.next_screen.TITLE" },
12853 { &title_default, "menu.next_screen.TITLE" },
12859 struct TitleMessageInfo *array;
12862 titlemessage_arrays[] =
12864 // initialize first titles from "enter screen" definitions, if defined
12865 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12866 { titlescreen_first, "menu.enter_screen.TITLE" },
12867 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12868 { titlemessage_first, "menu.enter_screen.TITLE" },
12870 // initialize titles from "next screen" definitions, if defined
12871 { titlescreen_initial, "menu.next_screen.TITLE" },
12872 { titlescreen, "menu.next_screen.TITLE" },
12873 { titlemessage_initial, "menu.next_screen.TITLE" },
12874 { titlemessage, "menu.next_screen.TITLE" },
12876 // overwrite titles with title definitions, if defined
12877 { titlescreen_initial_first, "[title_initial]" },
12878 { titlescreen_first, "[title]" },
12879 { titlemessage_initial_first, "[title_initial]" },
12880 { titlemessage_first, "[title]" },
12882 { titlescreen_initial, "[title_initial]" },
12883 { titlescreen, "[title]" },
12884 { titlemessage_initial, "[title_initial]" },
12885 { titlemessage, "[title]" },
12887 // overwrite titles with title screen/message definitions, if defined
12888 { titlescreen_initial_first, "[titlescreen_initial]" },
12889 { titlescreen_first, "[titlescreen]" },
12890 { titlemessage_initial_first, "[titlemessage_initial]" },
12891 { titlemessage_first, "[titlemessage]" },
12893 { titlescreen_initial, "[titlescreen_initial]" },
12894 { titlescreen, "[titlescreen]" },
12895 { titlemessage_initial, "[titlemessage_initial]" },
12896 { titlemessage, "[titlemessage]" },
12900 SetupFileHash *setup_file_hash;
12903 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12906 // the following initializes hierarchical values from dynamic configuration
12908 // special case: initialize with default values that may be overwritten
12909 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12910 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12912 struct TokenIntPtrInfo menu_config[] =
12914 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12915 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12916 { "menu.list_size", &menu.list_size[i] }
12919 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12921 char *token = menu_config[j].token;
12922 char *value = getHashEntry(setup_file_hash, token);
12925 *menu_config[j].value = get_integer_from_string(value);
12929 // special case: initialize with default values that may be overwritten
12930 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12931 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12933 struct TokenIntPtrInfo menu_config[] =
12935 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12936 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12937 { "menu.list_size.INFO", &menu.list_size_info[i] },
12938 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12939 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12942 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12944 char *token = menu_config[j].token;
12945 char *value = getHashEntry(setup_file_hash, token);
12948 *menu_config[j].value = get_integer_from_string(value);
12952 // special case: initialize with default values that may be overwritten
12953 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12954 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12956 struct TokenIntPtrInfo menu_config[] =
12958 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12959 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12962 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12964 char *token = menu_config[j].token;
12965 char *value = getHashEntry(setup_file_hash, token);
12968 *menu_config[j].value = get_integer_from_string(value);
12972 // special case: initialize with default values that may be overwritten
12973 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12974 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12976 struct TokenIntPtrInfo menu_config[] =
12978 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12979 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12980 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12981 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12982 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12983 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12984 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12985 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12986 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12987 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12990 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12992 char *token = menu_config[j].token;
12993 char *value = getHashEntry(setup_file_hash, token);
12996 *menu_config[j].value = get_integer_from_string(value);
13000 // special case: initialize with default values that may be overwritten
13001 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13002 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13004 struct TokenIntPtrInfo menu_config[] =
13006 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
13007 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13008 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13009 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
13010 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13011 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13012 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
13013 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
13014 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
13017 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13019 char *token = menu_config[j].token;
13020 char *value = getHashEntry(setup_file_hash, token);
13023 *menu_config[j].value = get_token_parameter_value(token, value);
13027 // special case: initialize with default values that may be overwritten
13028 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13029 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13033 char *token_prefix;
13034 struct RectWithBorder *struct_ptr;
13038 { "viewport.window", &viewport.window[i] },
13039 { "viewport.playfield", &viewport.playfield[i] },
13040 { "viewport.door_1", &viewport.door_1[i] },
13041 { "viewport.door_2", &viewport.door_2[i] }
13044 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13046 struct TokenIntPtrInfo vp_config[] =
13048 { ".x", &vp_struct[j].struct_ptr->x },
13049 { ".y", &vp_struct[j].struct_ptr->y },
13050 { ".width", &vp_struct[j].struct_ptr->width },
13051 { ".height", &vp_struct[j].struct_ptr->height },
13052 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13053 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13054 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13055 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13056 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13057 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13058 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13059 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13060 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13061 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13062 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13063 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13064 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13065 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13066 { ".align", &vp_struct[j].struct_ptr->align },
13067 { ".valign", &vp_struct[j].struct_ptr->valign }
13070 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13072 char *token = getStringCat2(vp_struct[j].token_prefix,
13073 vp_config[k].token);
13074 char *value = getHashEntry(setup_file_hash, token);
13077 *vp_config[k].value = get_token_parameter_value(token, value);
13084 // special case: initialize with default values that may be overwritten
13085 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13086 for (i = 0; title_info[i].info != NULL; i++)
13088 struct TitleFadingInfo *info = title_info[i].info;
13089 char *base_token = title_info[i].text;
13091 for (j = 0; title_tokens[j].type != -1; j++)
13093 char *token = getStringCat2(base_token, title_tokens[j].text);
13094 char *value = getHashEntry(setup_file_hash, token);
13098 int parameter_value = get_token_parameter_value(token, value);
13102 *(int *)title_tokens[j].value = (int)parameter_value;
13111 // special case: initialize with default values that may be overwritten
13112 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13113 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13115 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13116 char *base_token = titlemessage_arrays[i].text;
13118 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13120 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13121 char *value = getHashEntry(setup_file_hash, token);
13125 int parameter_value = get_token_parameter_value(token, value);
13127 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13131 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13132 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13134 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13144 // read (and overwrite with) values that may be specified in config file
13145 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13147 // special case: check if network and preview player positions are redefined
13148 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13150 freeSetupFileHash(setup_file_hash);
13153 void LoadMenuDesignSettings(void)
13155 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13157 InitMenuDesignSettings_Static();
13158 InitMenuDesignSettings_SpecialPreProcessing();
13159 InitMenuDesignSettings_PreviewPlayers();
13161 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13163 // first look for special settings configured in level series config
13164 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13166 if (fileExists(filename_base))
13167 LoadMenuDesignSettingsFromFilename(filename_base);
13170 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13172 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13173 LoadMenuDesignSettingsFromFilename(filename_local);
13175 InitMenuDesignSettings_SpecialPostProcessing();
13178 void LoadMenuDesignSettings_AfterGraphics(void)
13180 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13183 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13184 boolean ignore_defaults)
13188 for (i = 0; sound_config_vars[i].token != NULL; i++)
13190 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13192 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13193 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13197 *sound_config_vars[i].value =
13198 get_token_parameter_value(sound_config_vars[i].token, value);
13202 void InitSoundSettings_Static(void)
13204 // always start with reliable default values from static default config
13205 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13208 static void LoadSoundSettingsFromFilename(char *filename)
13210 SetupFileHash *setup_file_hash;
13212 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13215 // read (and overwrite with) values that may be specified in config file
13216 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13218 freeSetupFileHash(setup_file_hash);
13221 void LoadSoundSettings(void)
13223 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13225 InitSoundSettings_Static();
13227 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13229 // first look for special settings configured in level series config
13230 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13232 if (fileExists(filename_base))
13233 LoadSoundSettingsFromFilename(filename_base);
13236 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13238 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13239 LoadSoundSettingsFromFilename(filename_local);
13242 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13244 char *filename = getEditorSetupFilename();
13245 SetupFileList *setup_file_list, *list;
13246 SetupFileHash *element_hash;
13247 int num_unknown_tokens = 0;
13250 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13253 element_hash = newSetupFileHash();
13255 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13256 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13258 // determined size may be larger than needed (due to unknown elements)
13260 for (list = setup_file_list; list != NULL; list = list->next)
13263 // add space for up to 3 more elements for padding that may be needed
13264 *num_elements += 3;
13266 // free memory for old list of elements, if needed
13267 checked_free(*elements);
13269 // allocate memory for new list of elements
13270 *elements = checked_malloc(*num_elements * sizeof(int));
13273 for (list = setup_file_list; list != NULL; list = list->next)
13275 char *value = getHashEntry(element_hash, list->token);
13277 if (value == NULL) // try to find obsolete token mapping
13279 char *mapped_token = get_mapped_token(list->token);
13281 if (mapped_token != NULL)
13283 value = getHashEntry(element_hash, mapped_token);
13285 free(mapped_token);
13291 (*elements)[(*num_elements)++] = atoi(value);
13295 if (num_unknown_tokens == 0)
13298 Warn("unknown token(s) found in config file:");
13299 Warn("- config file: '%s'", filename);
13301 num_unknown_tokens++;
13304 Warn("- token: '%s'", list->token);
13308 if (num_unknown_tokens > 0)
13311 while (*num_elements % 4) // pad with empty elements, if needed
13312 (*elements)[(*num_elements)++] = EL_EMPTY;
13314 freeSetupFileList(setup_file_list);
13315 freeSetupFileHash(element_hash);
13318 for (i = 0; i < *num_elements; i++)
13319 Debug("editor", "element '%s' [%d]\n",
13320 element_info[(*elements)[i]].token_name, (*elements)[i]);
13324 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13327 SetupFileHash *setup_file_hash = NULL;
13328 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13329 char *filename_music, *filename_prefix, *filename_info;
13335 token_to_value_ptr[] =
13337 { "title_header", &tmp_music_file_info.title_header },
13338 { "artist_header", &tmp_music_file_info.artist_header },
13339 { "album_header", &tmp_music_file_info.album_header },
13340 { "year_header", &tmp_music_file_info.year_header },
13341 { "played_header", &tmp_music_file_info.played_header },
13343 { "title", &tmp_music_file_info.title },
13344 { "artist", &tmp_music_file_info.artist },
13345 { "album", &tmp_music_file_info.album },
13346 { "year", &tmp_music_file_info.year },
13347 { "played", &tmp_music_file_info.played },
13353 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13354 getCustomMusicFilename(basename));
13356 if (filename_music == NULL)
13359 // ---------- try to replace file extension ----------
13361 filename_prefix = getStringCopy(filename_music);
13362 if (strrchr(filename_prefix, '.') != NULL)
13363 *strrchr(filename_prefix, '.') = '\0';
13364 filename_info = getStringCat2(filename_prefix, ".txt");
13366 if (fileExists(filename_info))
13367 setup_file_hash = loadSetupFileHash(filename_info);
13369 free(filename_prefix);
13370 free(filename_info);
13372 if (setup_file_hash == NULL)
13374 // ---------- try to add file extension ----------
13376 filename_prefix = getStringCopy(filename_music);
13377 filename_info = getStringCat2(filename_prefix, ".txt");
13379 if (fileExists(filename_info))
13380 setup_file_hash = loadSetupFileHash(filename_info);
13382 free(filename_prefix);
13383 free(filename_info);
13386 if (setup_file_hash == NULL)
13389 // ---------- music file info found ----------
13391 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13393 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13395 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13397 *token_to_value_ptr[i].value_ptr =
13398 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13401 tmp_music_file_info.basename = getStringCopy(basename);
13402 tmp_music_file_info.music = music;
13403 tmp_music_file_info.is_sound = is_sound;
13405 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13406 *new_music_file_info = tmp_music_file_info;
13408 return new_music_file_info;
13411 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13413 return get_music_file_info_ext(basename, music, FALSE);
13416 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13418 return get_music_file_info_ext(basename, sound, TRUE);
13421 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13422 char *basename, boolean is_sound)
13424 for (; list != NULL; list = list->next)
13425 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13431 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13433 return music_info_listed_ext(list, basename, FALSE);
13436 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13438 return music_info_listed_ext(list, basename, TRUE);
13441 void LoadMusicInfo(void)
13443 int num_music_noconf = getMusicListSize_NoConf();
13444 int num_music = getMusicListSize();
13445 int num_sounds = getSoundListSize();
13446 struct FileInfo *music, *sound;
13447 struct MusicFileInfo *next, **new;
13451 while (music_file_info != NULL)
13453 next = music_file_info->next;
13455 checked_free(music_file_info->basename);
13457 checked_free(music_file_info->title_header);
13458 checked_free(music_file_info->artist_header);
13459 checked_free(music_file_info->album_header);
13460 checked_free(music_file_info->year_header);
13461 checked_free(music_file_info->played_header);
13463 checked_free(music_file_info->title);
13464 checked_free(music_file_info->artist);
13465 checked_free(music_file_info->album);
13466 checked_free(music_file_info->year);
13467 checked_free(music_file_info->played);
13469 free(music_file_info);
13471 music_file_info = next;
13474 new = &music_file_info;
13476 // get (configured or unconfigured) music file info for all levels
13477 for (i = leveldir_current->first_level;
13478 i <= leveldir_current->last_level; i++)
13482 if (levelset.music[i] != MUS_UNDEFINED)
13484 // get music file info for configured level music
13485 music_nr = levelset.music[i];
13487 else if (num_music_noconf > 0)
13489 // get music file info for unconfigured level music
13490 int level_pos = i - leveldir_current->first_level;
13492 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13499 char *basename = getMusicInfoEntryFilename(music_nr);
13501 if (basename == NULL)
13504 if (!music_info_listed(music_file_info, basename))
13506 *new = get_music_file_info(basename, music_nr);
13509 new = &(*new)->next;
13513 // get music file info for all remaining configured music files
13514 for (i = 0; i < num_music; i++)
13516 music = getMusicListEntry(i);
13518 if (music->filename == NULL)
13521 if (strEqual(music->filename, UNDEFINED_FILENAME))
13524 // a configured file may be not recognized as music
13525 if (!FileIsMusic(music->filename))
13528 if (!music_info_listed(music_file_info, music->filename))
13530 *new = get_music_file_info(music->filename, i);
13533 new = &(*new)->next;
13537 // get sound file info for all configured sound files
13538 for (i = 0; i < num_sounds; i++)
13540 sound = getSoundListEntry(i);
13542 if (sound->filename == NULL)
13545 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13548 // a configured file may be not recognized as sound
13549 if (!FileIsSound(sound->filename))
13552 if (!sound_info_listed(music_file_info, sound->filename))
13554 *new = get_sound_file_info(sound->filename, i);
13556 new = &(*new)->next;
13560 // add pointers to previous list nodes
13562 struct MusicFileInfo *node = music_file_info;
13564 while (node != NULL)
13567 node->next->prev = node;
13573 static void add_helpanim_entry(int element, int action, int direction,
13574 int delay, int *num_list_entries)
13576 struct HelpAnimInfo *new_list_entry;
13577 (*num_list_entries)++;
13580 checked_realloc(helpanim_info,
13581 *num_list_entries * sizeof(struct HelpAnimInfo));
13582 new_list_entry = &helpanim_info[*num_list_entries - 1];
13584 new_list_entry->element = element;
13585 new_list_entry->action = action;
13586 new_list_entry->direction = direction;
13587 new_list_entry->delay = delay;
13590 static void print_unknown_token(char *filename, char *token, int token_nr)
13595 Warn("unknown token(s) found in config file:");
13596 Warn("- config file: '%s'", filename);
13599 Warn("- token: '%s'", token);
13602 static void print_unknown_token_end(int token_nr)
13608 void LoadHelpAnimInfo(void)
13610 char *filename = getHelpAnimFilename();
13611 SetupFileList *setup_file_list = NULL, *list;
13612 SetupFileHash *element_hash, *action_hash, *direction_hash;
13613 int num_list_entries = 0;
13614 int num_unknown_tokens = 0;
13617 if (fileExists(filename))
13618 setup_file_list = loadSetupFileList(filename);
13620 if (setup_file_list == NULL)
13622 // use reliable default values from static configuration
13623 SetupFileList *insert_ptr;
13625 insert_ptr = setup_file_list =
13626 newSetupFileList(helpanim_config[0].token,
13627 helpanim_config[0].value);
13629 for (i = 1; helpanim_config[i].token; i++)
13630 insert_ptr = addListEntry(insert_ptr,
13631 helpanim_config[i].token,
13632 helpanim_config[i].value);
13635 element_hash = newSetupFileHash();
13636 action_hash = newSetupFileHash();
13637 direction_hash = newSetupFileHash();
13639 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13640 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13642 for (i = 0; i < NUM_ACTIONS; i++)
13643 setHashEntry(action_hash, element_action_info[i].suffix,
13644 i_to_a(element_action_info[i].value));
13646 // do not store direction index (bit) here, but direction value!
13647 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13648 setHashEntry(direction_hash, element_direction_info[i].suffix,
13649 i_to_a(1 << element_direction_info[i].value));
13651 for (list = setup_file_list; list != NULL; list = list->next)
13653 char *element_token, *action_token, *direction_token;
13654 char *element_value, *action_value, *direction_value;
13655 int delay = atoi(list->value);
13657 if (strEqual(list->token, "end"))
13659 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13664 /* first try to break element into element/action/direction parts;
13665 if this does not work, also accept combined "element[.act][.dir]"
13666 elements (like "dynamite.active"), which are unique elements */
13668 if (strchr(list->token, '.') == NULL) // token contains no '.'
13670 element_value = getHashEntry(element_hash, list->token);
13671 if (element_value != NULL) // element found
13672 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13673 &num_list_entries);
13676 // no further suffixes found -- this is not an element
13677 print_unknown_token(filename, list->token, num_unknown_tokens++);
13683 // token has format "<prefix>.<something>"
13685 action_token = strchr(list->token, '.'); // suffix may be action ...
13686 direction_token = action_token; // ... or direction
13688 element_token = getStringCopy(list->token);
13689 *strchr(element_token, '.') = '\0';
13691 element_value = getHashEntry(element_hash, element_token);
13693 if (element_value == NULL) // this is no element
13695 element_value = getHashEntry(element_hash, list->token);
13696 if (element_value != NULL) // combined element found
13697 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13698 &num_list_entries);
13700 print_unknown_token(filename, list->token, num_unknown_tokens++);
13702 free(element_token);
13707 action_value = getHashEntry(action_hash, action_token);
13709 if (action_value != NULL) // action found
13711 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13712 &num_list_entries);
13714 free(element_token);
13719 direction_value = getHashEntry(direction_hash, direction_token);
13721 if (direction_value != NULL) // direction found
13723 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13724 &num_list_entries);
13726 free(element_token);
13731 if (strchr(action_token + 1, '.') == NULL)
13733 // no further suffixes found -- this is not an action nor direction
13735 element_value = getHashEntry(element_hash, list->token);
13736 if (element_value != NULL) // combined element found
13737 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13738 &num_list_entries);
13740 print_unknown_token(filename, list->token, num_unknown_tokens++);
13742 free(element_token);
13747 // token has format "<prefix>.<suffix>.<something>"
13749 direction_token = strchr(action_token + 1, '.');
13751 action_token = getStringCopy(action_token);
13752 *strchr(action_token + 1, '.') = '\0';
13754 action_value = getHashEntry(action_hash, action_token);
13756 if (action_value == NULL) // this is no action
13758 element_value = getHashEntry(element_hash, list->token);
13759 if (element_value != NULL) // combined element found
13760 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13761 &num_list_entries);
13763 print_unknown_token(filename, list->token, num_unknown_tokens++);
13765 free(element_token);
13766 free(action_token);
13771 direction_value = getHashEntry(direction_hash, direction_token);
13773 if (direction_value != NULL) // direction found
13775 add_helpanim_entry(atoi(element_value), atoi(action_value),
13776 atoi(direction_value), delay, &num_list_entries);
13778 free(element_token);
13779 free(action_token);
13784 // this is no direction
13786 element_value = getHashEntry(element_hash, list->token);
13787 if (element_value != NULL) // combined element found
13788 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13789 &num_list_entries);
13791 print_unknown_token(filename, list->token, num_unknown_tokens++);
13793 free(element_token);
13794 free(action_token);
13797 print_unknown_token_end(num_unknown_tokens);
13799 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13800 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13802 freeSetupFileList(setup_file_list);
13803 freeSetupFileHash(element_hash);
13804 freeSetupFileHash(action_hash);
13805 freeSetupFileHash(direction_hash);
13808 for (i = 0; i < num_list_entries; i++)
13809 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13810 EL_NAME(helpanim_info[i].element),
13811 helpanim_info[i].element,
13812 helpanim_info[i].action,
13813 helpanim_info[i].direction,
13814 helpanim_info[i].delay);
13818 void LoadHelpTextInfo(void)
13820 char *filename = getHelpTextFilename();
13823 if (helptext_info != NULL)
13825 freeSetupFileHash(helptext_info);
13826 helptext_info = NULL;
13829 if (fileExists(filename))
13830 helptext_info = loadSetupFileHash(filename);
13832 if (helptext_info == NULL)
13834 // use reliable default values from static configuration
13835 helptext_info = newSetupFileHash();
13837 for (i = 0; helptext_config[i].token; i++)
13838 setHashEntry(helptext_info,
13839 helptext_config[i].token,
13840 helptext_config[i].value);
13844 BEGIN_HASH_ITERATION(helptext_info, itr)
13846 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13847 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13849 END_HASH_ITERATION(hash, itr)
13854 // ----------------------------------------------------------------------------
13856 // ----------------------------------------------------------------------------
13858 #define MAX_NUM_CONVERT_LEVELS 1000
13860 void ConvertLevels(void)
13862 static LevelDirTree *convert_leveldir = NULL;
13863 static int convert_level_nr = -1;
13864 static int num_levels_handled = 0;
13865 static int num_levels_converted = 0;
13866 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13869 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13870 global.convert_leveldir);
13872 if (convert_leveldir == NULL)
13873 Fail("no such level identifier: '%s'", global.convert_leveldir);
13875 leveldir_current = convert_leveldir;
13877 if (global.convert_level_nr != -1)
13879 convert_leveldir->first_level = global.convert_level_nr;
13880 convert_leveldir->last_level = global.convert_level_nr;
13883 convert_level_nr = convert_leveldir->first_level;
13885 PrintLine("=", 79);
13886 Print("Converting levels\n");
13887 PrintLine("-", 79);
13888 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13889 Print("Level series name: '%s'\n", convert_leveldir->name);
13890 Print("Level series author: '%s'\n", convert_leveldir->author);
13891 Print("Number of levels: %d\n", convert_leveldir->levels);
13892 PrintLine("=", 79);
13895 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13896 levels_failed[i] = FALSE;
13898 while (convert_level_nr <= convert_leveldir->last_level)
13900 char *level_filename;
13903 level_nr = convert_level_nr++;
13905 Print("Level %03d: ", level_nr);
13907 LoadLevel(level_nr);
13908 if (level.no_level_file || level.no_valid_file)
13910 Print("(no level)\n");
13914 Print("converting level ... ");
13917 // special case: conversion of some EMC levels as requested by ACME
13918 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13921 level_filename = getDefaultLevelFilename(level_nr);
13922 new_level = !fileExists(level_filename);
13926 SaveLevel(level_nr);
13928 num_levels_converted++;
13930 Print("converted.\n");
13934 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13935 levels_failed[level_nr] = TRUE;
13937 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13940 num_levels_handled++;
13944 PrintLine("=", 79);
13945 Print("Number of levels handled: %d\n", num_levels_handled);
13946 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13947 (num_levels_handled ?
13948 num_levels_converted * 100 / num_levels_handled : 0));
13949 PrintLine("-", 79);
13950 Print("Summary (for automatic parsing by scripts):\n");
13951 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13952 convert_leveldir->identifier, num_levels_converted,
13953 num_levels_handled,
13954 (num_levels_handled ?
13955 num_levels_converted * 100 / num_levels_handled : 0));
13957 if (num_levels_handled != num_levels_converted)
13959 Print(", FAILED:");
13960 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13961 if (levels_failed[i])
13966 PrintLine("=", 79);
13968 CloseAllAndExit(0);
13972 // ----------------------------------------------------------------------------
13973 // create and save images for use in level sketches (raw BMP format)
13974 // ----------------------------------------------------------------------------
13976 void CreateLevelSketchImages(void)
13982 InitElementPropertiesGfxElement();
13984 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13985 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13987 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13989 int element = getMappedElement(i);
13990 char basename1[16];
13991 char basename2[16];
13995 sprintf(basename1, "%04d.bmp", i);
13996 sprintf(basename2, "%04ds.bmp", i);
13998 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13999 filename2 = getPath2(global.create_sketch_images_dir, basename2);
14001 DrawSizedElement(0, 0, element, TILESIZE);
14002 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14004 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14005 Fail("cannot save level sketch image file '%s'", filename1);
14007 DrawSizedElement(0, 0, element, MINI_TILESIZE);
14008 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14010 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14011 Fail("cannot save level sketch image file '%s'", filename2);
14016 // create corresponding SQL statements (for normal and small images)
14019 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14020 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14023 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14024 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14026 // optional: create content for forum level sketch demonstration post
14028 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14031 FreeBitmap(bitmap1);
14032 FreeBitmap(bitmap2);
14035 fprintf(stderr, "\n");
14037 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14039 CloseAllAndExit(0);
14043 // ----------------------------------------------------------------------------
14044 // create and save images for element collecting animations (raw BMP format)
14045 // ----------------------------------------------------------------------------
14047 static boolean createCollectImage(int element)
14049 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14052 void CreateCollectElementImages(void)
14056 int anim_frames = num_steps - 1;
14057 int tile_size = TILESIZE;
14058 int anim_width = tile_size * anim_frames;
14059 int anim_height = tile_size;
14060 int num_collect_images = 0;
14061 int pos_collect_images = 0;
14063 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14064 if (createCollectImage(i))
14065 num_collect_images++;
14067 Info("Creating %d element collecting animation images ...",
14068 num_collect_images);
14070 int dst_width = anim_width * 2;
14071 int dst_height = anim_height * num_collect_images / 2;
14072 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14073 char *basename_bmp = "RocksCollect.bmp";
14074 char *basename_png = "RocksCollect.png";
14075 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14076 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14077 int len_filename_bmp = strlen(filename_bmp);
14078 int len_filename_png = strlen(filename_png);
14079 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14080 char cmd_convert[max_command_len];
14082 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14086 // force using RGBA surface for destination bitmap
14087 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14088 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14090 dst_bitmap->surface =
14091 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14093 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14095 if (!createCollectImage(i))
14098 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14099 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14100 int graphic = el2img(i);
14101 char *token_name = element_info[i].token_name;
14102 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14103 Bitmap *src_bitmap;
14106 Info("- creating collecting image for '%s' ...", token_name);
14108 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14110 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14111 tile_size, tile_size, 0, 0);
14113 // force using RGBA surface for temporary bitmap (using transparent black)
14114 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14115 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14117 tmp_bitmap->surface =
14118 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14120 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14122 for (j = 0; j < anim_frames; j++)
14124 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14125 int frame_size = frame_size_final * num_steps;
14126 int offset = (tile_size - frame_size_final) / 2;
14127 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14129 while (frame_size > frame_size_final)
14133 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14135 FreeBitmap(frame_bitmap);
14137 frame_bitmap = half_bitmap;
14140 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14141 frame_size_final, frame_size_final,
14142 dst_x + j * tile_size + offset, dst_y + offset);
14144 FreeBitmap(frame_bitmap);
14147 tmp_bitmap->surface_masked = NULL;
14149 FreeBitmap(tmp_bitmap);
14151 pos_collect_images++;
14154 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14155 Fail("cannot save element collecting image file '%s'", filename_bmp);
14157 FreeBitmap(dst_bitmap);
14159 Info("Converting image file from BMP to PNG ...");
14161 if (system(cmd_convert) != 0)
14162 Fail("converting image file failed");
14164 unlink(filename_bmp);
14168 CloseAllAndExit(0);
14172 // ----------------------------------------------------------------------------
14173 // create and save images for custom and group elements (raw BMP format)
14174 // ----------------------------------------------------------------------------
14176 void CreateCustomElementImages(char *directory)
14178 char *src_basename = "RocksCE-template.ilbm";
14179 char *dst_basename = "RocksCE.bmp";
14180 char *src_filename = getPath2(directory, src_basename);
14181 char *dst_filename = getPath2(directory, dst_basename);
14182 Bitmap *src_bitmap;
14184 int yoffset_ce = 0;
14185 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14188 InitVideoDefaults();
14190 ReCreateBitmap(&backbuffer, video.width, video.height);
14192 src_bitmap = LoadImage(src_filename);
14194 bitmap = CreateBitmap(TILEX * 16 * 2,
14195 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14198 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14205 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14206 TILEX * x, TILEY * y + yoffset_ce);
14208 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14210 TILEX * x + TILEX * 16,
14211 TILEY * y + yoffset_ce);
14213 for (j = 2; j >= 0; j--)
14217 BlitBitmap(src_bitmap, bitmap,
14218 TILEX + c * 7, 0, 6, 10,
14219 TILEX * x + 6 + j * 7,
14220 TILEY * y + 11 + yoffset_ce);
14222 BlitBitmap(src_bitmap, bitmap,
14223 TILEX + c * 8, TILEY, 6, 10,
14224 TILEX * 16 + TILEX * x + 6 + j * 8,
14225 TILEY * y + 10 + yoffset_ce);
14231 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14238 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14239 TILEX * x, TILEY * y + yoffset_ge);
14241 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14243 TILEX * x + TILEX * 16,
14244 TILEY * y + yoffset_ge);
14246 for (j = 1; j >= 0; j--)
14250 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14251 TILEX * x + 6 + j * 10,
14252 TILEY * y + 11 + yoffset_ge);
14254 BlitBitmap(src_bitmap, bitmap,
14255 TILEX + c * 8, TILEY + 12, 6, 10,
14256 TILEX * 16 + TILEX * x + 10 + j * 8,
14257 TILEY * y + 10 + yoffset_ge);
14263 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14264 Fail("cannot save CE graphics file '%s'", dst_filename);
14266 FreeBitmap(bitmap);
14268 CloseAllAndExit(0);