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
313 static struct LevelFileConfigInfo chunk_config_ELEM[] =
315 // (these values are the same for each player)
318 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
319 &li.block_last_field, FALSE // default case for EM levels
323 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
324 &li.sp_block_last_field, TRUE // default case for SP levels
328 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
329 &li.instant_relocation, FALSE
333 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
334 &li.can_pass_to_walkable, FALSE
338 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
339 &li.block_snap_field, TRUE
343 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
344 &li.continuous_snapping, TRUE
348 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
349 &li.shifted_relocation, FALSE
353 TYPE_BOOLEAN, CONF_VALUE_8_BIT(15),
354 &li.lazy_relocation, FALSE
358 TYPE_BOOLEAN, CONF_VALUE_8_BIT(16),
359 &li.finish_dig_collect, TRUE
363 TYPE_BOOLEAN, CONF_VALUE_8_BIT(17),
364 &li.keep_walkable_ce, FALSE
367 // (these values are different for each player)
370 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
371 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
375 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
376 &li.initial_player_gravity[0], FALSE
380 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
381 &li.use_start_element[0], FALSE
385 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
386 &li.start_element[0], EL_PLAYER_1
390 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
391 &li.use_artwork_element[0], FALSE
395 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
396 &li.artwork_element[0], EL_PLAYER_1
400 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
401 &li.use_explosion_element[0], FALSE
405 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
406 &li.explosion_element[0], EL_PLAYER_1
410 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
411 &li.use_initial_inventory[0], FALSE
415 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
416 &li.initial_inventory_size[0], 1
420 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
421 &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
422 &li.initial_inventory_size[0], 1, MAX_INITIAL_INVENTORY_SIZE
427 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
428 &li.initial_player_stepsize[1], STEPSIZE_NORMAL
432 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
433 &li.initial_player_gravity[1], FALSE
437 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
438 &li.use_start_element[1], FALSE
442 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
443 &li.start_element[1], EL_PLAYER_2
447 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
448 &li.use_artwork_element[1], FALSE
452 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
453 &li.artwork_element[1], EL_PLAYER_2
457 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
458 &li.use_explosion_element[1], FALSE
462 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
463 &li.explosion_element[1], EL_PLAYER_2
467 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
468 &li.use_initial_inventory[1], FALSE
472 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
473 &li.initial_inventory_size[1], 1
477 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
478 &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
479 &li.initial_inventory_size[1], 1, MAX_INITIAL_INVENTORY_SIZE
484 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
485 &li.initial_player_stepsize[2], STEPSIZE_NORMAL
489 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
490 &li.initial_player_gravity[2], FALSE
494 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
495 &li.use_start_element[2], FALSE
499 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
500 &li.start_element[2], EL_PLAYER_3
504 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
505 &li.use_artwork_element[2], FALSE
509 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
510 &li.artwork_element[2], EL_PLAYER_3
514 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
515 &li.use_explosion_element[2], FALSE
519 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
520 &li.explosion_element[2], EL_PLAYER_3
524 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
525 &li.use_initial_inventory[2], FALSE
529 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
530 &li.initial_inventory_size[2], 1
534 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
535 &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
536 &li.initial_inventory_size[2], 1, MAX_INITIAL_INVENTORY_SIZE
541 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
542 &li.initial_player_stepsize[3], STEPSIZE_NORMAL
546 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
547 &li.initial_player_gravity[3], FALSE
551 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
552 &li.use_start_element[3], FALSE
556 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
557 &li.start_element[3], EL_PLAYER_4
561 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
562 &li.use_artwork_element[3], FALSE
566 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
567 &li.artwork_element[3], EL_PLAYER_4
571 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
572 &li.use_explosion_element[3], FALSE
576 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
577 &li.explosion_element[3], EL_PLAYER_4
581 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
582 &li.use_initial_inventory[3], FALSE
586 TYPE_BOOLEAN, CONF_VALUE_8_BIT(14),
587 &li.initial_inventory_size[3], 1
591 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
592 &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
593 &li.initial_inventory_size[3], 1, MAX_INITIAL_INVENTORY_SIZE
596 // (these values are only valid for BD style levels)
599 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
600 &li.bd_diagonal_movements, FALSE
605 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
606 &li.score[SC_DIAMOND_EXTRA], 20
609 // (the following values are related to various game elements)
613 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
614 &li.score[SC_EMERALD], 10
619 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
620 &li.score[SC_DIAMOND], 10
625 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
626 &li.score[SC_BUG], 10
631 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
632 &li.score[SC_SPACESHIP], 10
637 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
638 &li.score[SC_PACMAN], 10
643 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
644 &li.score[SC_NUT], 10
649 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
650 &li.score[SC_DYNAMITE], 10
655 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
656 &li.score[SC_KEY], 10
661 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
662 &li.score[SC_PEARL], 10
667 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
668 &li.score[SC_CRYSTAL], 10
673 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
674 &li.amoeba_content, EL_DIAMOND
678 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
683 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
684 &li.grow_into_diggable, TRUE
689 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
690 &li.yamyam_content, EL_ROCK, NULL,
691 &li.num_yamyam_contents, 4, MAX_ELEMENT_CONTENTS
695 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
696 &li.score[SC_YAMYAM], 10
701 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
702 &li.score[SC_ROBOT], 10
706 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
712 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
718 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
719 &li.time_magic_wall, 10
724 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
725 &li.game_of_life[0], 2
729 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
730 &li.game_of_life[1], 3
734 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
735 &li.game_of_life[2], 3
739 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
740 &li.game_of_life[3], 3
744 TYPE_BOOLEAN, CONF_VALUE_8_BIT(5),
745 &li.use_life_bugs, FALSE
750 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
755 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
760 TYPE_INTEGER, CONF_VALUE_8_BIT(3),
765 TYPE_INTEGER, CONF_VALUE_8_BIT(4),
770 EL_TIMEGATE_SWITCH, -1,
771 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
772 &li.time_timegate, 10
776 EL_LIGHT_SWITCH_ACTIVE, -1,
777 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
782 EL_SHIELD_NORMAL, -1,
783 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
784 &li.shield_normal_time, 10
787 EL_SHIELD_NORMAL, -1,
788 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
789 &li.score[SC_SHIELD], 10
793 EL_SHIELD_DEADLY, -1,
794 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
795 &li.shield_deadly_time, 10
798 EL_SHIELD_DEADLY, -1,
799 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
800 &li.score[SC_SHIELD], 10
805 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
810 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
811 &li.extra_time_score, 10
815 EL_TIME_ORB_FULL, -1,
816 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
817 &li.time_orb_time, 10
820 EL_TIME_ORB_FULL, -1,
821 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
822 &li.use_time_orb_bug, FALSE
827 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
828 &li.use_spring_bug, FALSE
833 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
834 &li.android_move_time, 10
838 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
839 &li.android_clone_time, 10
842 EL_EMC_ANDROID, SAVE_CONF_NEVER,
843 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
844 &li.android_clone_element[0], EL_EMPTY, NULL,
845 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS_OLD
849 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
850 &li.android_clone_element[0], EL_EMPTY, NULL,
851 &li.num_android_clone_elements, 1, MAX_ANDROID_ELEMENTS
856 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
861 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
866 EL_EMC_MAGNIFIER, -1,
867 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
868 &li.magnify_score, 10
871 EL_EMC_MAGNIFIER, -1,
872 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
877 EL_EMC_MAGIC_BALL, -1,
878 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
882 EL_EMC_MAGIC_BALL, -1,
883 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
884 &li.ball_random, FALSE
887 EL_EMC_MAGIC_BALL, -1,
888 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
889 &li.ball_active_initial, FALSE
892 EL_EMC_MAGIC_BALL, -1,
893 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
894 &li.ball_content, EL_EMPTY, NULL,
895 &li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
899 EL_SOKOBAN_FIELD_EMPTY, -1,
900 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
901 &li.sb_fields_needed, TRUE
905 EL_SOKOBAN_OBJECT, -1,
906 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
907 &li.sb_objects_needed, TRUE
912 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
913 &li.mm_laser_red, FALSE
917 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
918 &li.mm_laser_green, FALSE
922 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
923 &li.mm_laser_blue, TRUE
928 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
929 &li.df_laser_red, TRUE
933 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
934 &li.df_laser_green, TRUE
938 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
939 &li.df_laser_blue, FALSE
943 EL_MM_FUSE_ACTIVE, -1,
944 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
949 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
955 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
960 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
961 &li.mm_ball_choice_mode, ANIM_RANDOM
965 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(1),
966 &li.mm_ball_content, EL_EMPTY, NULL,
967 &li.num_mm_ball_contents, 8, MAX_MM_BALL_CONTENTS
971 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
972 &li.rotate_mm_ball_content, TRUE
976 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
977 &li.explode_mm_ball, FALSE
981 EL_MM_STEEL_BLOCK, -1,
982 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
983 &li.mm_time_block, 75
987 TYPE_INTEGER, CONF_VALUE_16_BIT(1),
988 &li.score[SC_ELEM_BONUS], 10
998 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1002 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1003 &xx_envelope.xsize, MAX_ENVELOPE_XSIZE,
1007 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1008 &xx_envelope.ysize, MAX_ENVELOPE_YSIZE,
1013 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1014 &xx_envelope.autowrap, FALSE
1018 TYPE_BOOLEAN, CONF_VALUE_8_BIT(4),
1019 &xx_envelope.centered, FALSE
1024 TYPE_STRING, CONF_VALUE_BYTES(1),
1025 &xx_envelope.text, -1, NULL,
1026 &xx_string_length_unused, -1, MAX_ENVELOPE_TEXT_LEN,
1027 &xx_default_string_empty[0]
1037 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1041 TYPE_STRING, CONF_VALUE_BYTES(1),
1042 &xx_ei.description[0], -1,
1043 &yy_ei.description[0],
1044 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1045 &xx_default_description[0]
1050 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1051 &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1052 &yy_ei.properties[EP_BITFIELD_BASE_NR]
1054 #if ENABLE_RESERVED_CODE
1055 // (reserved for later use)
1058 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1059 &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1060 &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1066 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1067 &xx_ei.use_gfx_element, FALSE,
1068 &yy_ei.use_gfx_element
1072 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1073 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE,
1074 &yy_ei.gfx_element_initial
1079 TYPE_BITFIELD, CONF_VALUE_8_BIT(2),
1080 &xx_ei.access_direction, MV_ALL_DIRECTIONS,
1081 &yy_ei.access_direction
1086 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1087 &xx_ei.collect_score_initial, 10,
1088 &yy_ei.collect_score_initial
1092 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1093 &xx_ei.collect_count_initial, 1,
1094 &yy_ei.collect_count_initial
1099 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1100 &xx_ei.ce_value_fixed_initial, 0,
1101 &yy_ei.ce_value_fixed_initial
1105 TYPE_INTEGER, CONF_VALUE_16_BIT(5),
1106 &xx_ei.ce_value_random_initial, 0,
1107 &yy_ei.ce_value_random_initial
1111 TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
1112 &xx_ei.use_last_ce_value, FALSE,
1113 &yy_ei.use_last_ce_value
1118 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1119 &xx_ei.push_delay_fixed, 8,
1120 &yy_ei.push_delay_fixed
1124 TYPE_INTEGER, CONF_VALUE_16_BIT(7),
1125 &xx_ei.push_delay_random, 8,
1126 &yy_ei.push_delay_random
1130 TYPE_INTEGER, CONF_VALUE_16_BIT(8),
1131 &xx_ei.drop_delay_fixed, 0,
1132 &yy_ei.drop_delay_fixed
1136 TYPE_INTEGER, CONF_VALUE_16_BIT(9),
1137 &xx_ei.drop_delay_random, 0,
1138 &yy_ei.drop_delay_random
1142 TYPE_INTEGER, CONF_VALUE_16_BIT(10),
1143 &xx_ei.move_delay_fixed, 0,
1144 &yy_ei.move_delay_fixed
1148 TYPE_INTEGER, CONF_VALUE_16_BIT(11),
1149 &xx_ei.move_delay_random, 0,
1150 &yy_ei.move_delay_random
1154 TYPE_INTEGER, CONF_VALUE_16_BIT(16),
1155 &xx_ei.step_delay_fixed, 0,
1156 &yy_ei.step_delay_fixed
1160 TYPE_INTEGER, CONF_VALUE_16_BIT(17),
1161 &xx_ei.step_delay_random, 0,
1162 &yy_ei.step_delay_random
1167 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1168 &xx_ei.move_pattern, MV_ALL_DIRECTIONS,
1173 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1174 &xx_ei.move_direction_initial, MV_START_AUTOMATIC,
1175 &yy_ei.move_direction_initial
1179 TYPE_INTEGER, CONF_VALUE_8_BIT(5),
1180 &xx_ei.move_stepsize, TILEX / 8,
1181 &yy_ei.move_stepsize
1186 TYPE_ELEMENT, CONF_VALUE_16_BIT(12),
1187 &xx_ei.move_enter_element, EL_EMPTY_SPACE,
1188 &yy_ei.move_enter_element
1192 TYPE_ELEMENT, CONF_VALUE_16_BIT(13),
1193 &xx_ei.move_leave_element, EL_EMPTY_SPACE,
1194 &yy_ei.move_leave_element
1198 TYPE_INTEGER, CONF_VALUE_8_BIT(6),
1199 &xx_ei.move_leave_type, LEAVE_TYPE_UNLIMITED,
1200 &yy_ei.move_leave_type
1205 TYPE_INTEGER, CONF_VALUE_8_BIT(7),
1206 &xx_ei.slippery_type, SLIPPERY_ANY_RANDOM,
1207 &yy_ei.slippery_type
1212 TYPE_INTEGER, CONF_VALUE_8_BIT(8),
1213 &xx_ei.explosion_type, EXPLODES_3X3,
1214 &yy_ei.explosion_type
1218 TYPE_INTEGER, CONF_VALUE_16_BIT(14),
1219 &xx_ei.explosion_delay, 16,
1220 &yy_ei.explosion_delay
1224 TYPE_INTEGER, CONF_VALUE_16_BIT(15),
1225 &xx_ei.ignition_delay, 8,
1226 &yy_ei.ignition_delay
1231 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(2),
1232 &xx_ei.content, EL_EMPTY_SPACE,
1234 &xx_num_contents, 1, 1
1237 // ---------- "num_change_pages" must be the last entry ---------------------
1240 -1, SAVE_CONF_ALWAYS,
1241 TYPE_INTEGER, CONF_VALUE_8_BIT(9),
1242 &xx_ei.num_change_pages, 1,
1243 &yy_ei.num_change_pages
1254 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1256 // ---------- "current_change_page" must be the first entry -----------------
1259 -1, SAVE_CONF_ALWAYS,
1260 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1261 &xx_current_change_page, -1
1264 // ---------- (the remaining entries can be in any order) -------------------
1268 TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
1269 &xx_change.can_change, FALSE
1274 TYPE_BITFIELD, CONF_VALUE_32_BIT(1),
1275 &xx_event_bits[0], 0
1279 TYPE_BITFIELD, CONF_VALUE_32_BIT(2),
1280 &xx_event_bits[1], 0
1285 TYPE_BITFIELD, CONF_VALUE_8_BIT(3),
1286 &xx_change.trigger_player, CH_PLAYER_ANY
1290 TYPE_BITFIELD, CONF_VALUE_8_BIT(4),
1291 &xx_change.trigger_side, CH_SIDE_ANY
1295 TYPE_BITFIELD, CONF_VALUE_32_BIT(3),
1296 &xx_change.trigger_page, CH_PAGE_ANY
1301 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1302 &xx_change.target_element, EL_EMPTY_SPACE
1307 TYPE_INTEGER, CONF_VALUE_16_BIT(2),
1308 &xx_change.delay_fixed, 0
1312 TYPE_INTEGER, CONF_VALUE_16_BIT(3),
1313 &xx_change.delay_random, 0
1317 TYPE_INTEGER, CONF_VALUE_16_BIT(4),
1318 &xx_change.delay_frames, FRAMES_PER_SECOND
1323 TYPE_ELEMENT, CONF_VALUE_16_BIT(5),
1324 &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1329 TYPE_BOOLEAN, CONF_VALUE_8_BIT(6),
1330 &xx_change.explode, FALSE
1334 TYPE_BOOLEAN, CONF_VALUE_8_BIT(7),
1335 &xx_change.use_target_content, FALSE
1339 TYPE_BOOLEAN, CONF_VALUE_8_BIT(8),
1340 &xx_change.only_if_complete, FALSE
1344 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1345 &xx_change.use_random_replace, FALSE
1349 TYPE_INTEGER, CONF_VALUE_8_BIT(10),
1350 &xx_change.random_percentage, 100
1354 TYPE_INTEGER, CONF_VALUE_8_BIT(11),
1355 &xx_change.replace_when, CP_WHEN_EMPTY
1360 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1361 &xx_change.has_action, FALSE
1365 TYPE_INTEGER, CONF_VALUE_8_BIT(13),
1366 &xx_change.action_type, CA_NO_ACTION
1370 TYPE_INTEGER, CONF_VALUE_8_BIT(14),
1371 &xx_change.action_mode, CA_MODE_UNDEFINED
1375 TYPE_INTEGER, CONF_VALUE_16_BIT(6),
1376 &xx_change.action_arg, CA_ARG_UNDEFINED
1381 TYPE_ELEMENT, CONF_VALUE_16_BIT(7),
1382 &xx_change.action_element, EL_EMPTY_SPACE
1387 TYPE_CONTENT_LIST, CONF_VALUE_BYTES(1),
1388 &xx_change.target_content, EL_EMPTY_SPACE, NULL,
1389 &xx_num_contents, 1, 1
1399 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1403 TYPE_STRING, CONF_VALUE_BYTES(1),
1404 &xx_ei.description[0], -1, NULL,
1405 &xx_string_length_unused, -1, MAX_ELEMENT_NAME_LEN,
1406 &xx_default_description[0]
1411 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1412 &xx_ei.use_gfx_element, FALSE
1416 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1417 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1422 TYPE_INTEGER, CONF_VALUE_8_BIT(2),
1423 &xx_group.choice_mode, ANIM_RANDOM
1428 TYPE_ELEMENT_LIST, CONF_VALUE_BYTES(2),
1429 &xx_group.element[0], EL_EMPTY_SPACE, NULL,
1430 &xx_group.num_elements, 1, MAX_ELEMENTS_IN_GROUP
1440 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1444 TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
1445 &xx_ei.use_gfx_element, FALSE
1449 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1450 &xx_ei.gfx_element_initial, EL_EMPTY_SPACE
1460 static struct LevelFileConfigInfo chunk_config_CONF[] = // (OBSOLETE)
1464 TYPE_BOOLEAN, CONF_VALUE_8_BIT(9),
1465 &li.block_snap_field, TRUE
1469 TYPE_BOOLEAN, CONF_VALUE_8_BIT(13),
1470 &li.continuous_snapping, TRUE
1474 TYPE_INTEGER, CONF_VALUE_8_BIT(1),
1475 &li.initial_player_stepsize[0], STEPSIZE_NORMAL
1479 TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
1480 &li.use_start_element[0], FALSE
1484 TYPE_ELEMENT, CONF_VALUE_16_BIT(1),
1485 &li.start_element[0], EL_PLAYER_1
1489 TYPE_BOOLEAN, CONF_VALUE_8_BIT(11),
1490 &li.use_artwork_element[0], FALSE
1494 TYPE_ELEMENT, CONF_VALUE_16_BIT(2),
1495 &li.artwork_element[0], EL_PLAYER_1
1499 TYPE_BOOLEAN, CONF_VALUE_8_BIT(12),
1500 &li.use_explosion_element[0], FALSE
1504 TYPE_ELEMENT, CONF_VALUE_16_BIT(3),
1505 &li.explosion_element[0], EL_PLAYER_1
1520 filetype_id_list[] =
1522 { LEVEL_FILE_TYPE_RND, "RND" },
1523 { LEVEL_FILE_TYPE_BD, "BD" },
1524 { LEVEL_FILE_TYPE_EM, "EM" },
1525 { LEVEL_FILE_TYPE_SP, "SP" },
1526 { LEVEL_FILE_TYPE_DX, "DX" },
1527 { LEVEL_FILE_TYPE_SB, "SB" },
1528 { LEVEL_FILE_TYPE_DC, "DC" },
1529 { LEVEL_FILE_TYPE_MM, "MM" },
1530 { LEVEL_FILE_TYPE_MM, "DF" },
1535 // ============================================================================
1536 // level file functions
1537 // ============================================================================
1539 static boolean check_special_flags(char *flag)
1541 if (strEqual(options.special_flags, flag) ||
1542 strEqual(leveldir_current->special_flags, flag))
1548 static struct DateInfo getCurrentDate(void)
1550 time_t epoch_seconds = time(NULL);
1551 struct tm *now = localtime(&epoch_seconds);
1552 struct DateInfo date;
1554 date.year = now->tm_year + 1900;
1555 date.month = now->tm_mon + 1;
1556 date.day = now->tm_mday;
1558 date.src = DATE_SRC_CLOCK;
1563 static void resetEventFlags(struct ElementChangeInfo *change)
1567 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1568 change->has_event[i] = FALSE;
1571 static void resetEventBits(void)
1575 for (i = 0; i < NUM_CE_BITFIELDS; i++)
1576 xx_event_bits[i] = 0;
1579 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1583 /* important: only change event flag if corresponding event bit is set
1584 (this is because all xx_event_bits[] values are loaded separately,
1585 and all xx_event_bits[] values are set back to zero before loading
1586 another value xx_event_bits[x] (each value representing 32 flags)) */
1588 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1589 if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1590 change->has_event[i] = TRUE;
1593 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1597 /* in contrast to the above function setEventFlagsFromEventBits(), it
1598 would also be possible to set all bits in xx_event_bits[] to 0 or 1
1599 depending on the corresponding change->has_event[i] values here, as
1600 all xx_event_bits[] values are reset in resetEventBits() before */
1602 for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1603 if (change->has_event[i])
1604 xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1607 static char *getDefaultElementDescription(struct ElementInfo *ei)
1609 static char description[MAX_ELEMENT_NAME_LEN + 1];
1610 char *default_description = (ei->custom_description != NULL ?
1611 ei->custom_description :
1612 ei->editor_description);
1615 // always start with reliable default values
1616 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1617 description[i] = '\0';
1619 // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1620 strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1622 return &description[0];
1625 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1627 char *default_description = getDefaultElementDescription(ei);
1630 for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1631 ei->description[i] = default_description[i];
1634 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1638 for (i = 0; conf[i].data_type != -1; i++)
1640 int default_value = conf[i].default_value;
1641 int data_type = conf[i].data_type;
1642 int conf_type = conf[i].conf_type;
1643 int byte_mask = conf_type & CONF_MASK_BYTES;
1645 if (byte_mask == CONF_MASK_MULTI_BYTES)
1647 int default_num_entities = conf[i].default_num_entities;
1648 int max_num_entities = conf[i].max_num_entities;
1650 *(int *)(conf[i].num_entities) = default_num_entities;
1652 if (data_type == TYPE_STRING)
1654 char *default_string = conf[i].default_string;
1655 char *string = (char *)(conf[i].value);
1657 strncpy(string, default_string, max_num_entities);
1659 else if (data_type == TYPE_ELEMENT_LIST)
1661 int *element_array = (int *)(conf[i].value);
1664 for (j = 0; j < max_num_entities; j++)
1665 element_array[j] = default_value;
1667 else if (data_type == TYPE_CONTENT_LIST)
1669 struct Content *content = (struct Content *)(conf[i].value);
1672 for (c = 0; c < max_num_entities; c++)
1673 for (y = 0; y < 3; y++)
1674 for (x = 0; x < 3; x++)
1675 content[c].e[x][y] = default_value;
1678 else // constant size configuration data (1, 2 or 4 bytes)
1680 if (data_type == TYPE_BOOLEAN)
1681 *(boolean *)(conf[i].value) = default_value;
1683 *(int *) (conf[i].value) = default_value;
1688 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1692 for (i = 0; conf[i].data_type != -1; i++)
1694 int data_type = conf[i].data_type;
1695 int conf_type = conf[i].conf_type;
1696 int byte_mask = conf_type & CONF_MASK_BYTES;
1698 if (byte_mask == CONF_MASK_MULTI_BYTES)
1700 int max_num_entities = conf[i].max_num_entities;
1702 if (data_type == TYPE_STRING)
1704 char *string = (char *)(conf[i].value);
1705 char *string_copy = (char *)(conf[i].value_copy);
1707 strncpy(string_copy, string, max_num_entities);
1709 else if (data_type == TYPE_ELEMENT_LIST)
1711 int *element_array = (int *)(conf[i].value);
1712 int *element_array_copy = (int *)(conf[i].value_copy);
1715 for (j = 0; j < max_num_entities; j++)
1716 element_array_copy[j] = element_array[j];
1718 else if (data_type == TYPE_CONTENT_LIST)
1720 struct Content *content = (struct Content *)(conf[i].value);
1721 struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1724 for (c = 0; c < max_num_entities; c++)
1725 for (y = 0; y < 3; y++)
1726 for (x = 0; x < 3; x++)
1727 content_copy[c].e[x][y] = content[c].e[x][y];
1730 else // constant size configuration data (1, 2 or 4 bytes)
1732 if (data_type == TYPE_BOOLEAN)
1733 *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1735 *(int *) (conf[i].value_copy) = *(int *) (conf[i].value);
1740 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1744 xx_ei = *ei_from; // copy element data into temporary buffer
1745 yy_ei = *ei_to; // copy element data into temporary buffer
1747 copyConfigFromConfigList(chunk_config_CUSX_base);
1752 // ---------- reinitialize and copy change pages ----------
1754 ei_to->num_change_pages = ei_from->num_change_pages;
1755 ei_to->current_change_page = ei_from->current_change_page;
1757 setElementChangePages(ei_to, ei_to->num_change_pages);
1759 for (i = 0; i < ei_to->num_change_pages; i++)
1760 ei_to->change_page[i] = ei_from->change_page[i];
1762 // ---------- copy group element info ----------
1763 if (ei_from->group != NULL && ei_to->group != NULL) // group or internal
1764 *ei_to->group = *ei_from->group;
1766 // mark this custom element as modified
1767 ei_to->modified_settings = TRUE;
1770 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1772 int change_page_size = sizeof(struct ElementChangeInfo);
1774 ei->num_change_pages = MAX(1, change_pages);
1777 checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1779 if (ei->current_change_page >= ei->num_change_pages)
1780 ei->current_change_page = ei->num_change_pages - 1;
1782 ei->change = &ei->change_page[ei->current_change_page];
1785 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1787 xx_change = *change; // copy change data into temporary buffer
1789 setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1791 *change = xx_change;
1793 resetEventFlags(change);
1795 change->direct_action = 0;
1796 change->other_action = 0;
1798 change->pre_change_function = NULL;
1799 change->change_function = NULL;
1800 change->post_change_function = NULL;
1803 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1807 li = *level; // copy level data into temporary buffer
1808 setConfigToDefaultsFromConfigList(chunk_config_INFO);
1809 *level = li; // copy temporary buffer back to level data
1811 setLevelInfoToDefaults_BD();
1812 setLevelInfoToDefaults_EM();
1813 setLevelInfoToDefaults_SP();
1814 setLevelInfoToDefaults_MM();
1816 level->native_bd_level = &native_bd_level;
1817 level->native_em_level = &native_em_level;
1818 level->native_sp_level = &native_sp_level;
1819 level->native_mm_level = &native_mm_level;
1821 level->file_version = FILE_VERSION_ACTUAL;
1822 level->game_version = GAME_VERSION_ACTUAL;
1824 level->creation_date = getCurrentDate();
1826 level->encoding_16bit_field = TRUE;
1827 level->encoding_16bit_yamyam = TRUE;
1828 level->encoding_16bit_amoeba = TRUE;
1830 // clear level name and level author string buffers
1831 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1832 level->name[i] = '\0';
1833 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1834 level->author[i] = '\0';
1836 // set level name and level author to default values
1837 strcpy(level->name, NAMELESS_LEVEL_NAME);
1838 strcpy(level->author, ANONYMOUS_NAME);
1840 // set level playfield to playable default level with player and exit
1841 for (x = 0; x < MAX_LEV_FIELDX; x++)
1842 for (y = 0; y < MAX_LEV_FIELDY; y++)
1843 level->field[x][y] = EL_SAND;
1845 level->field[0][0] = EL_PLAYER_1;
1846 level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1848 BorderElement = EL_STEELWALL;
1850 // detect custom elements when loading them
1851 level->file_has_custom_elements = FALSE;
1853 // set all bug compatibility flags to "false" => do not emulate this bug
1854 level->use_action_after_change_bug = FALSE;
1856 if (leveldir_current)
1858 // try to determine better author name than 'anonymous'
1859 if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1861 strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1862 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1866 switch (LEVELCLASS(leveldir_current))
1868 case LEVELCLASS_TUTORIAL:
1869 strcpy(level->author, PROGRAM_AUTHOR_STRING);
1872 case LEVELCLASS_CONTRIB:
1873 strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1874 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1877 case LEVELCLASS_PRIVATE:
1878 strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1879 level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1883 // keep default value
1890 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1892 static boolean clipboard_elements_initialized = FALSE;
1895 InitElementPropertiesStatic();
1897 li = *level; // copy level data into temporary buffer
1898 setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1899 *level = li; // copy temporary buffer back to level data
1901 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1904 struct ElementInfo *ei = &element_info[element];
1906 if (element == EL_MM_GRAY_BALL)
1908 struct LevelInfo_MM *level_mm = level->native_mm_level;
1911 for (j = 0; j < level->num_mm_ball_contents; j++)
1912 level->mm_ball_content[j] =
1913 map_element_MM_to_RND(level_mm->ball_content[j]);
1916 // never initialize clipboard elements after the very first time
1917 // (to be able to use clipboard elements between several levels)
1918 if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1921 if (IS_ENVELOPE(element))
1923 int envelope_nr = element - EL_ENVELOPE_1;
1925 setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1927 level->envelope[envelope_nr] = xx_envelope;
1930 if (IS_CUSTOM_ELEMENT(element) ||
1931 IS_GROUP_ELEMENT(element) ||
1932 IS_INTERNAL_ELEMENT(element))
1934 xx_ei = *ei; // copy element data into temporary buffer
1936 setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1941 setElementChangePages(ei, 1);
1942 setElementChangeInfoToDefaults(ei->change);
1944 if (IS_CUSTOM_ELEMENT(element) ||
1945 IS_GROUP_ELEMENT(element))
1947 setElementDescriptionToDefault(ei);
1949 ei->modified_settings = FALSE;
1952 if (IS_CUSTOM_ELEMENT(element) ||
1953 IS_INTERNAL_ELEMENT(element))
1955 // internal values used in level editor
1957 ei->access_type = 0;
1958 ei->access_layer = 0;
1959 ei->access_protected = 0;
1960 ei->walk_to_action = 0;
1961 ei->smash_targets = 0;
1964 ei->can_explode_by_fire = FALSE;
1965 ei->can_explode_smashed = FALSE;
1966 ei->can_explode_impact = FALSE;
1968 ei->current_change_page = 0;
1971 if (IS_GROUP_ELEMENT(element) ||
1972 IS_INTERNAL_ELEMENT(element))
1974 struct ElementGroupInfo *group;
1976 // initialize memory for list of elements in group
1977 if (ei->group == NULL)
1978 ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1982 xx_group = *group; // copy group data into temporary buffer
1984 setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1989 if (IS_EMPTY_ELEMENT(element) ||
1990 IS_INTERNAL_ELEMENT(element))
1992 xx_ei = *ei; // copy element data into temporary buffer
1994 setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2000 clipboard_elements_initialized = TRUE;
2003 static void setLevelInfoToDefaults(struct LevelInfo *level,
2004 boolean level_info_only,
2005 boolean reset_file_status)
2007 setLevelInfoToDefaults_Level(level);
2009 if (!level_info_only)
2010 setLevelInfoToDefaults_Elements(level);
2012 if (reset_file_status)
2014 level->no_valid_file = FALSE;
2015 level->no_level_file = FALSE;
2018 level->changed = FALSE;
2021 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2023 level_file_info->nr = 0;
2024 level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2025 level_file_info->packed = FALSE;
2027 setString(&level_file_info->basename, NULL);
2028 setString(&level_file_info->filename, NULL);
2031 int getMappedElement_SB(int, boolean);
2033 static void ActivateLevelTemplate(void)
2037 if (check_special_flags("load_xsb_to_ces"))
2039 // fill smaller playfields with padding "beyond border wall" elements
2040 if (level.fieldx < level_template.fieldx ||
2041 level.fieldy < level_template.fieldy)
2043 short field[level.fieldx][level.fieldy];
2044 int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2045 int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2046 int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2047 int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2049 // copy old playfield (which is smaller than the visible area)
2050 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2051 field[x][y] = level.field[x][y];
2053 // fill new, larger playfield with "beyond border wall" elements
2054 for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2055 level.field[x][y] = getMappedElement_SB('_', TRUE);
2057 // copy the old playfield to the middle of the new playfield
2058 for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2059 level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2061 level.fieldx = new_fieldx;
2062 level.fieldy = new_fieldy;
2066 // Currently there is no special action needed to activate the template
2067 // data, because 'element_info' property settings overwrite the original
2068 // level data, while all other variables do not change.
2070 // Exception: 'from_level_template' elements in the original level playfield
2071 // are overwritten with the corresponding elements at the same position in
2072 // playfield from the level template.
2074 for (x = 0; x < level.fieldx; x++)
2075 for (y = 0; y < level.fieldy; y++)
2076 if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2077 level.field[x][y] = level_template.field[x][y];
2079 if (check_special_flags("load_xsb_to_ces"))
2081 struct LevelInfo level_backup = level;
2083 // overwrite all individual level settings from template level settings
2084 level = level_template;
2086 // restore level file info
2087 level.file_info = level_backup.file_info;
2089 // restore playfield size
2090 level.fieldx = level_backup.fieldx;
2091 level.fieldy = level_backup.fieldy;
2093 // restore playfield content
2094 for (x = 0; x < level.fieldx; x++)
2095 for (y = 0; y < level.fieldy; y++)
2096 level.field[x][y] = level_backup.field[x][y];
2098 // restore name and author from individual level
2099 strcpy(level.name, level_backup.name);
2100 strcpy(level.author, level_backup.author);
2102 // restore flag "use_custom_template"
2103 level.use_custom_template = level_backup.use_custom_template;
2107 static boolean checkForPackageFromBasename_BD(char *basename)
2109 // check for native BD level file extensions
2110 if (!strSuffixLower(basename, ".bd") &&
2111 !strSuffixLower(basename, ".bdr") &&
2112 !strSuffixLower(basename, ".brc") &&
2113 !strSuffixLower(basename, ".gds"))
2116 // check for standard single-level BD files (like "001.bd")
2117 if (strSuffixLower(basename, ".bd") &&
2118 strlen(basename) == 6 &&
2119 basename[0] >= '0' && basename[0] <= '9' &&
2120 basename[1] >= '0' && basename[1] <= '9' &&
2121 basename[2] >= '0' && basename[2] <= '9')
2124 // this is a level package in native BD file format
2128 static char *getLevelFilenameFromBasename(char *basename)
2130 static char *filename = NULL;
2132 checked_free(filename);
2134 filename = getPath2(getCurrentLevelDir(), basename);
2139 static int getFileTypeFromBasename(char *basename)
2141 // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2143 static char *filename = NULL;
2144 struct stat file_status;
2146 // ---------- try to determine file type from filename ----------
2148 // check for typical filename of a Supaplex level package file
2149 if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2150 return LEVEL_FILE_TYPE_SP;
2152 // check for typical filename of a Diamond Caves II level package file
2153 if (strSuffixLower(basename, ".dc") ||
2154 strSuffixLower(basename, ".dc2"))
2155 return LEVEL_FILE_TYPE_DC;
2157 // check for typical filename of a Sokoban level package file
2158 if (strSuffixLower(basename, ".xsb") &&
2159 strchr(basename, '%') == NULL)
2160 return LEVEL_FILE_TYPE_SB;
2162 // check for typical filename of a Boulder Dash (GDash) level package file
2163 if (checkForPackageFromBasename_BD(basename))
2164 return LEVEL_FILE_TYPE_BD;
2166 // ---------- try to determine file type from filesize ----------
2168 checked_free(filename);
2169 filename = getPath2(getCurrentLevelDir(), basename);
2171 if (stat(filename, &file_status) == 0)
2173 // check for typical filesize of a Supaplex level package file
2174 if (file_status.st_size == 170496)
2175 return LEVEL_FILE_TYPE_SP;
2178 return LEVEL_FILE_TYPE_UNKNOWN;
2181 static int getFileTypeFromMagicBytes(char *filename, int type)
2185 if ((file = openFile(filename, MODE_READ)))
2187 char chunk_name[CHUNK_ID_LEN + 1];
2189 getFileChunkBE(file, chunk_name, NULL);
2191 if (strEqual(chunk_name, "MMII") ||
2192 strEqual(chunk_name, "MIRR"))
2193 type = LEVEL_FILE_TYPE_MM;
2201 static boolean checkForPackageFromBasename(char *basename)
2203 // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2204 // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES !!!
2206 return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2209 static char *getSingleLevelBasenameExt(int nr, char *extension)
2211 static char basename[MAX_FILENAME_LEN];
2214 sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2216 sprintf(basename, "%03d.%s", nr, extension);
2221 static char *getSingleLevelBasename(int nr)
2223 return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2226 static char *getPackedLevelBasename(int type)
2228 static char basename[MAX_FILENAME_LEN];
2229 char *directory = getCurrentLevelDir();
2231 DirectoryEntry *dir_entry;
2233 strcpy(basename, UNDEFINED_FILENAME); // default: undefined file
2235 if ((dir = openDirectory(directory)) == NULL)
2237 Warn("cannot read current level directory '%s'", directory);
2242 while ((dir_entry = readDirectory(dir)) != NULL) // loop all entries
2244 char *entry_basename = dir_entry->basename;
2245 int entry_type = getFileTypeFromBasename(entry_basename);
2247 if (entry_type != LEVEL_FILE_TYPE_UNKNOWN) // found valid level package
2249 if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2252 strcpy(basename, entry_basename);
2259 closeDirectory(dir);
2264 static char *getSingleLevelFilename(int nr)
2266 return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2269 #if ENABLE_UNUSED_CODE
2270 static char *getPackedLevelFilename(int type)
2272 return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2276 char *getDefaultLevelFilename(int nr)
2278 return getSingleLevelFilename(nr);
2281 #if ENABLE_UNUSED_CODE
2282 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2286 lfi->packed = FALSE;
2288 setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2289 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2293 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2294 int type, char *format, ...)
2296 static char basename[MAX_FILENAME_LEN];
2299 va_start(ap, format);
2300 vsprintf(basename, format, ap);
2304 lfi->packed = FALSE;
2306 setString(&lfi->basename, basename);
2307 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2310 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2316 setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2317 setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2320 static int getFiletypeFromID(char *filetype_id)
2322 char *filetype_id_lower;
2323 int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2326 if (filetype_id == NULL)
2327 return LEVEL_FILE_TYPE_UNKNOWN;
2329 filetype_id_lower = getStringToLower(filetype_id);
2331 for (i = 0; filetype_id_list[i].id != NULL; i++)
2333 char *id_lower = getStringToLower(filetype_id_list[i].id);
2335 if (strEqual(filetype_id_lower, id_lower))
2336 filetype = filetype_id_list[i].filetype;
2340 if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2344 free(filetype_id_lower);
2349 char *getLocalLevelTemplateFilename(void)
2351 return getDefaultLevelFilename(-1);
2354 char *getGlobalLevelTemplateFilename(void)
2356 // global variable "leveldir_current" must be modified in the loop below
2357 LevelDirTree *leveldir_current_last = leveldir_current;
2358 char *filename = NULL;
2360 // check for template level in path from current to topmost tree node
2362 while (leveldir_current != NULL)
2364 filename = getDefaultLevelFilename(-1);
2366 if (fileExists(filename))
2369 leveldir_current = leveldir_current->node_parent;
2372 // restore global variable "leveldir_current" modified in above loop
2373 leveldir_current = leveldir_current_last;
2378 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2382 // special case: level number is negative => check for level template file
2385 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2386 getSingleLevelBasename(-1));
2388 // replace local level template filename with global template filename
2389 setString(&lfi->filename, getGlobalLevelTemplateFilename());
2391 // no fallback if template file not existing
2395 // special case: check for file name/pattern specified in "levelinfo.conf"
2396 if (leveldir_current->level_filename != NULL)
2398 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2400 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2401 leveldir_current->level_filename, nr);
2403 lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2405 if (fileExists(lfi->filename))
2408 else if (leveldir_current->level_filetype != NULL)
2410 int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2412 // check for specified native level file with standard file name
2413 setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2414 "%03d.%s", nr, LEVELFILE_EXTENSION);
2415 if (fileExists(lfi->filename))
2419 // check for native Rocks'n'Diamonds level file
2420 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2421 "%03d.%s", nr, LEVELFILE_EXTENSION);
2422 if (fileExists(lfi->filename))
2425 // check for native Boulder Dash level file
2426 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2427 if (fileExists(lfi->filename))
2430 // check for Emerald Mine level file (V1)
2431 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2432 'a' + (nr / 10) % 26, '0' + nr % 10);
2433 if (fileExists(lfi->filename))
2435 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2436 'A' + (nr / 10) % 26, '0' + nr % 10);
2437 if (fileExists(lfi->filename))
2440 // check for Emerald Mine level file (V2 to V5)
2441 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2442 if (fileExists(lfi->filename))
2445 // check for Emerald Mine level file (V6 / single mode)
2446 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2447 if (fileExists(lfi->filename))
2449 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2450 if (fileExists(lfi->filename))
2453 // check for Emerald Mine level file (V6 / teamwork mode)
2454 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2455 if (fileExists(lfi->filename))
2457 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2458 if (fileExists(lfi->filename))
2461 // check for various packed level file formats
2462 setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2463 if (fileExists(lfi->filename))
2466 // no known level file found -- use default values (and fail later)
2467 setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2468 "%03d.%s", nr, LEVELFILE_EXTENSION);
2471 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2473 if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2474 lfi->type = getFileTypeFromBasename(lfi->basename);
2476 if (lfi->type == LEVEL_FILE_TYPE_RND)
2477 lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2480 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2482 // always start with reliable default values
2483 setFileInfoToDefaults(level_file_info);
2485 level_file_info->nr = nr; // set requested level number
2487 determineLevelFileInfo_Filename(level_file_info);
2488 determineLevelFileInfo_Filetype(level_file_info);
2491 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2492 struct LevelFileInfo *lfi_to)
2494 lfi_to->nr = lfi_from->nr;
2495 lfi_to->type = lfi_from->type;
2496 lfi_to->packed = lfi_from->packed;
2498 setString(&lfi_to->basename, lfi_from->basename);
2499 setString(&lfi_to->filename, lfi_from->filename);
2502 // ----------------------------------------------------------------------------
2503 // functions for loading R'n'D level
2504 // ----------------------------------------------------------------------------
2506 int getMappedElement(int element)
2508 // remap some (historic, now obsolete) elements
2512 case EL_PLAYER_OBSOLETE:
2513 element = EL_PLAYER_1;
2516 case EL_KEY_OBSOLETE:
2520 case EL_EM_KEY_1_FILE_OBSOLETE:
2521 element = EL_EM_KEY_1;
2524 case EL_EM_KEY_2_FILE_OBSOLETE:
2525 element = EL_EM_KEY_2;
2528 case EL_EM_KEY_3_FILE_OBSOLETE:
2529 element = EL_EM_KEY_3;
2532 case EL_EM_KEY_4_FILE_OBSOLETE:
2533 element = EL_EM_KEY_4;
2536 case EL_ENVELOPE_OBSOLETE:
2537 element = EL_ENVELOPE_1;
2545 if (element >= NUM_FILE_ELEMENTS)
2547 Warn("invalid level element %d", element);
2549 element = EL_UNKNOWN;
2557 static int getMappedElementByVersion(int element, int game_version)
2559 // remap some elements due to certain game version
2561 if (game_version <= VERSION_IDENT(2,2,0,0))
2563 // map game font elements
2564 element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
2565 element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2566 element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
2567 element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
2570 if (game_version < VERSION_IDENT(3,0,0,0))
2572 // map Supaplex gravity tube elements
2573 element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
2574 element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2575 element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
2576 element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
2583 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2585 level->file_version = getFileVersion(file);
2586 level->game_version = getFileVersion(file);
2591 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2593 level->creation_date.year = getFile16BitBE(file);
2594 level->creation_date.month = getFile8Bit(file);
2595 level->creation_date.day = getFile8Bit(file);
2597 level->creation_date.src = DATE_SRC_LEVELFILE;
2602 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2604 int initial_player_stepsize;
2605 int initial_player_gravity;
2608 level->fieldx = getFile8Bit(file);
2609 level->fieldy = getFile8Bit(file);
2611 level->time = getFile16BitBE(file);
2612 level->gems_needed = getFile16BitBE(file);
2614 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2615 level->name[i] = getFile8Bit(file);
2616 level->name[MAX_LEVEL_NAME_LEN] = 0;
2618 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2619 level->score[i] = getFile8Bit(file);
2621 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2622 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2623 for (y = 0; y < 3; y++)
2624 for (x = 0; x < 3; x++)
2625 level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2627 level->amoeba_speed = getFile8Bit(file);
2628 level->time_magic_wall = getFile8Bit(file);
2629 level->time_wheel = getFile8Bit(file);
2630 level->amoeba_content = getMappedElement(getFile8Bit(file));
2632 initial_player_stepsize = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2635 for (i = 0; i < MAX_PLAYERS; i++)
2636 level->initial_player_stepsize[i] = initial_player_stepsize;
2638 initial_player_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2640 for (i = 0; i < MAX_PLAYERS; i++)
2641 level->initial_player_gravity[i] = initial_player_gravity;
2643 level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2644 level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2646 level->use_custom_template = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2648 level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2649 level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2650 level->can_move_into_acid_bits = getFile32BitBE(file);
2651 level->dont_collide_with_bits = getFile8Bit(file);
2653 level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2654 level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2656 level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2657 level->can_pass_to_walkable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2658 level->grow_into_diggable = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2660 level->game_engine_type = getFile8Bit(file);
2662 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2667 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2671 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2672 level->name[i] = getFile8Bit(file);
2673 level->name[MAX_LEVEL_NAME_LEN] = 0;
2678 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2682 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2683 level->author[i] = getFile8Bit(file);
2684 level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2689 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2692 int chunk_size_expected = level->fieldx * level->fieldy;
2694 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2695 stored with 16-bit encoding (and should be twice as big then).
2696 Even worse, playfield data was stored 16-bit when only yamyam content
2697 contained 16-bit elements and vice versa. */
2699 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2700 chunk_size_expected *= 2;
2702 if (chunk_size_expected != chunk_size)
2704 ReadUnusedBytesFromFile(file, chunk_size);
2705 return chunk_size_expected;
2708 for (y = 0; y < level->fieldy; y++)
2709 for (x = 0; x < level->fieldx; x++)
2710 level->field[x][y] =
2711 getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2716 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2719 int header_size = 4;
2720 int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2721 int chunk_size_expected = header_size + content_size;
2723 /* Note: "chunk_size" was wrong before version 2.0 when elements are
2724 stored with 16-bit encoding (and should be twice as big then).
2725 Even worse, playfield data was stored 16-bit when only yamyam content
2726 contained 16-bit elements and vice versa. */
2728 if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2729 chunk_size_expected += content_size;
2731 if (chunk_size_expected != chunk_size)
2733 ReadUnusedBytesFromFile(file, chunk_size);
2734 return chunk_size_expected;
2738 level->num_yamyam_contents = getFile8Bit(file);
2742 // correct invalid number of content fields -- should never happen
2743 if (level->num_yamyam_contents < 1 ||
2744 level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2745 level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2747 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2748 for (y = 0; y < 3; y++)
2749 for (x = 0; x < 3; x++)
2750 level->yamyam_content[i].e[x][y] =
2751 getMappedElement(level->encoding_16bit_field ?
2752 getFile16BitBE(file) : getFile8Bit(file));
2756 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2761 int content_array[MAX_ELEMENT_CONTENTS][3][3];
2763 element = getMappedElement(getFile16BitBE(file));
2764 num_contents = getFile8Bit(file);
2766 getFile8Bit(file); // content x size (unused)
2767 getFile8Bit(file); // content y size (unused)
2769 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2771 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2772 for (y = 0; y < 3; y++)
2773 for (x = 0; x < 3; x++)
2774 content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2776 // correct invalid number of content fields -- should never happen
2777 if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2778 num_contents = STD_ELEMENT_CONTENTS;
2780 if (element == EL_YAMYAM)
2782 level->num_yamyam_contents = num_contents;
2784 for (i = 0; i < num_contents; i++)
2785 for (y = 0; y < 3; y++)
2786 for (x = 0; x < 3; x++)
2787 level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2789 else if (element == EL_BD_AMOEBA)
2791 level->amoeba_content = content_array[0][0][0];
2795 Warn("cannot load content for element '%d'", element);
2801 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2807 int chunk_size_expected;
2809 element = getMappedElement(getFile16BitBE(file));
2810 if (!IS_ENVELOPE(element))
2811 element = EL_ENVELOPE_1;
2813 envelope_nr = element - EL_ENVELOPE_1;
2815 envelope_len = getFile16BitBE(file);
2817 level->envelope[envelope_nr].xsize = getFile8Bit(file);
2818 level->envelope[envelope_nr].ysize = getFile8Bit(file);
2820 ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2822 chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2823 if (chunk_size_expected != chunk_size)
2825 ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2826 return chunk_size_expected;
2829 for (i = 0; i < envelope_len; i++)
2830 level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2835 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2837 int num_changed_custom_elements = getFile16BitBE(file);
2838 int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2841 if (chunk_size_expected != chunk_size)
2843 ReadUnusedBytesFromFile(file, chunk_size - 2);
2844 return chunk_size_expected;
2847 for (i = 0; i < num_changed_custom_elements; i++)
2849 int element = getMappedElement(getFile16BitBE(file));
2850 int properties = getFile32BitBE(file);
2852 if (IS_CUSTOM_ELEMENT(element))
2853 element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2855 Warn("invalid custom element number %d", element);
2857 // older game versions that wrote level files with CUS1 chunks used
2858 // different default push delay values (not yet stored in level file)
2859 element_info[element].push_delay_fixed = 2;
2860 element_info[element].push_delay_random = 8;
2863 level->file_has_custom_elements = TRUE;
2868 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2870 int num_changed_custom_elements = getFile16BitBE(file);
2871 int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2874 if (chunk_size_expected != chunk_size)
2876 ReadUnusedBytesFromFile(file, chunk_size - 2);
2877 return chunk_size_expected;
2880 for (i = 0; i < num_changed_custom_elements; i++)
2882 int element = getMappedElement(getFile16BitBE(file));
2883 int custom_target_element = getMappedElement(getFile16BitBE(file));
2885 if (IS_CUSTOM_ELEMENT(element))
2886 element_info[element].change->target_element = custom_target_element;
2888 Warn("invalid custom element number %d", element);
2891 level->file_has_custom_elements = TRUE;
2896 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2898 int num_changed_custom_elements = getFile16BitBE(file);
2899 int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2902 if (chunk_size_expected != chunk_size)
2904 ReadUnusedBytesFromFile(file, chunk_size - 2);
2905 return chunk_size_expected;
2908 for (i = 0; i < num_changed_custom_elements; i++)
2910 int element = getMappedElement(getFile16BitBE(file));
2911 struct ElementInfo *ei = &element_info[element];
2912 unsigned int event_bits;
2914 if (!IS_CUSTOM_ELEMENT(element))
2916 Warn("invalid custom element number %d", element);
2918 element = EL_INTERNAL_DUMMY;
2921 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2922 ei->description[j] = getFile8Bit(file);
2923 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2925 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2927 // some free bytes for future properties and padding
2928 ReadUnusedBytesFromFile(file, 7);
2930 ei->use_gfx_element = getFile8Bit(file);
2931 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2933 ei->collect_score_initial = getFile8Bit(file);
2934 ei->collect_count_initial = getFile8Bit(file);
2936 ei->push_delay_fixed = getFile16BitBE(file);
2937 ei->push_delay_random = getFile16BitBE(file);
2938 ei->move_delay_fixed = getFile16BitBE(file);
2939 ei->move_delay_random = getFile16BitBE(file);
2941 ei->move_pattern = getFile16BitBE(file);
2942 ei->move_direction_initial = getFile8Bit(file);
2943 ei->move_stepsize = getFile8Bit(file);
2945 for (y = 0; y < 3; y++)
2946 for (x = 0; x < 3; x++)
2947 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2949 // bits 0 - 31 of "has_event[]"
2950 event_bits = getFile32BitBE(file);
2951 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2952 if (event_bits & (1u << j))
2953 ei->change->has_event[j] = TRUE;
2955 ei->change->target_element = getMappedElement(getFile16BitBE(file));
2957 ei->change->delay_fixed = getFile16BitBE(file);
2958 ei->change->delay_random = getFile16BitBE(file);
2959 ei->change->delay_frames = getFile16BitBE(file);
2961 ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2963 ei->change->explode = getFile8Bit(file);
2964 ei->change->use_target_content = getFile8Bit(file);
2965 ei->change->only_if_complete = getFile8Bit(file);
2966 ei->change->use_random_replace = getFile8Bit(file);
2968 ei->change->random_percentage = getFile8Bit(file);
2969 ei->change->replace_when = getFile8Bit(file);
2971 for (y = 0; y < 3; y++)
2972 for (x = 0; x < 3; x++)
2973 ei->change->target_content.e[x][y] =
2974 getMappedElement(getFile16BitBE(file));
2976 ei->slippery_type = getFile8Bit(file);
2978 // some free bytes for future properties and padding
2979 ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2981 // mark that this custom element has been modified
2982 ei->modified_settings = TRUE;
2985 level->file_has_custom_elements = TRUE;
2990 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2992 struct ElementInfo *ei;
2993 int chunk_size_expected;
2997 // ---------- custom element base property values (96 bytes) ----------------
2999 element = getMappedElement(getFile16BitBE(file));
3001 if (!IS_CUSTOM_ELEMENT(element))
3003 Warn("invalid custom element number %d", element);
3005 ReadUnusedBytesFromFile(file, chunk_size - 2);
3010 ei = &element_info[element];
3012 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3013 ei->description[i] = getFile8Bit(file);
3014 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3016 ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3018 ReadUnusedBytesFromFile(file, 4); // reserved for more base properties
3020 ei->num_change_pages = getFile8Bit(file);
3022 chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3023 if (chunk_size_expected != chunk_size)
3025 ReadUnusedBytesFromFile(file, chunk_size - 43);
3026 return chunk_size_expected;
3029 ei->ce_value_fixed_initial = getFile16BitBE(file);
3030 ei->ce_value_random_initial = getFile16BitBE(file);
3031 ei->use_last_ce_value = getFile8Bit(file);
3033 ei->use_gfx_element = getFile8Bit(file);
3034 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3036 ei->collect_score_initial = getFile8Bit(file);
3037 ei->collect_count_initial = getFile8Bit(file);
3039 ei->drop_delay_fixed = getFile8Bit(file);
3040 ei->push_delay_fixed = getFile8Bit(file);
3041 ei->drop_delay_random = getFile8Bit(file);
3042 ei->push_delay_random = getFile8Bit(file);
3043 ei->move_delay_fixed = getFile16BitBE(file);
3044 ei->move_delay_random = getFile16BitBE(file);
3046 // bits 0 - 15 of "move_pattern" ...
3047 ei->move_pattern = getFile16BitBE(file);
3048 ei->move_direction_initial = getFile8Bit(file);
3049 ei->move_stepsize = getFile8Bit(file);
3051 ei->slippery_type = getFile8Bit(file);
3053 for (y = 0; y < 3; y++)
3054 for (x = 0; x < 3; x++)
3055 ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3057 ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3058 ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3059 ei->move_leave_type = getFile8Bit(file);
3061 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3062 ei->move_pattern |= (getFile16BitBE(file) << 16);
3064 ei->access_direction = getFile8Bit(file);
3066 ei->explosion_delay = getFile8Bit(file);
3067 ei->ignition_delay = getFile8Bit(file);
3068 ei->explosion_type = getFile8Bit(file);
3070 // some free bytes for future custom property values and padding
3071 ReadUnusedBytesFromFile(file, 1);
3073 // ---------- change page property values (48 bytes) ------------------------
3075 setElementChangePages(ei, ei->num_change_pages);
3077 for (i = 0; i < ei->num_change_pages; i++)
3079 struct ElementChangeInfo *change = &ei->change_page[i];
3080 unsigned int event_bits;
3082 // always start with reliable default values
3083 setElementChangeInfoToDefaults(change);
3085 // bits 0 - 31 of "has_event[]" ...
3086 event_bits = getFile32BitBE(file);
3087 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3088 if (event_bits & (1u << j))
3089 change->has_event[j] = TRUE;
3091 change->target_element = getMappedElement(getFile16BitBE(file));
3093 change->delay_fixed = getFile16BitBE(file);
3094 change->delay_random = getFile16BitBE(file);
3095 change->delay_frames = getFile16BitBE(file);
3097 change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3099 change->explode = getFile8Bit(file);
3100 change->use_target_content = getFile8Bit(file);
3101 change->only_if_complete = getFile8Bit(file);
3102 change->use_random_replace = getFile8Bit(file);
3104 change->random_percentage = getFile8Bit(file);
3105 change->replace_when = getFile8Bit(file);
3107 for (y = 0; y < 3; y++)
3108 for (x = 0; x < 3; x++)
3109 change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3111 change->can_change = getFile8Bit(file);
3113 change->trigger_side = getFile8Bit(file);
3115 change->trigger_player = getFile8Bit(file);
3116 change->trigger_page = getFile8Bit(file);
3118 change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3119 CH_PAGE_ANY : (1 << change->trigger_page));
3121 change->has_action = getFile8Bit(file);
3122 change->action_type = getFile8Bit(file);
3123 change->action_mode = getFile8Bit(file);
3124 change->action_arg = getFile16BitBE(file);
3126 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3127 event_bits = getFile8Bit(file);
3128 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3129 if (event_bits & (1u << (j - 32)))
3130 change->has_event[j] = TRUE;
3133 // mark this custom element as modified
3134 ei->modified_settings = TRUE;
3136 level->file_has_custom_elements = TRUE;
3141 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3143 struct ElementInfo *ei;
3144 struct ElementGroupInfo *group;
3148 element = getMappedElement(getFile16BitBE(file));
3150 if (!IS_GROUP_ELEMENT(element))
3152 Warn("invalid group element number %d", element);
3154 ReadUnusedBytesFromFile(file, chunk_size - 2);
3159 ei = &element_info[element];
3161 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3162 ei->description[i] = getFile8Bit(file);
3163 ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3165 group = element_info[element].group;
3167 group->num_elements = getFile8Bit(file);
3169 ei->use_gfx_element = getFile8Bit(file);
3170 ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3172 group->choice_mode = getFile8Bit(file);
3174 // some free bytes for future values and padding
3175 ReadUnusedBytesFromFile(file, 3);
3177 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3178 group->element[i] = getMappedElement(getFile16BitBE(file));
3180 // mark this group element as modified
3181 element_info[element].modified_settings = TRUE;
3183 level->file_has_custom_elements = TRUE;
3188 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3189 int element, int real_element)
3191 int micro_chunk_size = 0;
3192 int conf_type = getFile8Bit(file);
3193 int byte_mask = conf_type & CONF_MASK_BYTES;
3194 boolean element_found = FALSE;
3197 micro_chunk_size += 1;
3199 if (byte_mask == CONF_MASK_MULTI_BYTES)
3201 int num_bytes = getFile16BitBE(file);
3202 byte *buffer = checked_malloc(num_bytes);
3204 ReadBytesFromFile(file, buffer, num_bytes);
3206 for (i = 0; conf[i].data_type != -1; i++)
3208 if (conf[i].element == element &&
3209 conf[i].conf_type == conf_type)
3211 int data_type = conf[i].data_type;
3212 int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3213 int max_num_entities = conf[i].max_num_entities;
3215 if (num_entities > max_num_entities)
3217 Warn("truncating number of entities for element %d from %d to %d",
3218 element, num_entities, max_num_entities);
3220 num_entities = max_num_entities;
3223 if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3224 data_type == TYPE_CONTENT_LIST))
3226 // for element and content lists, zero entities are not allowed
3227 Warn("found empty list of entities for element %d", element);
3229 // do not set "num_entities" here to prevent reading behind buffer
3231 *(int *)(conf[i].num_entities) = 1; // at least one is required
3235 *(int *)(conf[i].num_entities) = num_entities;
3238 element_found = TRUE;
3240 if (data_type == TYPE_STRING)
3242 char *string = (char *)(conf[i].value);
3245 for (j = 0; j < max_num_entities; j++)
3246 string[j] = (j < num_entities ? buffer[j] : '\0');
3248 else if (data_type == TYPE_ELEMENT_LIST)
3250 int *element_array = (int *)(conf[i].value);
3253 for (j = 0; j < num_entities; j++)
3255 getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3257 else if (data_type == TYPE_CONTENT_LIST)
3259 struct Content *content= (struct Content *)(conf[i].value);
3262 for (c = 0; c < num_entities; c++)
3263 for (y = 0; y < 3; y++)
3264 for (x = 0; x < 3; x++)
3265 content[c].e[x][y] =
3266 getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3269 element_found = FALSE;
3275 checked_free(buffer);
3277 micro_chunk_size += 2 + num_bytes;
3279 else // constant size configuration data (1, 2 or 4 bytes)
3281 int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit (file) :
3282 byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3283 byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3285 for (i = 0; conf[i].data_type != -1; i++)
3287 if (conf[i].element == element &&
3288 conf[i].conf_type == conf_type)
3290 int data_type = conf[i].data_type;
3292 if (data_type == TYPE_ELEMENT)
3293 value = getMappedElement(value);
3295 if (data_type == TYPE_BOOLEAN)
3296 *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3298 *(int *) (conf[i].value) = value;
3300 element_found = TRUE;
3306 micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3311 char *error_conf_chunk_bytes =
3312 (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3313 byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3314 byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3315 int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3316 int error_element = real_element;
3318 Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3319 error_conf_chunk_bytes, error_conf_chunk_token,
3320 error_element, EL_NAME(error_element));
3323 return micro_chunk_size;
3326 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3328 int real_chunk_size = 0;
3330 li = *level; // copy level data into temporary buffer
3332 while (!checkEndOfFile(file))
3334 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3336 if (real_chunk_size >= chunk_size)
3340 *level = li; // copy temporary buffer back to level data
3342 return real_chunk_size;
3345 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3347 int real_chunk_size = 0;
3349 li = *level; // copy level data into temporary buffer
3351 while (!checkEndOfFile(file))
3353 int element = getMappedElement(getFile16BitBE(file));
3355 real_chunk_size += 2;
3356 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3358 if (real_chunk_size >= chunk_size)
3362 *level = li; // copy temporary buffer back to level data
3364 return real_chunk_size;
3367 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3369 int real_chunk_size = 0;
3371 li = *level; // copy level data into temporary buffer
3373 while (!checkEndOfFile(file))
3375 int element = getMappedElement(getFile16BitBE(file));
3377 real_chunk_size += 2;
3378 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3380 if (real_chunk_size >= chunk_size)
3384 *level = li; // copy temporary buffer back to level data
3386 return real_chunk_size;
3389 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3391 int element = getMappedElement(getFile16BitBE(file));
3392 int envelope_nr = element - EL_ENVELOPE_1;
3393 int real_chunk_size = 2;
3395 xx_envelope = level->envelope[envelope_nr]; // copy into temporary buffer
3397 while (!checkEndOfFile(file))
3399 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3402 if (real_chunk_size >= chunk_size)
3406 level->envelope[envelope_nr] = xx_envelope; // copy from temporary buffer
3408 return real_chunk_size;
3411 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3413 int element = getMappedElement(getFile16BitBE(file));
3414 int real_chunk_size = 2;
3415 struct ElementInfo *ei = &element_info[element];
3418 xx_ei = *ei; // copy element data into temporary buffer
3420 xx_ei.num_change_pages = -1;
3422 while (!checkEndOfFile(file))
3424 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3426 if (xx_ei.num_change_pages != -1)
3429 if (real_chunk_size >= chunk_size)
3435 if (ei->num_change_pages == -1)
3437 Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3440 ei->num_change_pages = 1;
3442 setElementChangePages(ei, 1);
3443 setElementChangeInfoToDefaults(ei->change);
3445 return real_chunk_size;
3448 // initialize number of change pages stored for this custom element
3449 setElementChangePages(ei, ei->num_change_pages);
3450 for (i = 0; i < ei->num_change_pages; i++)
3451 setElementChangeInfoToDefaults(&ei->change_page[i]);
3453 // start with reading properties for the first change page
3454 xx_current_change_page = 0;
3456 while (!checkEndOfFile(file))
3458 // level file might contain invalid change page number
3459 if (xx_current_change_page >= ei->num_change_pages)
3462 struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3464 xx_change = *change; // copy change data into temporary buffer
3466 resetEventBits(); // reset bits; change page might have changed
3468 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3471 *change = xx_change;
3473 setEventFlagsFromEventBits(change);
3475 if (real_chunk_size >= chunk_size)
3479 level->file_has_custom_elements = TRUE;
3481 return real_chunk_size;
3484 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3486 int element = getMappedElement(getFile16BitBE(file));
3487 int real_chunk_size = 2;
3488 struct ElementInfo *ei = &element_info[element];
3489 struct ElementGroupInfo *group = ei->group;
3494 xx_ei = *ei; // copy element data into temporary buffer
3495 xx_group = *group; // copy group data into temporary buffer
3497 while (!checkEndOfFile(file))
3499 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3502 if (real_chunk_size >= chunk_size)
3509 level->file_has_custom_elements = TRUE;
3511 return real_chunk_size;
3514 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3516 int element = getMappedElement(getFile16BitBE(file));
3517 int real_chunk_size = 2;
3518 struct ElementInfo *ei = &element_info[element];
3520 xx_ei = *ei; // copy element data into temporary buffer
3522 while (!checkEndOfFile(file))
3524 real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3527 if (real_chunk_size >= chunk_size)
3533 level->file_has_custom_elements = TRUE;
3535 return real_chunk_size;
3538 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3539 struct LevelFileInfo *level_file_info,
3540 boolean level_info_only)
3542 char *filename = level_file_info->filename;
3543 char cookie[MAX_LINE_LEN];
3544 char chunk_name[CHUNK_ID_LEN + 1];
3548 if (!(file = openFile(filename, MODE_READ)))
3550 level->no_valid_file = TRUE;
3551 level->no_level_file = TRUE;
3553 if (level_info_only)
3556 Warn("cannot read level '%s' -- using empty level", filename);
3558 if (!setup.editor.use_template_for_new_levels)
3561 // if level file not found, try to initialize level data from template
3562 filename = getGlobalLevelTemplateFilename();
3564 if (!(file = openFile(filename, MODE_READ)))
3567 // default: for empty levels, use level template for custom elements
3568 level->use_custom_template = TRUE;
3570 level->no_valid_file = FALSE;
3573 getFileChunkBE(file, chunk_name, NULL);
3574 if (strEqual(chunk_name, "RND1"))
3576 getFile32BitBE(file); // not used
3578 getFileChunkBE(file, chunk_name, NULL);
3579 if (!strEqual(chunk_name, "CAVE"))
3581 level->no_valid_file = TRUE;
3583 Warn("unknown format of level file '%s'", filename);
3590 else // check for pre-2.0 file format with cookie string
3592 strcpy(cookie, chunk_name);
3593 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3595 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3596 cookie[strlen(cookie) - 1] = '\0';
3598 if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3600 level->no_valid_file = TRUE;
3602 Warn("unknown format of level file '%s'", filename);
3609 if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3611 level->no_valid_file = TRUE;
3613 Warn("unsupported version of level file '%s'", filename);
3620 // pre-2.0 level files have no game version, so use file version here
3621 level->game_version = level->file_version;
3624 if (level->file_version < FILE_VERSION_1_2)
3626 // level files from versions before 1.2.0 without chunk structure
3627 LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE, level);
3628 LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3636 int (*loader)(File *, int, struct LevelInfo *);
3640 { "VERS", LEVEL_CHUNK_VERS_SIZE, LoadLevel_VERS },
3641 { "DATE", LEVEL_CHUNK_DATE_SIZE, LoadLevel_DATE },
3642 { "HEAD", LEVEL_CHUNK_HEAD_SIZE, LoadLevel_HEAD },
3643 { "NAME", LEVEL_CHUNK_NAME_SIZE, LoadLevel_NAME },
3644 { "AUTH", LEVEL_CHUNK_AUTH_SIZE, LoadLevel_AUTH },
3645 { "INFO", -1, LoadLevel_INFO },
3646 { "BODY", -1, LoadLevel_BODY },
3647 { "CONT", -1, LoadLevel_CONT },
3648 { "CNT2", LEVEL_CHUNK_CNT2_SIZE, LoadLevel_CNT2 },
3649 { "CNT3", -1, LoadLevel_CNT3 },
3650 { "CUS1", -1, LoadLevel_CUS1 },
3651 { "CUS2", -1, LoadLevel_CUS2 },
3652 { "CUS3", -1, LoadLevel_CUS3 },
3653 { "CUS4", -1, LoadLevel_CUS4 },
3654 { "GRP1", -1, LoadLevel_GRP1 },
3655 { "CONF", -1, LoadLevel_CONF },
3656 { "ELEM", -1, LoadLevel_ELEM },
3657 { "NOTE", -1, LoadLevel_NOTE },
3658 { "CUSX", -1, LoadLevel_CUSX },
3659 { "GRPX", -1, LoadLevel_GRPX },
3660 { "EMPX", -1, LoadLevel_EMPX },
3665 while (getFileChunkBE(file, chunk_name, &chunk_size))
3669 while (chunk_info[i].name != NULL &&
3670 !strEqual(chunk_name, chunk_info[i].name))
3673 if (chunk_info[i].name == NULL)
3675 Warn("unknown chunk '%s' in level file '%s'",
3676 chunk_name, filename);
3678 ReadUnusedBytesFromFile(file, chunk_size);
3680 else if (chunk_info[i].size != -1 &&
3681 chunk_info[i].size != chunk_size)
3683 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3684 chunk_size, chunk_name, filename);
3686 ReadUnusedBytesFromFile(file, chunk_size);
3690 // call function to load this level chunk
3691 int chunk_size_expected =
3692 (chunk_info[i].loader)(file, chunk_size, level);
3694 if (chunk_size_expected < 0)
3696 Warn("error reading chunk '%s' in level file '%s'",
3697 chunk_name, filename);
3702 // the size of some chunks cannot be checked before reading other
3703 // chunks first (like "HEAD" and "BODY") that contain some header
3704 // information, so check them here
3705 if (chunk_size_expected != chunk_size)
3707 Warn("wrong size (%d) of chunk '%s' in level file '%s'",
3708 chunk_size, chunk_name, filename);
3720 // ----------------------------------------------------------------------------
3721 // functions for loading BD level
3722 // ----------------------------------------------------------------------------
3724 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
3726 struct LevelInfo_BD *level_bd = level->native_bd_level;
3727 GdCave *cave = NULL; // will be changed below
3728 int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3729 int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3732 setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
3734 // cave and map newly allocated when set to defaults above
3735 cave = level_bd->cave;
3737 for (i = 0; i < 5; i++)
3739 cave->level_time[i] = level->time;
3740 cave->level_diamonds[i] = level->gems_needed;
3741 cave->level_magic_wall_time[i] = level->time_magic_wall;
3743 cave->level_speed[i] = level->bd_cycle_delay_ms;
3744 cave->level_ckdelay[i] = level->bd_cycle_delay_c64;
3746 cave->level_timevalue[i] = level->score[SC_TIME_BONUS];
3749 cave->diamond_value = level->score[SC_EMERALD];
3750 cave->extra_diamond_value = level->score[SC_DIAMOND_EXTRA];
3752 cave->scheduling = level->bd_scheduling_type;
3753 cave->pal_timing = level->bd_pal_timing;
3754 cave->intermission = level->bd_intermission;
3755 cave->diagonal_movements = level->bd_diagonal_movements;
3757 strncpy(cave->name, level->name, sizeof(GdString));
3758 cave->name[sizeof(GdString) - 1] = '\0';
3760 for (x = 0; x < cave->w; x++)
3761 for (y = 0; y < cave->h; y++)
3762 cave->map[y][x] = map_element_RND_to_BD(level->field[x][y]);
3765 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
3767 struct LevelInfo_BD *level_bd = level->native_bd_level;
3768 GdCave *cave = level_bd->cave;
3769 int bd_level_nr = level_bd->level_nr;
3772 level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
3773 level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
3775 level->time = cave->level_time[bd_level_nr];
3776 level->gems_needed = cave->level_diamonds[bd_level_nr];
3777 level->time_magic_wall = cave->level_magic_wall_time[bd_level_nr];
3779 level->bd_cycle_delay_ms = cave->level_speed[bd_level_nr];
3780 level->bd_cycle_delay_c64 = cave->level_ckdelay[bd_level_nr];
3782 level->score[SC_TIME_BONUS] = cave->level_timevalue[bd_level_nr];
3783 level->score[SC_EMERALD] = cave->diamond_value;
3784 level->score[SC_DIAMOND_EXTRA] = cave->extra_diamond_value;
3786 level->bd_scheduling_type = cave->scheduling;
3787 level->bd_pal_timing = cave->pal_timing;
3788 level->bd_intermission = cave->intermission;
3789 level->bd_diagonal_movements = cave->diagonal_movements;
3791 char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
3793 strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
3794 level->name[MAX_LEVEL_NAME_LEN] = '\0';
3796 for (x = 0; x < level->fieldx; x++)
3797 for (y = 0; y < level->fieldy; y++)
3798 level->field[x][y] = map_element_BD_to_RND(cave->map[y][x]);
3800 checked_free(cave_name);
3803 static void setTapeInfoToDefaults(void);
3805 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
3807 struct LevelInfo_BD *level_bd = level->native_bd_level;
3808 GdCave *cave = level_bd->cave;
3809 GdReplay *replay = level_bd->replay;
3815 // always start with reliable default values
3816 setTapeInfoToDefaults();
3818 tape.level_nr = level_nr; // (currently not used)
3819 tape.random_seed = replay->seed;
3821 TapeSetDateFromIsoDateString(replay->date);
3824 tape.pos[tape.counter].delay = 0;
3826 tape.bd_replay = TRUE;
3828 // all time calculations only used to display approximate tape time
3829 int cave_speed = cave->speed;
3830 int milliseconds_game = 0;
3831 int milliseconds_elapsed = 20;
3833 for (i = 0; i < replay->movements->len; i++)
3835 int replay_action = replay->movements->data[i];
3836 int tape_action = map_action_BD_to_RND(replay_action);
3837 byte action[MAX_TAPE_ACTIONS] = { tape_action };
3838 boolean success = 0;
3842 success = TapeAddAction(action);
3844 milliseconds_game += milliseconds_elapsed;
3846 if (milliseconds_game >= cave_speed)
3848 milliseconds_game -= cave_speed;
3855 tape.pos[tape.counter].delay = 0;
3856 tape.pos[tape.counter].action[0] = 0;
3860 Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
3866 TapeHaltRecording();
3870 // ----------------------------------------------------------------------------
3871 // functions for loading EM level
3872 // ----------------------------------------------------------------------------
3874 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3876 static int ball_xy[8][2] =
3887 struct LevelInfo_EM *level_em = level->native_em_level;
3888 struct CAVE *cav = level_em->cav;
3891 cav->width = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
3892 cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
3894 cav->time_seconds = level->time;
3895 cav->gems_needed = level->gems_needed;
3897 cav->emerald_score = level->score[SC_EMERALD];
3898 cav->diamond_score = level->score[SC_DIAMOND];
3899 cav->alien_score = level->score[SC_ROBOT];
3900 cav->tank_score = level->score[SC_SPACESHIP];
3901 cav->bug_score = level->score[SC_BUG];
3902 cav->eater_score = level->score[SC_YAMYAM];
3903 cav->nut_score = level->score[SC_NUT];
3904 cav->dynamite_score = level->score[SC_DYNAMITE];
3905 cav->key_score = level->score[SC_KEY];
3906 cav->exit_score = level->score[SC_TIME_BONUS];
3908 cav->num_eater_arrays = level->num_yamyam_contents;
3910 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3911 for (y = 0; y < 3; y++)
3912 for (x = 0; x < 3; x++)
3913 cav->eater_array[i][y * 3 + x] =
3914 map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
3916 cav->amoeba_time = level->amoeba_speed;
3917 cav->wonderwall_time = level->time_magic_wall;
3918 cav->wheel_time = level->time_wheel;
3920 cav->android_move_time = level->android_move_time;
3921 cav->android_clone_time = level->android_clone_time;
3922 cav->ball_random = level->ball_random;
3923 cav->ball_active = level->ball_active_initial;
3924 cav->ball_time = level->ball_time;
3925 cav->num_ball_arrays = level->num_ball_contents;
3927 cav->lenses_score = level->lenses_score;
3928 cav->magnify_score = level->magnify_score;
3929 cav->slurp_score = level->slurp_score;
3931 cav->lenses_time = level->lenses_time;
3932 cav->magnify_time = level->magnify_time;
3934 cav->wind_time = 9999;
3935 cav->wind_direction =
3936 map_direction_RND_to_EM(level->wind_direction_initial);
3938 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3939 for (j = 0; j < 8; j++)
3940 cav->ball_array[i][j] =
3941 map_element_RND_to_EM_cave(level->ball_content[i].
3942 e[ball_xy[j][0]][ball_xy[j][1]]);
3944 map_android_clone_elements_RND_to_EM(level);
3946 // first fill the complete playfield with the empty space element
3947 for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3948 for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3949 cav->cave[x][y] = Cblank;
3951 // then copy the real level contents from level file into the playfield
3952 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3954 int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
3956 if (level->field[x][y] == EL_AMOEBA_DEAD)
3957 new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
3959 cav->cave[x][y] = new_element;
3962 for (i = 0; i < MAX_PLAYERS; i++)
3964 cav->player_x[i] = -1;
3965 cav->player_y[i] = -1;
3968 // initialize player positions and delete players from the playfield
3969 for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
3971 if (IS_PLAYER_ELEMENT(level->field[x][y]))
3973 int player_nr = GET_PLAYER_NR(level->field[x][y]);
3975 cav->player_x[player_nr] = x;
3976 cav->player_y[player_nr] = y;
3978 cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
3983 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3985 static int ball_xy[8][2] =
3996 struct LevelInfo_EM *level_em = level->native_em_level;
3997 struct CAVE *cav = level_em->cav;
4000 level->fieldx = MIN(cav->width, MAX_LEV_FIELDX);
4001 level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4003 level->time = cav->time_seconds;
4004 level->gems_needed = cav->gems_needed;
4006 sprintf(level->name, "Level %d", level->file_info.nr);
4008 level->score[SC_EMERALD] = cav->emerald_score;
4009 level->score[SC_DIAMOND] = cav->diamond_score;
4010 level->score[SC_ROBOT] = cav->alien_score;
4011 level->score[SC_SPACESHIP] = cav->tank_score;
4012 level->score[SC_BUG] = cav->bug_score;
4013 level->score[SC_YAMYAM] = cav->eater_score;
4014 level->score[SC_NUT] = cav->nut_score;
4015 level->score[SC_DYNAMITE] = cav->dynamite_score;
4016 level->score[SC_KEY] = cav->key_score;
4017 level->score[SC_TIME_BONUS] = cav->exit_score;
4019 level->num_yamyam_contents = cav->num_eater_arrays;
4021 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4022 for (y = 0; y < 3; y++)
4023 for (x = 0; x < 3; x++)
4024 level->yamyam_content[i].e[x][y] =
4025 map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4027 level->amoeba_speed = cav->amoeba_time;
4028 level->time_magic_wall = cav->wonderwall_time;
4029 level->time_wheel = cav->wheel_time;
4031 level->android_move_time = cav->android_move_time;
4032 level->android_clone_time = cav->android_clone_time;
4033 level->ball_random = cav->ball_random;
4034 level->ball_active_initial = cav->ball_active;
4035 level->ball_time = cav->ball_time;
4036 level->num_ball_contents = cav->num_ball_arrays;
4038 level->lenses_score = cav->lenses_score;
4039 level->magnify_score = cav->magnify_score;
4040 level->slurp_score = cav->slurp_score;
4042 level->lenses_time = cav->lenses_time;
4043 level->magnify_time = cav->magnify_time;
4045 level->wind_direction_initial =
4046 map_direction_EM_to_RND(cav->wind_direction);
4048 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4049 for (j = 0; j < 8; j++)
4050 level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4051 map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4053 map_android_clone_elements_EM_to_RND(level);
4055 // convert the playfield (some elements need special treatment)
4056 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4058 int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4060 if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4061 new_element = EL_AMOEBA_DEAD;
4063 level->field[x][y] = new_element;
4066 for (i = 0; i < MAX_PLAYERS; i++)
4068 // in case of all players set to the same field, use the first player
4069 int nr = MAX_PLAYERS - i - 1;
4070 int jx = cav->player_x[nr];
4071 int jy = cav->player_y[nr];
4073 if (jx != -1 && jy != -1)
4074 level->field[jx][jy] = EL_PLAYER_1 + nr;
4077 // time score is counted for each 10 seconds left in Emerald Mine levels
4078 level->time_score_base = 10;
4082 // ----------------------------------------------------------------------------
4083 // functions for loading SP level
4084 // ----------------------------------------------------------------------------
4086 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4088 struct LevelInfo_SP *level_sp = level->native_sp_level;
4089 LevelInfoType *header = &level_sp->header;
4092 level_sp->width = level->fieldx;
4093 level_sp->height = level->fieldy;
4095 for (x = 0; x < level->fieldx; x++)
4096 for (y = 0; y < level->fieldy; y++)
4097 level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4099 header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4101 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4102 header->LevelTitle[i] = level->name[i];
4103 // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4105 header->InfotronsNeeded = level->gems_needed;
4107 header->SpecialPortCount = 0;
4109 for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4111 boolean gravity_port_found = FALSE;
4112 boolean gravity_port_valid = FALSE;
4113 int gravity_port_flag;
4114 int gravity_port_base_element;
4115 int element = level->field[x][y];
4117 if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4118 element <= EL_SP_GRAVITY_ON_PORT_UP)
4120 gravity_port_found = TRUE;
4121 gravity_port_valid = TRUE;
4122 gravity_port_flag = 1;
4123 gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4125 else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4126 element <= EL_SP_GRAVITY_OFF_PORT_UP)
4128 gravity_port_found = TRUE;
4129 gravity_port_valid = TRUE;
4130 gravity_port_flag = 0;
4131 gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4133 else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4134 element <= EL_SP_GRAVITY_PORT_UP)
4136 // change R'n'D style gravity inverting special port to normal port
4137 // (there are no gravity inverting ports in native Supaplex engine)
4139 gravity_port_found = TRUE;
4140 gravity_port_valid = FALSE;
4141 gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4144 if (gravity_port_found)
4146 if (gravity_port_valid &&
4147 header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4149 SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4151 port->PortLocation = (y * level->fieldx + x) * 2;
4152 port->Gravity = gravity_port_flag;
4154 element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4156 header->SpecialPortCount++;
4160 // change special gravity port to normal port
4162 element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4165 level_sp->playfield[x][y] = element - EL_SP_START;
4170 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4172 struct LevelInfo_SP *level_sp = level->native_sp_level;
4173 LevelInfoType *header = &level_sp->header;
4174 boolean num_invalid_elements = 0;
4177 level->fieldx = level_sp->width;
4178 level->fieldy = level_sp->height;
4180 for (x = 0; x < level->fieldx; x++)
4182 for (y = 0; y < level->fieldy; y++)
4184 int element_old = level_sp->playfield[x][y];
4185 int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4187 if (element_new == EL_UNKNOWN)
4189 num_invalid_elements++;
4191 Debug("level:native:SP", "invalid element %d at position %d, %d",
4195 level->field[x][y] = element_new;
4199 if (num_invalid_elements > 0)
4200 Warn("found %d invalid elements%s", num_invalid_elements,
4201 (!options.debug ? " (use '--debug' for more details)" : ""));
4203 for (i = 0; i < MAX_PLAYERS; i++)
4204 level->initial_player_gravity[i] =
4205 (header->InitialGravity == 1 ? TRUE : FALSE);
4207 // skip leading spaces
4208 for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4209 if (header->LevelTitle[i] != ' ')
4213 for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4214 level->name[j] = header->LevelTitle[i];
4215 level->name[j] = '\0';
4217 // cut trailing spaces
4219 if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4220 level->name[j - 1] = '\0';
4222 level->gems_needed = header->InfotronsNeeded;
4224 for (i = 0; i < header->SpecialPortCount; i++)
4226 SpecialPortType *port = &header->SpecialPort[i];
4227 int port_location = port->PortLocation;
4228 int gravity = port->Gravity;
4229 int port_x, port_y, port_element;
4231 port_x = (port_location / 2) % level->fieldx;
4232 port_y = (port_location / 2) / level->fieldx;
4234 if (port_x < 0 || port_x >= level->fieldx ||
4235 port_y < 0 || port_y >= level->fieldy)
4237 Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4242 port_element = level->field[port_x][port_y];
4244 if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4245 port_element > EL_SP_GRAVITY_PORT_UP)
4247 Warn("no special port at position (%d, %d)", port_x, port_y);
4252 // change previous (wrong) gravity inverting special port to either
4253 // gravity enabling special port or gravity disabling special port
4254 level->field[port_x][port_y] +=
4255 (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4256 EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4259 // change special gravity ports without database entries to normal ports
4260 for (x = 0; x < level->fieldx; x++)
4261 for (y = 0; y < level->fieldy; y++)
4262 if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4263 level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4264 level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4266 level->time = 0; // no time limit
4267 level->amoeba_speed = 0;
4268 level->time_magic_wall = 0;
4269 level->time_wheel = 0;
4270 level->amoeba_content = EL_EMPTY;
4272 // original Supaplex does not use score values -- rate by playing time
4273 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4274 level->score[i] = 0;
4276 level->rate_time_over_score = TRUE;
4278 // there are no yamyams in supaplex levels
4279 for (i = 0; i < level->num_yamyam_contents; i++)
4280 for (x = 0; x < 3; x++)
4281 for (y = 0; y < 3; y++)
4282 level->yamyam_content[i].e[x][y] = EL_EMPTY;
4285 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4287 struct LevelInfo_SP *level_sp = level->native_sp_level;
4288 struct DemoInfo_SP *demo = &level_sp->demo;
4291 // always start with reliable default values
4292 demo->is_available = FALSE;
4295 if (TAPE_IS_EMPTY(tape))
4298 demo->level_nr = tape.level_nr; // (currently not used)
4300 level_sp->header.DemoRandomSeed = tape.random_seed;
4304 for (i = 0; i < tape.length; i++)
4306 int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4307 int demo_repeat = tape.pos[i].delay;
4308 int demo_entries = (demo_repeat + 15) / 16;
4310 if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4312 Warn("tape truncated: size exceeds maximum SP demo size %d",
4318 for (j = 0; j < demo_repeat / 16; j++)
4319 demo->data[demo->length++] = 0xf0 | demo_action;
4321 if (demo_repeat % 16)
4322 demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4325 demo->is_available = TRUE;
4328 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4330 struct LevelInfo_SP *level_sp = level->native_sp_level;
4331 struct DemoInfo_SP *demo = &level_sp->demo;
4332 char *filename = level->file_info.filename;
4335 // always start with reliable default values
4336 setTapeInfoToDefaults();
4338 if (!demo->is_available)
4341 tape.level_nr = demo->level_nr; // (currently not used)
4342 tape.random_seed = level_sp->header.DemoRandomSeed;
4344 TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4347 tape.pos[tape.counter].delay = 0;
4349 for (i = 0; i < demo->length; i++)
4351 int demo_action = demo->data[i] & 0x0f;
4352 int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4353 int tape_action = map_key_SP_to_RND(demo_action);
4354 int tape_repeat = demo_repeat + 1;
4355 byte action[MAX_TAPE_ACTIONS] = { tape_action };
4356 boolean success = 0;
4359 for (j = 0; j < tape_repeat; j++)
4360 success = TapeAddAction(action);
4364 Warn("SP demo truncated: size exceeds maximum tape size %d",
4371 TapeHaltRecording();
4375 // ----------------------------------------------------------------------------
4376 // functions for loading MM level
4377 // ----------------------------------------------------------------------------
4379 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4381 struct LevelInfo_MM *level_mm = level->native_mm_level;
4384 level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4385 level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4387 level_mm->time = level->time;
4388 level_mm->kettles_needed = level->gems_needed;
4389 level_mm->auto_count_kettles = level->auto_count_gems;
4391 level_mm->mm_laser_red = level->mm_laser_red;
4392 level_mm->mm_laser_green = level->mm_laser_green;
4393 level_mm->mm_laser_blue = level->mm_laser_blue;
4395 level_mm->df_laser_red = level->df_laser_red;
4396 level_mm->df_laser_green = level->df_laser_green;
4397 level_mm->df_laser_blue = level->df_laser_blue;
4399 strcpy(level_mm->name, level->name);
4400 strcpy(level_mm->author, level->author);
4402 level_mm->score[SC_EMERALD] = level->score[SC_EMERALD];
4403 level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
4404 level_mm->score[SC_KEY] = level->score[SC_KEY];
4405 level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4406 level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4408 level_mm->amoeba_speed = level->amoeba_speed;
4409 level_mm->time_fuse = level->mm_time_fuse;
4410 level_mm->time_bomb = level->mm_time_bomb;
4411 level_mm->time_ball = level->mm_time_ball;
4412 level_mm->time_block = level->mm_time_block;
4414 level_mm->num_ball_contents = level->num_mm_ball_contents;
4415 level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4416 level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4417 level_mm->explode_ball = level->explode_mm_ball;
4419 for (i = 0; i < level->num_mm_ball_contents; i++)
4420 level_mm->ball_content[i] =
4421 map_element_RND_to_MM(level->mm_ball_content[i]);
4423 for (x = 0; x < level->fieldx; x++)
4424 for (y = 0; y < level->fieldy; y++)
4426 level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4429 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4431 struct LevelInfo_MM *level_mm = level->native_mm_level;
4434 level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4435 level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4437 level->time = level_mm->time;
4438 level->gems_needed = level_mm->kettles_needed;
4439 level->auto_count_gems = level_mm->auto_count_kettles;
4441 level->mm_laser_red = level_mm->mm_laser_red;
4442 level->mm_laser_green = level_mm->mm_laser_green;
4443 level->mm_laser_blue = level_mm->mm_laser_blue;
4445 level->df_laser_red = level_mm->df_laser_red;
4446 level->df_laser_green = level_mm->df_laser_green;
4447 level->df_laser_blue = level_mm->df_laser_blue;
4449 strcpy(level->name, level_mm->name);
4451 // only overwrite author from 'levelinfo.conf' if author defined in level
4452 if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4453 strcpy(level->author, level_mm->author);
4455 level->score[SC_EMERALD] = level_mm->score[SC_EMERALD];
4456 level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
4457 level->score[SC_KEY] = level_mm->score[SC_KEY];
4458 level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4459 level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4461 level->amoeba_speed = level_mm->amoeba_speed;
4462 level->mm_time_fuse = level_mm->time_fuse;
4463 level->mm_time_bomb = level_mm->time_bomb;
4464 level->mm_time_ball = level_mm->time_ball;
4465 level->mm_time_block = level_mm->time_block;
4467 level->num_mm_ball_contents = level_mm->num_ball_contents;
4468 level->mm_ball_choice_mode = level_mm->ball_choice_mode;
4469 level->rotate_mm_ball_content = level_mm->rotate_ball_content;
4470 level->explode_mm_ball = level_mm->explode_ball;
4472 for (i = 0; i < level->num_mm_ball_contents; i++)
4473 level->mm_ball_content[i] =
4474 map_element_MM_to_RND(level_mm->ball_content[i]);
4476 for (x = 0; x < level->fieldx; x++)
4477 for (y = 0; y < level->fieldy; y++)
4478 level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
4482 // ----------------------------------------------------------------------------
4483 // functions for loading DC level
4484 // ----------------------------------------------------------------------------
4486 #define DC_LEVEL_HEADER_SIZE 344
4488 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
4491 static int last_data_encoded;
4495 int diff_hi, diff_lo;
4496 int data_hi, data_lo;
4497 unsigned short data_decoded;
4501 last_data_encoded = 0;
4508 diff = data_encoded - last_data_encoded;
4509 diff_hi = diff & ~0xff;
4510 diff_lo = diff & 0xff;
4514 data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
4515 data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
4516 data_hi = data_hi & 0xff00;
4518 data_decoded = data_hi | data_lo;
4520 last_data_encoded = data_encoded;
4522 offset1 = (offset1 + 1) % 31;
4523 offset2 = offset2 & 0xff;
4525 return data_decoded;
4528 static int getMappedElement_DC(int element)
4536 // 0x0117 - 0x036e: (?)
4539 // 0x042d - 0x0684: (?)
4555 element = EL_CRYSTAL;
4558 case 0x0e77: // quicksand (boulder)
4559 element = EL_QUICKSAND_FAST_FULL;
4562 case 0x0e99: // slow quicksand (boulder)
4563 element = EL_QUICKSAND_FULL;
4567 element = EL_EM_EXIT_OPEN;
4571 element = EL_EM_EXIT_CLOSED;
4575 element = EL_EM_STEEL_EXIT_OPEN;
4579 element = EL_EM_STEEL_EXIT_CLOSED;
4582 case 0x0f4f: // dynamite (lit 1)
4583 element = EL_EM_DYNAMITE_ACTIVE;
4586 case 0x0f57: // dynamite (lit 2)
4587 element = EL_EM_DYNAMITE_ACTIVE;
4590 case 0x0f5f: // dynamite (lit 3)
4591 element = EL_EM_DYNAMITE_ACTIVE;
4594 case 0x0f67: // dynamite (lit 4)
4595 element = EL_EM_DYNAMITE_ACTIVE;
4602 element = EL_AMOEBA_WET;
4606 element = EL_AMOEBA_DROP;
4610 element = EL_DC_MAGIC_WALL;
4614 element = EL_SPACESHIP_UP;
4618 element = EL_SPACESHIP_DOWN;
4622 element = EL_SPACESHIP_LEFT;
4626 element = EL_SPACESHIP_RIGHT;
4630 element = EL_BUG_UP;
4634 element = EL_BUG_DOWN;
4638 element = EL_BUG_LEFT;
4642 element = EL_BUG_RIGHT;
4646 element = EL_MOLE_UP;
4650 element = EL_MOLE_DOWN;
4654 element = EL_MOLE_LEFT;
4658 element = EL_MOLE_RIGHT;
4666 element = EL_YAMYAM_UP;
4670 element = EL_SWITCHGATE_OPEN;
4674 element = EL_SWITCHGATE_CLOSED;
4678 element = EL_DC_SWITCHGATE_SWITCH_UP;
4682 element = EL_TIMEGATE_CLOSED;
4685 case 0x144c: // conveyor belt switch (green)
4686 element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4689 case 0x144f: // conveyor belt switch (red)
4690 element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4693 case 0x1452: // conveyor belt switch (blue)
4694 element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4698 element = EL_CONVEYOR_BELT_3_MIDDLE;
4702 element = EL_CONVEYOR_BELT_3_LEFT;
4706 element = EL_CONVEYOR_BELT_3_RIGHT;
4710 element = EL_CONVEYOR_BELT_1_MIDDLE;
4714 element = EL_CONVEYOR_BELT_1_LEFT;
4718 element = EL_CONVEYOR_BELT_1_RIGHT;
4722 element = EL_CONVEYOR_BELT_4_MIDDLE;
4726 element = EL_CONVEYOR_BELT_4_LEFT;
4730 element = EL_CONVEYOR_BELT_4_RIGHT;
4734 element = EL_EXPANDABLE_WALL_HORIZONTAL;
4738 element = EL_EXPANDABLE_WALL_VERTICAL;
4742 element = EL_EXPANDABLE_WALL_ANY;
4745 case 0x14ce: // growing steel wall (left/right)
4746 element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4749 case 0x14df: // growing steel wall (up/down)
4750 element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4753 case 0x14e8: // growing steel wall (up/down/left/right)
4754 element = EL_EXPANDABLE_STEELWALL_ANY;
4758 element = EL_SHIELD_DEADLY;
4762 element = EL_EXTRA_TIME;
4770 element = EL_EMPTY_SPACE;
4773 case 0x1578: // quicksand (empty)
4774 element = EL_QUICKSAND_FAST_EMPTY;
4777 case 0x1579: // slow quicksand (empty)
4778 element = EL_QUICKSAND_EMPTY;
4788 element = EL_EM_DYNAMITE;
4791 case 0x15a1: // key (red)
4792 element = EL_EM_KEY_1;
4795 case 0x15a2: // key (yellow)
4796 element = EL_EM_KEY_2;
4799 case 0x15a3: // key (blue)
4800 element = EL_EM_KEY_4;
4803 case 0x15a4: // key (green)
4804 element = EL_EM_KEY_3;
4807 case 0x15a5: // key (white)
4808 element = EL_DC_KEY_WHITE;
4812 element = EL_WALL_SLIPPERY;
4819 case 0x15a8: // wall (not round)
4823 case 0x15a9: // (blue)
4824 element = EL_CHAR_A;
4827 case 0x15aa: // (blue)
4828 element = EL_CHAR_B;
4831 case 0x15ab: // (blue)
4832 element = EL_CHAR_C;
4835 case 0x15ac: // (blue)
4836 element = EL_CHAR_D;
4839 case 0x15ad: // (blue)
4840 element = EL_CHAR_E;
4843 case 0x15ae: // (blue)
4844 element = EL_CHAR_F;
4847 case 0x15af: // (blue)
4848 element = EL_CHAR_G;
4851 case 0x15b0: // (blue)
4852 element = EL_CHAR_H;
4855 case 0x15b1: // (blue)
4856 element = EL_CHAR_I;
4859 case 0x15b2: // (blue)
4860 element = EL_CHAR_J;
4863 case 0x15b3: // (blue)
4864 element = EL_CHAR_K;
4867 case 0x15b4: // (blue)
4868 element = EL_CHAR_L;
4871 case 0x15b5: // (blue)
4872 element = EL_CHAR_M;
4875 case 0x15b6: // (blue)
4876 element = EL_CHAR_N;
4879 case 0x15b7: // (blue)
4880 element = EL_CHAR_O;
4883 case 0x15b8: // (blue)
4884 element = EL_CHAR_P;
4887 case 0x15b9: // (blue)
4888 element = EL_CHAR_Q;
4891 case 0x15ba: // (blue)
4892 element = EL_CHAR_R;
4895 case 0x15bb: // (blue)
4896 element = EL_CHAR_S;
4899 case 0x15bc: // (blue)
4900 element = EL_CHAR_T;
4903 case 0x15bd: // (blue)
4904 element = EL_CHAR_U;
4907 case 0x15be: // (blue)
4908 element = EL_CHAR_V;
4911 case 0x15bf: // (blue)
4912 element = EL_CHAR_W;
4915 case 0x15c0: // (blue)
4916 element = EL_CHAR_X;
4919 case 0x15c1: // (blue)
4920 element = EL_CHAR_Y;
4923 case 0x15c2: // (blue)
4924 element = EL_CHAR_Z;
4927 case 0x15c3: // (blue)
4928 element = EL_CHAR_AUMLAUT;
4931 case 0x15c4: // (blue)
4932 element = EL_CHAR_OUMLAUT;
4935 case 0x15c5: // (blue)
4936 element = EL_CHAR_UUMLAUT;
4939 case 0x15c6: // (blue)
4940 element = EL_CHAR_0;
4943 case 0x15c7: // (blue)
4944 element = EL_CHAR_1;
4947 case 0x15c8: // (blue)
4948 element = EL_CHAR_2;
4951 case 0x15c9: // (blue)
4952 element = EL_CHAR_3;
4955 case 0x15ca: // (blue)
4956 element = EL_CHAR_4;
4959 case 0x15cb: // (blue)
4960 element = EL_CHAR_5;
4963 case 0x15cc: // (blue)
4964 element = EL_CHAR_6;
4967 case 0x15cd: // (blue)
4968 element = EL_CHAR_7;
4971 case 0x15ce: // (blue)
4972 element = EL_CHAR_8;
4975 case 0x15cf: // (blue)
4976 element = EL_CHAR_9;
4979 case 0x15d0: // (blue)
4980 element = EL_CHAR_PERIOD;
4983 case 0x15d1: // (blue)
4984 element = EL_CHAR_EXCLAM;
4987 case 0x15d2: // (blue)
4988 element = EL_CHAR_COLON;
4991 case 0x15d3: // (blue)
4992 element = EL_CHAR_LESS;
4995 case 0x15d4: // (blue)
4996 element = EL_CHAR_GREATER;
4999 case 0x15d5: // (blue)
5000 element = EL_CHAR_QUESTION;
5003 case 0x15d6: // (blue)
5004 element = EL_CHAR_COPYRIGHT;
5007 case 0x15d7: // (blue)
5008 element = EL_CHAR_UP;
5011 case 0x15d8: // (blue)
5012 element = EL_CHAR_DOWN;
5015 case 0x15d9: // (blue)
5016 element = EL_CHAR_BUTTON;
5019 case 0x15da: // (blue)
5020 element = EL_CHAR_PLUS;
5023 case 0x15db: // (blue)
5024 element = EL_CHAR_MINUS;
5027 case 0x15dc: // (blue)
5028 element = EL_CHAR_APOSTROPHE;
5031 case 0x15dd: // (blue)
5032 element = EL_CHAR_PARENLEFT;
5035 case 0x15de: // (blue)
5036 element = EL_CHAR_PARENRIGHT;
5039 case 0x15df: // (green)
5040 element = EL_CHAR_A;
5043 case 0x15e0: // (green)
5044 element = EL_CHAR_B;
5047 case 0x15e1: // (green)
5048 element = EL_CHAR_C;
5051 case 0x15e2: // (green)
5052 element = EL_CHAR_D;
5055 case 0x15e3: // (green)
5056 element = EL_CHAR_E;
5059 case 0x15e4: // (green)
5060 element = EL_CHAR_F;
5063 case 0x15e5: // (green)
5064 element = EL_CHAR_G;
5067 case 0x15e6: // (green)
5068 element = EL_CHAR_H;
5071 case 0x15e7: // (green)
5072 element = EL_CHAR_I;
5075 case 0x15e8: // (green)
5076 element = EL_CHAR_J;
5079 case 0x15e9: // (green)
5080 element = EL_CHAR_K;
5083 case 0x15ea: // (green)
5084 element = EL_CHAR_L;
5087 case 0x15eb: // (green)
5088 element = EL_CHAR_M;
5091 case 0x15ec: // (green)
5092 element = EL_CHAR_N;
5095 case 0x15ed: // (green)
5096 element = EL_CHAR_O;
5099 case 0x15ee: // (green)
5100 element = EL_CHAR_P;
5103 case 0x15ef: // (green)
5104 element = EL_CHAR_Q;
5107 case 0x15f0: // (green)
5108 element = EL_CHAR_R;
5111 case 0x15f1: // (green)
5112 element = EL_CHAR_S;
5115 case 0x15f2: // (green)
5116 element = EL_CHAR_T;
5119 case 0x15f3: // (green)
5120 element = EL_CHAR_U;
5123 case 0x15f4: // (green)
5124 element = EL_CHAR_V;
5127 case 0x15f5: // (green)
5128 element = EL_CHAR_W;
5131 case 0x15f6: // (green)
5132 element = EL_CHAR_X;
5135 case 0x15f7: // (green)
5136 element = EL_CHAR_Y;
5139 case 0x15f8: // (green)
5140 element = EL_CHAR_Z;
5143 case 0x15f9: // (green)
5144 element = EL_CHAR_AUMLAUT;
5147 case 0x15fa: // (green)
5148 element = EL_CHAR_OUMLAUT;
5151 case 0x15fb: // (green)
5152 element = EL_CHAR_UUMLAUT;
5155 case 0x15fc: // (green)
5156 element = EL_CHAR_0;
5159 case 0x15fd: // (green)
5160 element = EL_CHAR_1;
5163 case 0x15fe: // (green)
5164 element = EL_CHAR_2;
5167 case 0x15ff: // (green)
5168 element = EL_CHAR_3;
5171 case 0x1600: // (green)
5172 element = EL_CHAR_4;
5175 case 0x1601: // (green)
5176 element = EL_CHAR_5;
5179 case 0x1602: // (green)
5180 element = EL_CHAR_6;
5183 case 0x1603: // (green)
5184 element = EL_CHAR_7;
5187 case 0x1604: // (green)
5188 element = EL_CHAR_8;
5191 case 0x1605: // (green)
5192 element = EL_CHAR_9;
5195 case 0x1606: // (green)
5196 element = EL_CHAR_PERIOD;
5199 case 0x1607: // (green)
5200 element = EL_CHAR_EXCLAM;
5203 case 0x1608: // (green)
5204 element = EL_CHAR_COLON;
5207 case 0x1609: // (green)
5208 element = EL_CHAR_LESS;
5211 case 0x160a: // (green)
5212 element = EL_CHAR_GREATER;
5215 case 0x160b: // (green)
5216 element = EL_CHAR_QUESTION;
5219 case 0x160c: // (green)
5220 element = EL_CHAR_COPYRIGHT;
5223 case 0x160d: // (green)
5224 element = EL_CHAR_UP;
5227 case 0x160e: // (green)
5228 element = EL_CHAR_DOWN;
5231 case 0x160f: // (green)
5232 element = EL_CHAR_BUTTON;
5235 case 0x1610: // (green)
5236 element = EL_CHAR_PLUS;
5239 case 0x1611: // (green)
5240 element = EL_CHAR_MINUS;
5243 case 0x1612: // (green)
5244 element = EL_CHAR_APOSTROPHE;
5247 case 0x1613: // (green)
5248 element = EL_CHAR_PARENLEFT;
5251 case 0x1614: // (green)
5252 element = EL_CHAR_PARENRIGHT;
5255 case 0x1615: // (blue steel)
5256 element = EL_STEEL_CHAR_A;
5259 case 0x1616: // (blue steel)
5260 element = EL_STEEL_CHAR_B;
5263 case 0x1617: // (blue steel)
5264 element = EL_STEEL_CHAR_C;
5267 case 0x1618: // (blue steel)
5268 element = EL_STEEL_CHAR_D;
5271 case 0x1619: // (blue steel)
5272 element = EL_STEEL_CHAR_E;
5275 case 0x161a: // (blue steel)
5276 element = EL_STEEL_CHAR_F;
5279 case 0x161b: // (blue steel)
5280 element = EL_STEEL_CHAR_G;
5283 case 0x161c: // (blue steel)
5284 element = EL_STEEL_CHAR_H;
5287 case 0x161d: // (blue steel)
5288 element = EL_STEEL_CHAR_I;
5291 case 0x161e: // (blue steel)
5292 element = EL_STEEL_CHAR_J;
5295 case 0x161f: // (blue steel)
5296 element = EL_STEEL_CHAR_K;
5299 case 0x1620: // (blue steel)
5300 element = EL_STEEL_CHAR_L;
5303 case 0x1621: // (blue steel)
5304 element = EL_STEEL_CHAR_M;
5307 case 0x1622: // (blue steel)
5308 element = EL_STEEL_CHAR_N;
5311 case 0x1623: // (blue steel)
5312 element = EL_STEEL_CHAR_O;
5315 case 0x1624: // (blue steel)
5316 element = EL_STEEL_CHAR_P;
5319 case 0x1625: // (blue steel)
5320 element = EL_STEEL_CHAR_Q;
5323 case 0x1626: // (blue steel)
5324 element = EL_STEEL_CHAR_R;
5327 case 0x1627: // (blue steel)
5328 element = EL_STEEL_CHAR_S;
5331 case 0x1628: // (blue steel)
5332 element = EL_STEEL_CHAR_T;
5335 case 0x1629: // (blue steel)
5336 element = EL_STEEL_CHAR_U;
5339 case 0x162a: // (blue steel)
5340 element = EL_STEEL_CHAR_V;
5343 case 0x162b: // (blue steel)
5344 element = EL_STEEL_CHAR_W;
5347 case 0x162c: // (blue steel)
5348 element = EL_STEEL_CHAR_X;
5351 case 0x162d: // (blue steel)
5352 element = EL_STEEL_CHAR_Y;
5355 case 0x162e: // (blue steel)
5356 element = EL_STEEL_CHAR_Z;
5359 case 0x162f: // (blue steel)
5360 element = EL_STEEL_CHAR_AUMLAUT;
5363 case 0x1630: // (blue steel)
5364 element = EL_STEEL_CHAR_OUMLAUT;
5367 case 0x1631: // (blue steel)
5368 element = EL_STEEL_CHAR_UUMLAUT;
5371 case 0x1632: // (blue steel)
5372 element = EL_STEEL_CHAR_0;
5375 case 0x1633: // (blue steel)
5376 element = EL_STEEL_CHAR_1;
5379 case 0x1634: // (blue steel)
5380 element = EL_STEEL_CHAR_2;
5383 case 0x1635: // (blue steel)
5384 element = EL_STEEL_CHAR_3;
5387 case 0x1636: // (blue steel)
5388 element = EL_STEEL_CHAR_4;
5391 case 0x1637: // (blue steel)
5392 element = EL_STEEL_CHAR_5;
5395 case 0x1638: // (blue steel)
5396 element = EL_STEEL_CHAR_6;
5399 case 0x1639: // (blue steel)
5400 element = EL_STEEL_CHAR_7;
5403 case 0x163a: // (blue steel)
5404 element = EL_STEEL_CHAR_8;
5407 case 0x163b: // (blue steel)
5408 element = EL_STEEL_CHAR_9;
5411 case 0x163c: // (blue steel)
5412 element = EL_STEEL_CHAR_PERIOD;
5415 case 0x163d: // (blue steel)
5416 element = EL_STEEL_CHAR_EXCLAM;
5419 case 0x163e: // (blue steel)
5420 element = EL_STEEL_CHAR_COLON;
5423 case 0x163f: // (blue steel)
5424 element = EL_STEEL_CHAR_LESS;
5427 case 0x1640: // (blue steel)
5428 element = EL_STEEL_CHAR_GREATER;
5431 case 0x1641: // (blue steel)
5432 element = EL_STEEL_CHAR_QUESTION;
5435 case 0x1642: // (blue steel)
5436 element = EL_STEEL_CHAR_COPYRIGHT;
5439 case 0x1643: // (blue steel)
5440 element = EL_STEEL_CHAR_UP;
5443 case 0x1644: // (blue steel)
5444 element = EL_STEEL_CHAR_DOWN;
5447 case 0x1645: // (blue steel)
5448 element = EL_STEEL_CHAR_BUTTON;
5451 case 0x1646: // (blue steel)
5452 element = EL_STEEL_CHAR_PLUS;
5455 case 0x1647: // (blue steel)
5456 element = EL_STEEL_CHAR_MINUS;
5459 case 0x1648: // (blue steel)
5460 element = EL_STEEL_CHAR_APOSTROPHE;
5463 case 0x1649: // (blue steel)
5464 element = EL_STEEL_CHAR_PARENLEFT;
5467 case 0x164a: // (blue steel)
5468 element = EL_STEEL_CHAR_PARENRIGHT;
5471 case 0x164b: // (green steel)
5472 element = EL_STEEL_CHAR_A;
5475 case 0x164c: // (green steel)
5476 element = EL_STEEL_CHAR_B;
5479 case 0x164d: // (green steel)
5480 element = EL_STEEL_CHAR_C;
5483 case 0x164e: // (green steel)
5484 element = EL_STEEL_CHAR_D;
5487 case 0x164f: // (green steel)
5488 element = EL_STEEL_CHAR_E;
5491 case 0x1650: // (green steel)
5492 element = EL_STEEL_CHAR_F;
5495 case 0x1651: // (green steel)
5496 element = EL_STEEL_CHAR_G;
5499 case 0x1652: // (green steel)
5500 element = EL_STEEL_CHAR_H;
5503 case 0x1653: // (green steel)
5504 element = EL_STEEL_CHAR_I;
5507 case 0x1654: // (green steel)
5508 element = EL_STEEL_CHAR_J;
5511 case 0x1655: // (green steel)
5512 element = EL_STEEL_CHAR_K;
5515 case 0x1656: // (green steel)
5516 element = EL_STEEL_CHAR_L;
5519 case 0x1657: // (green steel)
5520 element = EL_STEEL_CHAR_M;
5523 case 0x1658: // (green steel)
5524 element = EL_STEEL_CHAR_N;
5527 case 0x1659: // (green steel)
5528 element = EL_STEEL_CHAR_O;
5531 case 0x165a: // (green steel)
5532 element = EL_STEEL_CHAR_P;
5535 case 0x165b: // (green steel)
5536 element = EL_STEEL_CHAR_Q;
5539 case 0x165c: // (green steel)
5540 element = EL_STEEL_CHAR_R;
5543 case 0x165d: // (green steel)
5544 element = EL_STEEL_CHAR_S;
5547 case 0x165e: // (green steel)
5548 element = EL_STEEL_CHAR_T;
5551 case 0x165f: // (green steel)
5552 element = EL_STEEL_CHAR_U;
5555 case 0x1660: // (green steel)
5556 element = EL_STEEL_CHAR_V;
5559 case 0x1661: // (green steel)
5560 element = EL_STEEL_CHAR_W;
5563 case 0x1662: // (green steel)
5564 element = EL_STEEL_CHAR_X;
5567 case 0x1663: // (green steel)
5568 element = EL_STEEL_CHAR_Y;
5571 case 0x1664: // (green steel)
5572 element = EL_STEEL_CHAR_Z;
5575 case 0x1665: // (green steel)
5576 element = EL_STEEL_CHAR_AUMLAUT;
5579 case 0x1666: // (green steel)
5580 element = EL_STEEL_CHAR_OUMLAUT;
5583 case 0x1667: // (green steel)
5584 element = EL_STEEL_CHAR_UUMLAUT;
5587 case 0x1668: // (green steel)
5588 element = EL_STEEL_CHAR_0;
5591 case 0x1669: // (green steel)
5592 element = EL_STEEL_CHAR_1;
5595 case 0x166a: // (green steel)
5596 element = EL_STEEL_CHAR_2;
5599 case 0x166b: // (green steel)
5600 element = EL_STEEL_CHAR_3;
5603 case 0x166c: // (green steel)
5604 element = EL_STEEL_CHAR_4;
5607 case 0x166d: // (green steel)
5608 element = EL_STEEL_CHAR_5;
5611 case 0x166e: // (green steel)
5612 element = EL_STEEL_CHAR_6;
5615 case 0x166f: // (green steel)
5616 element = EL_STEEL_CHAR_7;
5619 case 0x1670: // (green steel)
5620 element = EL_STEEL_CHAR_8;
5623 case 0x1671: // (green steel)
5624 element = EL_STEEL_CHAR_9;
5627 case 0x1672: // (green steel)
5628 element = EL_STEEL_CHAR_PERIOD;
5631 case 0x1673: // (green steel)
5632 element = EL_STEEL_CHAR_EXCLAM;
5635 case 0x1674: // (green steel)
5636 element = EL_STEEL_CHAR_COLON;
5639 case 0x1675: // (green steel)
5640 element = EL_STEEL_CHAR_LESS;
5643 case 0x1676: // (green steel)
5644 element = EL_STEEL_CHAR_GREATER;
5647 case 0x1677: // (green steel)
5648 element = EL_STEEL_CHAR_QUESTION;
5651 case 0x1678: // (green steel)
5652 element = EL_STEEL_CHAR_COPYRIGHT;
5655 case 0x1679: // (green steel)
5656 element = EL_STEEL_CHAR_UP;
5659 case 0x167a: // (green steel)
5660 element = EL_STEEL_CHAR_DOWN;
5663 case 0x167b: // (green steel)
5664 element = EL_STEEL_CHAR_BUTTON;
5667 case 0x167c: // (green steel)
5668 element = EL_STEEL_CHAR_PLUS;
5671 case 0x167d: // (green steel)
5672 element = EL_STEEL_CHAR_MINUS;
5675 case 0x167e: // (green steel)
5676 element = EL_STEEL_CHAR_APOSTROPHE;
5679 case 0x167f: // (green steel)
5680 element = EL_STEEL_CHAR_PARENLEFT;
5683 case 0x1680: // (green steel)
5684 element = EL_STEEL_CHAR_PARENRIGHT;
5687 case 0x1681: // gate (red)
5688 element = EL_EM_GATE_1;
5691 case 0x1682: // secret gate (red)
5692 element = EL_EM_GATE_1_GRAY;
5695 case 0x1683: // gate (yellow)
5696 element = EL_EM_GATE_2;
5699 case 0x1684: // secret gate (yellow)
5700 element = EL_EM_GATE_2_GRAY;
5703 case 0x1685: // gate (blue)
5704 element = EL_EM_GATE_4;
5707 case 0x1686: // secret gate (blue)
5708 element = EL_EM_GATE_4_GRAY;
5711 case 0x1687: // gate (green)
5712 element = EL_EM_GATE_3;
5715 case 0x1688: // secret gate (green)
5716 element = EL_EM_GATE_3_GRAY;
5719 case 0x1689: // gate (white)
5720 element = EL_DC_GATE_WHITE;
5723 case 0x168a: // secret gate (white)
5724 element = EL_DC_GATE_WHITE_GRAY;
5727 case 0x168b: // secret gate (no key)
5728 element = EL_DC_GATE_FAKE_GRAY;
5732 element = EL_ROBOT_WHEEL;
5736 element = EL_DC_TIMEGATE_SWITCH;
5740 element = EL_ACID_POOL_BOTTOM;
5744 element = EL_ACID_POOL_TOPLEFT;
5748 element = EL_ACID_POOL_TOPRIGHT;
5752 element = EL_ACID_POOL_BOTTOMLEFT;
5756 element = EL_ACID_POOL_BOTTOMRIGHT;
5760 element = EL_STEELWALL;
5764 element = EL_STEELWALL_SLIPPERY;
5767 case 0x1695: // steel wall (not round)
5768 element = EL_STEELWALL;
5771 case 0x1696: // steel wall (left)
5772 element = EL_DC_STEELWALL_1_LEFT;
5775 case 0x1697: // steel wall (bottom)
5776 element = EL_DC_STEELWALL_1_BOTTOM;
5779 case 0x1698: // steel wall (right)
5780 element = EL_DC_STEELWALL_1_RIGHT;
5783 case 0x1699: // steel wall (top)
5784 element = EL_DC_STEELWALL_1_TOP;
5787 case 0x169a: // steel wall (left/bottom)
5788 element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5791 case 0x169b: // steel wall (right/bottom)
5792 element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5795 case 0x169c: // steel wall (right/top)
5796 element = EL_DC_STEELWALL_1_TOPRIGHT;
5799 case 0x169d: // steel wall (left/top)
5800 element = EL_DC_STEELWALL_1_TOPLEFT;
5803 case 0x169e: // steel wall (right/bottom small)
5804 element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5807 case 0x169f: // steel wall (left/bottom small)
5808 element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5811 case 0x16a0: // steel wall (right/top small)
5812 element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5815 case 0x16a1: // steel wall (left/top small)
5816 element = EL_DC_STEELWALL_1_TOPLEFT_2;
5819 case 0x16a2: // steel wall (left/right)
5820 element = EL_DC_STEELWALL_1_VERTICAL;
5823 case 0x16a3: // steel wall (top/bottom)
5824 element = EL_DC_STEELWALL_1_HORIZONTAL;
5827 case 0x16a4: // steel wall 2 (left end)
5828 element = EL_DC_STEELWALL_2_LEFT;
5831 case 0x16a5: // steel wall 2 (right end)
5832 element = EL_DC_STEELWALL_2_RIGHT;
5835 case 0x16a6: // steel wall 2 (top end)
5836 element = EL_DC_STEELWALL_2_TOP;
5839 case 0x16a7: // steel wall 2 (bottom end)
5840 element = EL_DC_STEELWALL_2_BOTTOM;
5843 case 0x16a8: // steel wall 2 (left/right)
5844 element = EL_DC_STEELWALL_2_HORIZONTAL;
5847 case 0x16a9: // steel wall 2 (up/down)
5848 element = EL_DC_STEELWALL_2_VERTICAL;
5851 case 0x16aa: // steel wall 2 (mid)
5852 element = EL_DC_STEELWALL_2_MIDDLE;
5856 element = EL_SIGN_EXCLAMATION;
5860 element = EL_SIGN_RADIOACTIVITY;
5864 element = EL_SIGN_STOP;
5868 element = EL_SIGN_WHEELCHAIR;
5872 element = EL_SIGN_PARKING;
5876 element = EL_SIGN_NO_ENTRY;
5880 element = EL_SIGN_HEART;
5884 element = EL_SIGN_GIVE_WAY;
5888 element = EL_SIGN_ENTRY_FORBIDDEN;
5892 element = EL_SIGN_EMERGENCY_EXIT;
5896 element = EL_SIGN_YIN_YANG;
5900 element = EL_WALL_EMERALD;
5904 element = EL_WALL_DIAMOND;
5908 element = EL_WALL_PEARL;
5912 element = EL_WALL_CRYSTAL;
5916 element = EL_INVISIBLE_WALL;
5920 element = EL_INVISIBLE_STEELWALL;
5924 // EL_INVISIBLE_SAND
5927 element = EL_LIGHT_SWITCH;
5931 element = EL_ENVELOPE_1;
5935 if (element >= 0x0117 && element <= 0x036e) // (?)
5936 element = EL_DIAMOND;
5937 else if (element >= 0x042d && element <= 0x0684) // (?)
5938 element = EL_EMERALD;
5939 else if (element >= 0x157c && element <= 0x158b)
5941 else if (element >= 0x1590 && element <= 0x159f)
5942 element = EL_DC_LANDMINE;
5943 else if (element >= 0x16bc && element <= 0x16cb)
5944 element = EL_INVISIBLE_SAND;
5947 Warn("unknown Diamond Caves element 0x%04x", element);
5949 element = EL_UNKNOWN;
5954 return getMappedElement(element);
5957 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
5959 byte header[DC_LEVEL_HEADER_SIZE];
5961 int envelope_header_pos = 62;
5962 int envelope_content_pos = 94;
5963 int level_name_pos = 251;
5964 int level_author_pos = 292;
5965 int envelope_header_len;
5966 int envelope_content_len;
5968 int level_author_len;
5970 int num_yamyam_contents;
5973 getDecodedWord_DC(0, TRUE); // initialize DC2 decoding engine
5975 for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5977 unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5979 header[i * 2 + 0] = header_word >> 8;
5980 header[i * 2 + 1] = header_word & 0xff;
5983 // read some values from level header to check level decoding integrity
5984 fieldx = header[6] | (header[7] << 8);
5985 fieldy = header[8] | (header[9] << 8);
5986 num_yamyam_contents = header[60] | (header[61] << 8);
5988 // do some simple sanity checks to ensure that level was correctly decoded
5989 if (fieldx < 1 || fieldx > 256 ||
5990 fieldy < 1 || fieldy > 256 ||
5991 num_yamyam_contents < 1 || num_yamyam_contents > 8)
5993 level->no_valid_file = TRUE;
5995 Warn("cannot decode level from stream -- using empty level");
6000 // maximum envelope header size is 31 bytes
6001 envelope_header_len = header[envelope_header_pos];
6002 // maximum envelope content size is 110 (156?) bytes
6003 envelope_content_len = header[envelope_content_pos];
6005 // maximum level title size is 40 bytes
6006 level_name_len = MIN(header[level_name_pos], MAX_LEVEL_NAME_LEN);
6007 // maximum level author size is 30 (51?) bytes
6008 level_author_len = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6012 for (i = 0; i < envelope_header_len; i++)
6013 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6014 level->envelope[0].text[envelope_size++] =
6015 header[envelope_header_pos + 1 + i];
6017 if (envelope_header_len > 0 && envelope_content_len > 0)
6019 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6020 level->envelope[0].text[envelope_size++] = '\n';
6021 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6022 level->envelope[0].text[envelope_size++] = '\n';
6025 for (i = 0; i < envelope_content_len; i++)
6026 if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6027 level->envelope[0].text[envelope_size++] =
6028 header[envelope_content_pos + 1 + i];
6030 level->envelope[0].text[envelope_size] = '\0';
6032 level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6033 level->envelope[0].ysize = 10;
6034 level->envelope[0].autowrap = TRUE;
6035 level->envelope[0].centered = TRUE;
6037 for (i = 0; i < level_name_len; i++)
6038 level->name[i] = header[level_name_pos + 1 + i];
6039 level->name[level_name_len] = '\0';
6041 for (i = 0; i < level_author_len; i++)
6042 level->author[i] = header[level_author_pos + 1 + i];
6043 level->author[level_author_len] = '\0';
6045 num_yamyam_contents = header[60] | (header[61] << 8);
6046 level->num_yamyam_contents =
6047 MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6049 for (i = 0; i < num_yamyam_contents; i++)
6051 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6053 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6054 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6056 if (i < MAX_ELEMENT_CONTENTS)
6057 level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6061 fieldx = header[6] | (header[7] << 8);
6062 fieldy = header[8] | (header[9] << 8);
6063 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6064 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6066 for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6068 unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6069 int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6071 if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6072 level->field[x][y] = getMappedElement_DC(element_dc);
6075 x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6076 y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6077 level->field[x][y] = EL_PLAYER_1;
6079 x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6080 y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6081 level->field[x][y] = EL_PLAYER_2;
6083 level->gems_needed = header[18] | (header[19] << 8);
6085 level->score[SC_EMERALD] = header[20] | (header[21] << 8);
6086 level->score[SC_DIAMOND] = header[22] | (header[23] << 8);
6087 level->score[SC_PEARL] = header[24] | (header[25] << 8);
6088 level->score[SC_CRYSTAL] = header[26] | (header[27] << 8);
6089 level->score[SC_NUT] = header[28] | (header[29] << 8);
6090 level->score[SC_ROBOT] = header[30] | (header[31] << 8);
6091 level->score[SC_SPACESHIP] = header[32] | (header[33] << 8);
6092 level->score[SC_BUG] = header[34] | (header[35] << 8);
6093 level->score[SC_YAMYAM] = header[36] | (header[37] << 8);
6094 level->score[SC_DYNAMITE] = header[38] | (header[39] << 8);
6095 level->score[SC_KEY] = header[40] | (header[41] << 8);
6096 level->score[SC_TIME_BONUS] = header[42] | (header[43] << 8);
6098 level->time = header[44] | (header[45] << 8);
6100 level->amoeba_speed = header[46] | (header[47] << 8);
6101 level->time_light = header[48] | (header[49] << 8);
6102 level->time_timegate = header[50] | (header[51] << 8);
6103 level->time_wheel = header[52] | (header[53] << 8);
6104 level->time_magic_wall = header[54] | (header[55] << 8);
6105 level->extra_time = header[56] | (header[57] << 8);
6106 level->shield_normal_time = header[58] | (header[59] << 8);
6108 // shield and extra time elements do not have a score
6109 level->score[SC_SHIELD] = 0;
6110 level->extra_time_score = 0;
6112 // set time for normal and deadly shields to the same value
6113 level->shield_deadly_time = level->shield_normal_time;
6115 // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6116 // can slip down from flat walls, like normal walls and steel walls
6117 level->em_slippery_gems = TRUE;
6119 // time score is counted for each 10 seconds left in Diamond Caves levels
6120 level->time_score_base = 10;
6123 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6124 struct LevelFileInfo *level_file_info,
6125 boolean level_info_only)
6127 char *filename = level_file_info->filename;
6129 int num_magic_bytes = 8;
6130 char magic_bytes[num_magic_bytes + 1];
6131 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6133 if (!(file = openFile(filename, MODE_READ)))
6135 level->no_valid_file = TRUE;
6137 if (!level_info_only)
6138 Warn("cannot read level '%s' -- using empty level", filename);
6143 // fseek(file, 0x0000, SEEK_SET);
6145 if (level_file_info->packed)
6147 // read "magic bytes" from start of file
6148 if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6149 magic_bytes[0] = '\0';
6151 // check "magic bytes" for correct file format
6152 if (!strPrefix(magic_bytes, "DC2"))
6154 level->no_valid_file = TRUE;
6156 Warn("unknown DC level file '%s' -- using empty level", filename);
6161 if (strPrefix(magic_bytes, "DC2Win95") ||
6162 strPrefix(magic_bytes, "DC2Win98"))
6164 int position_first_level = 0x00fa;
6165 int extra_bytes = 4;
6168 // advance file stream to first level inside the level package
6169 skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6171 // each block of level data is followed by block of non-level data
6172 num_levels_to_skip *= 2;
6174 // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6175 while (num_levels_to_skip >= 0)
6177 // advance file stream to next level inside the level package
6178 if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6180 level->no_valid_file = TRUE;
6182 Warn("cannot fseek in file '%s' -- using empty level", filename);
6187 // skip apparently unused extra bytes following each level
6188 ReadUnusedBytesFromFile(file, extra_bytes);
6190 // read size of next level in level package
6191 skip_bytes = getFile32BitLE(file);
6193 num_levels_to_skip--;
6198 level->no_valid_file = TRUE;
6200 Warn("unknown DC2 level file '%s' -- using empty level", filename);
6206 LoadLevelFromFileStream_DC(file, level);
6212 // ----------------------------------------------------------------------------
6213 // functions for loading SB level
6214 // ----------------------------------------------------------------------------
6216 int getMappedElement_SB(int element_ascii, boolean use_ces)
6224 sb_element_mapping[] =
6226 { ' ', EL_EMPTY, EL_CUSTOM_1 }, // floor (space)
6227 { '#', EL_STEELWALL, EL_CUSTOM_2 }, // wall
6228 { '@', EL_PLAYER_1, EL_CUSTOM_3 }, // player
6229 { '$', EL_SOKOBAN_OBJECT, EL_CUSTOM_4 }, // box
6230 { '.', EL_SOKOBAN_FIELD_EMPTY, EL_CUSTOM_5 }, // goal square
6231 { '*', EL_SOKOBAN_FIELD_FULL, EL_CUSTOM_6 }, // box on goal square
6232 { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 }, // player on goal square
6233 { '_', EL_INVISIBLE_STEELWALL, EL_FROM_LEVEL_TEMPLATE }, // floor beyond border
6240 for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6241 if (element_ascii == sb_element_mapping[i].ascii)
6242 return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6244 return EL_UNDEFINED;
6247 static void SetLevelSettings_SB(struct LevelInfo *level)
6251 level->use_step_counter = TRUE;
6254 level->score[SC_TIME_BONUS] = 0;
6255 level->time_score_base = 1;
6256 level->rate_time_over_score = TRUE;
6259 level->auto_exit_sokoban = TRUE;
6262 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6263 struct LevelFileInfo *level_file_info,
6264 boolean level_info_only)
6266 char *filename = level_file_info->filename;
6267 char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6268 char last_comment[MAX_LINE_LEN];
6269 char level_name[MAX_LINE_LEN];
6272 int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6273 boolean read_continued_line = FALSE;
6274 boolean reading_playfield = FALSE;
6275 boolean got_valid_playfield_line = FALSE;
6276 boolean invalid_playfield_char = FALSE;
6277 boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6278 int file_level_nr = 0;
6279 int x = 0, y = 0; // initialized to make compilers happy
6281 last_comment[0] = '\0';
6282 level_name[0] = '\0';
6284 if (!(file = openFile(filename, MODE_READ)))
6286 level->no_valid_file = TRUE;
6288 if (!level_info_only)
6289 Warn("cannot read level '%s' -- using empty level", filename);
6294 while (!checkEndOfFile(file))
6296 // level successfully read, but next level may follow here
6297 if (!got_valid_playfield_line && reading_playfield)
6299 // read playfield from single level file -- skip remaining file
6300 if (!level_file_info->packed)
6303 if (file_level_nr >= num_levels_to_skip)
6308 last_comment[0] = '\0';
6309 level_name[0] = '\0';
6311 reading_playfield = FALSE;
6314 got_valid_playfield_line = FALSE;
6316 // read next line of input file
6317 if (!getStringFromFile(file, line, MAX_LINE_LEN))
6320 // cut trailing line break (this can be newline and/or carriage return)
6321 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6322 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6325 // copy raw input line for later use (mainly debugging output)
6326 strcpy(line_raw, line);
6328 if (read_continued_line)
6330 // append new line to existing line, if there is enough space
6331 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6332 strcat(previous_line, line_ptr);
6334 strcpy(line, previous_line); // copy storage buffer to line
6336 read_continued_line = FALSE;
6339 // if the last character is '\', continue at next line
6340 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6342 line[strlen(line) - 1] = '\0'; // cut off trailing backslash
6343 strcpy(previous_line, line); // copy line to storage buffer
6345 read_continued_line = TRUE;
6351 if (line[0] == '\0')
6354 // extract comment text from comment line
6357 for (line_ptr = line; *line_ptr; line_ptr++)
6358 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6361 strcpy(last_comment, line_ptr);
6366 // extract level title text from line containing level title
6367 if (line[0] == '\'')
6369 strcpy(level_name, &line[1]);
6371 if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6372 level_name[strlen(level_name) - 1] = '\0';
6377 // skip lines containing only spaces (or empty lines)
6378 for (line_ptr = line; *line_ptr; line_ptr++)
6379 if (*line_ptr != ' ')
6381 if (*line_ptr == '\0')
6384 // at this point, we have found a line containing part of a playfield
6386 got_valid_playfield_line = TRUE;
6388 if (!reading_playfield)
6390 reading_playfield = TRUE;
6391 invalid_playfield_char = FALSE;
6393 for (x = 0; x < MAX_LEV_FIELDX; x++)
6394 for (y = 0; y < MAX_LEV_FIELDY; y++)
6395 level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6400 // start with topmost tile row
6404 // skip playfield line if larger row than allowed
6405 if (y >= MAX_LEV_FIELDY)
6408 // start with leftmost tile column
6411 // read playfield elements from line
6412 for (line_ptr = line; *line_ptr; line_ptr++)
6414 int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6416 // stop parsing playfield line if larger column than allowed
6417 if (x >= MAX_LEV_FIELDX)
6420 if (mapped_sb_element == EL_UNDEFINED)
6422 invalid_playfield_char = TRUE;
6427 level->field[x][y] = mapped_sb_element;
6429 // continue with next tile column
6432 level->fieldx = MAX(x, level->fieldx);
6435 if (invalid_playfield_char)
6437 // if first playfield line, treat invalid lines as comment lines
6439 reading_playfield = FALSE;
6444 // continue with next tile row
6452 level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6453 level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6455 if (!reading_playfield)
6457 level->no_valid_file = TRUE;
6459 Warn("cannot read level '%s' -- using empty level", filename);
6464 if (*level_name != '\0')
6466 strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
6467 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6469 else if (*last_comment != '\0')
6471 strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
6472 level->name[MAX_LEVEL_NAME_LEN] = '\0';
6476 sprintf(level->name, "--> Level %d <--", level_file_info->nr);
6479 // set all empty fields beyond the border walls to invisible steel wall
6480 for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
6482 if ((x == 0 || x == level->fieldx - 1 ||
6483 y == 0 || y == level->fieldy - 1) &&
6484 level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
6485 FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
6486 level->field, level->fieldx, level->fieldy);
6489 // set special level settings for Sokoban levels
6490 SetLevelSettings_SB(level);
6492 if (load_xsb_to_ces)
6494 // special global settings can now be set in level template
6495 level->use_custom_template = TRUE;
6500 // -------------------------------------------------------------------------
6501 // functions for handling native levels
6502 // -------------------------------------------------------------------------
6504 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
6505 struct LevelFileInfo *level_file_info,
6506 boolean level_info_only)
6510 // determine position of requested level inside level package
6511 if (level_file_info->packed)
6512 pos = level_file_info->nr - leveldir_current->first_level;
6514 if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
6515 level->no_valid_file = TRUE;
6518 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
6519 struct LevelFileInfo *level_file_info,
6520 boolean level_info_only)
6522 if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
6523 level->no_valid_file = TRUE;
6526 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
6527 struct LevelFileInfo *level_file_info,
6528 boolean level_info_only)
6532 // determine position of requested level inside level package
6533 if (level_file_info->packed)
6534 pos = level_file_info->nr - leveldir_current->first_level;
6536 if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
6537 level->no_valid_file = TRUE;
6540 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
6541 struct LevelFileInfo *level_file_info,
6542 boolean level_info_only)
6544 if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
6545 level->no_valid_file = TRUE;
6548 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
6550 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6551 CopyNativeLevel_RND_to_BD(level);
6552 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6553 CopyNativeLevel_RND_to_EM(level);
6554 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6555 CopyNativeLevel_RND_to_SP(level);
6556 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6557 CopyNativeLevel_RND_to_MM(level);
6560 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
6562 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6563 CopyNativeLevel_BD_to_RND(level);
6564 else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
6565 CopyNativeLevel_EM_to_RND(level);
6566 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6567 CopyNativeLevel_SP_to_RND(level);
6568 else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
6569 CopyNativeLevel_MM_to_RND(level);
6572 void SaveNativeLevel(struct LevelInfo *level)
6574 // saving native level files only supported for some game engines
6575 if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
6576 level->game_engine_type != GAME_ENGINE_TYPE_SP)
6579 char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
6580 level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
6581 char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
6582 char *filename = getLevelFilenameFromBasename(basename);
6584 if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
6587 boolean success = FALSE;
6589 if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
6591 CopyNativeLevel_RND_to_BD(level);
6592 // CopyNativeTape_RND_to_BD(level);
6594 success = SaveNativeLevel_BD(filename);
6596 else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
6598 CopyNativeLevel_RND_to_SP(level);
6599 CopyNativeTape_RND_to_SP(level);
6601 success = SaveNativeLevel_SP(filename);
6605 Request("Native level file saved!", REQ_CONFIRM);
6607 Request("Failed to save native level file!", REQ_CONFIRM);
6611 // ----------------------------------------------------------------------------
6612 // functions for loading generic level
6613 // ----------------------------------------------------------------------------
6615 static void LoadLevelFromFileInfo(struct LevelInfo *level,
6616 struct LevelFileInfo *level_file_info,
6617 boolean level_info_only)
6619 // always start with reliable default values
6620 setLevelInfoToDefaults(level, level_info_only, TRUE);
6622 switch (level_file_info->type)
6624 case LEVEL_FILE_TYPE_RND:
6625 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6628 case LEVEL_FILE_TYPE_BD:
6629 LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
6630 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6633 case LEVEL_FILE_TYPE_EM:
6634 LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
6635 level->game_engine_type = GAME_ENGINE_TYPE_EM;
6638 case LEVEL_FILE_TYPE_SP:
6639 LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
6640 level->game_engine_type = GAME_ENGINE_TYPE_SP;
6643 case LEVEL_FILE_TYPE_MM:
6644 LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
6645 level->game_engine_type = GAME_ENGINE_TYPE_MM;
6648 case LEVEL_FILE_TYPE_DC:
6649 LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
6652 case LEVEL_FILE_TYPE_SB:
6653 LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
6657 LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
6661 // if level file is invalid, restore level structure to default values
6662 if (level->no_valid_file)
6663 setLevelInfoToDefaults(level, level_info_only, FALSE);
6665 if (check_special_flags("use_native_bd_game_engine"))
6666 level->game_engine_type = GAME_ENGINE_TYPE_BD;
6668 if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
6669 level->game_engine_type = GAME_ENGINE_TYPE_RND;
6671 if (level_file_info->type != LEVEL_FILE_TYPE_RND)
6672 CopyNativeLevel_Native_to_RND(level);
6675 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
6677 static struct LevelFileInfo level_file_info;
6679 // always start with reliable default values
6680 setFileInfoToDefaults(&level_file_info);
6682 level_file_info.nr = 0; // unknown level number
6683 level_file_info.type = LEVEL_FILE_TYPE_RND; // no others supported yet
6685 setString(&level_file_info.filename, filename);
6687 LoadLevelFromFileInfo(level, &level_file_info, FALSE);
6690 static void LoadLevel_InitVersion(struct LevelInfo *level)
6694 if (leveldir_current == NULL) // only when dumping level
6697 // all engine modifications also valid for levels which use latest engine
6698 if (level->game_version < VERSION_IDENT(3,2,0,5))
6700 // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
6701 level->time_score_base = 10;
6704 if (leveldir_current->latest_engine)
6706 // ---------- use latest game engine --------------------------------------
6708 /* For all levels which are forced to use the latest game engine version
6709 (normally all but user contributed, private and undefined levels), set
6710 the game engine version to the actual version; this allows for actual
6711 corrections in the game engine to take effect for existing, converted
6712 levels (from "classic" or other existing games) to make the emulation
6713 of the corresponding game more accurate, while (hopefully) not breaking
6714 existing levels created from other players. */
6716 level->game_version = GAME_VERSION_ACTUAL;
6718 /* Set special EM style gems behaviour: EM style gems slip down from
6719 normal, steel and growing wall. As this is a more fundamental change,
6720 it seems better to set the default behaviour to "off" (as it is more
6721 natural) and make it configurable in the level editor (as a property
6722 of gem style elements). Already existing converted levels (neither
6723 private nor contributed levels) are changed to the new behaviour. */
6725 if (level->file_version < FILE_VERSION_2_0)
6726 level->em_slippery_gems = TRUE;
6731 // ---------- use game engine the level was created with --------------------
6733 /* For all levels which are not forced to use the latest game engine
6734 version (normally user contributed, private and undefined levels),
6735 use the version of the game engine the levels were created for.
6737 Since 2.0.1, the game engine version is now directly stored
6738 in the level file (chunk "VERS"), so there is no need anymore
6739 to set the game version from the file version (except for old,
6740 pre-2.0 levels, where the game version is still taken from the
6741 file format version used to store the level -- see above). */
6743 // player was faster than enemies in 1.0.0 and before
6744 if (level->file_version == FILE_VERSION_1_0)
6745 for (i = 0; i < MAX_PLAYERS; i++)
6746 level->initial_player_stepsize[i] = STEPSIZE_FAST;
6748 // default behaviour for EM style gems was "slippery" only in 2.0.1
6749 if (level->game_version == VERSION_IDENT(2,0,1,0))
6750 level->em_slippery_gems = TRUE;
6752 // springs could be pushed over pits before (pre-release version) 2.2.0
6753 if (level->game_version < VERSION_IDENT(2,2,0,0))
6754 level->use_spring_bug = TRUE;
6756 if (level->game_version < VERSION_IDENT(3,2,0,5))
6758 // time orb caused limited time in endless time levels before 3.2.0-5
6759 level->use_time_orb_bug = TRUE;
6761 // default behaviour for snapping was "no snap delay" before 3.2.0-5
6762 level->block_snap_field = FALSE;
6764 // extra time score was same value as time left score before 3.2.0-5
6765 level->extra_time_score = level->score[SC_TIME_BONUS];
6768 if (level->game_version < VERSION_IDENT(3,2,0,7))
6770 // default behaviour for snapping was "not continuous" before 3.2.0-7
6771 level->continuous_snapping = FALSE;
6774 // only few elements were able to actively move into acid before 3.1.0
6775 // trigger settings did not exist before 3.1.0; set to default "any"
6776 if (level->game_version < VERSION_IDENT(3,1,0,0))
6778 // correct "can move into acid" settings (all zero in old levels)
6780 level->can_move_into_acid_bits = 0; // nothing can move into acid
6781 level->dont_collide_with_bits = 0; // nothing is deadly when colliding
6783 setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
6784 setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6785 setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
6786 setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
6788 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6789 SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6791 // correct trigger settings (stored as zero == "none" in old levels)
6793 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6795 int element = EL_CUSTOM_START + i;
6796 struct ElementInfo *ei = &element_info[element];
6798 for (j = 0; j < ei->num_change_pages; j++)
6800 struct ElementChangeInfo *change = &ei->change_page[j];
6802 change->trigger_player = CH_PLAYER_ANY;
6803 change->trigger_page = CH_PAGE_ANY;
6808 // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
6810 int element = EL_CUSTOM_256;
6811 struct ElementInfo *ei = &element_info[element];
6812 struct ElementChangeInfo *change = &ei->change_page[0];
6814 /* This is needed to fix a problem that was caused by a bugfix in function
6815 game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6816 when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6817 not replace walkable elements, but instead just placed the player on it,
6818 without placing the Sokoban field under the player). Unfortunately, this
6819 breaks "Snake Bite" style levels when the snake is halfway through a door
6820 that just closes (the snake head is still alive and can be moved in this
6821 case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6822 player (without Sokoban element) which then gets killed as designed). */
6824 if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6825 strncmp(ei->description, "pause b4 death", 14) == 0) &&
6826 change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6827 change->target_element = EL_PLAYER_1;
6830 // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
6831 if (level->game_version < VERSION_IDENT(3,2,5,0))
6833 /* This is needed to fix a problem that was caused by a bugfix in function
6834 game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6835 corrects the behaviour when a custom element changes to another custom
6836 element with a higher element number that has change actions defined.
6837 Normally, only one change per frame is allowed for custom elements.
6838 Therefore, it is checked if a custom element already changed in the
6839 current frame; if it did, subsequent changes are suppressed.
6840 Unfortunately, this is only checked for element changes, but not for
6841 change actions, which are still executed. As the function above loops
6842 through all custom elements from lower to higher, an element change
6843 resulting in a lower CE number won't be checked again, while a target
6844 element with a higher number will also be checked, and potential change
6845 actions will get executed for this CE, too (which is wrong), while
6846 further changes are ignored (which is correct). As this bugfix breaks
6847 Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6848 few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6849 behaviour for existing levels and tapes that make use of this bug */
6851 level->use_action_after_change_bug = TRUE;
6854 // not centering level after relocating player was default only in 3.2.3
6855 if (level->game_version == VERSION_IDENT(3,2,3,0)) // (no pre-releases)
6856 level->shifted_relocation = TRUE;
6858 // EM style elements always chain-exploded in R'n'D engine before 3.2.6
6859 if (level->game_version < VERSION_IDENT(3,2,6,0))
6860 level->em_explodes_by_fire = TRUE;
6862 // levels were solved by the first player entering an exit up to 4.1.0.0
6863 if (level->game_version <= VERSION_IDENT(4,1,0,0))
6864 level->solved_by_one_player = TRUE;
6866 // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
6867 if (level->game_version < VERSION_IDENT(4,1,1,1))
6868 level->use_life_bugs = TRUE;
6870 // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
6871 if (level->game_version < VERSION_IDENT(4,1,1,1))
6872 level->sb_objects_needed = FALSE;
6874 // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
6875 if (level->game_version <= VERSION_IDENT(4,2,2,0))
6876 level->finish_dig_collect = FALSE;
6878 // CE changing to player was kept under the player if walkable up to 4.2.3.1
6879 if (level->game_version <= VERSION_IDENT(4,2,3,1))
6880 level->keep_walkable_ce = TRUE;
6883 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
6885 boolean is_sokoban_level = TRUE; // unless non-Sokoban elements found
6888 // check if this level is (not) a Sokoban level
6889 for (y = 0; y < level->fieldy; y++)
6890 for (x = 0; x < level->fieldx; x++)
6891 if (!IS_SB_ELEMENT(Tile[x][y]))
6892 is_sokoban_level = FALSE;
6894 if (is_sokoban_level)
6896 // set special level settings for Sokoban levels
6897 SetLevelSettings_SB(level);
6901 static void LoadLevel_InitSettings(struct LevelInfo *level)
6903 // adjust level settings for (non-native) Sokoban-style levels
6904 LoadLevel_InitSettings_SB(level);
6906 // rename levels with title "nameless level" or if renaming is forced
6907 if (leveldir_current->empty_level_name != NULL &&
6908 (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
6909 leveldir_current->force_level_name))
6910 snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
6911 leveldir_current->empty_level_name, level_nr);
6914 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
6918 // map elements that have changed in newer versions
6919 level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6920 level->game_version);
6921 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6922 for (x = 0; x < 3; x++)
6923 for (y = 0; y < 3; y++)
6924 level->yamyam_content[i].e[x][y] =
6925 getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6926 level->game_version);
6930 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
6934 // map custom element change events that have changed in newer versions
6935 // (these following values were accidentally changed in version 3.0.1)
6936 // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
6937 if (level->game_version <= VERSION_IDENT(3,0,0,0))
6939 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6941 int element = EL_CUSTOM_START + i;
6943 // order of checking and copying events to be mapped is important
6944 // (do not change the start and end value -- they are constant)
6945 for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6947 if (HAS_CHANGE_EVENT(element, j - 2))
6949 SET_CHANGE_EVENT(element, j - 2, FALSE);
6950 SET_CHANGE_EVENT(element, j, TRUE);
6954 // order of checking and copying events to be mapped is important
6955 // (do not change the start and end value -- they are constant)
6956 for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6958 if (HAS_CHANGE_EVENT(element, j - 1))
6960 SET_CHANGE_EVENT(element, j - 1, FALSE);
6961 SET_CHANGE_EVENT(element, j, TRUE);
6967 // initialize "can_change" field for old levels with only one change page
6968 if (level->game_version <= VERSION_IDENT(3,0,2,0))
6970 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6972 int element = EL_CUSTOM_START + i;
6974 if (CAN_CHANGE(element))
6975 element_info[element].change->can_change = TRUE;
6979 // correct custom element values (for old levels without these options)
6980 if (level->game_version < VERSION_IDENT(3,1,1,0))
6982 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6984 int element = EL_CUSTOM_START + i;
6985 struct ElementInfo *ei = &element_info[element];
6987 if (ei->access_direction == MV_NO_DIRECTION)
6988 ei->access_direction = MV_ALL_DIRECTIONS;
6992 // correct custom element values (fix invalid values for all versions)
6995 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6997 int element = EL_CUSTOM_START + i;
6998 struct ElementInfo *ei = &element_info[element];
7000 for (j = 0; j < ei->num_change_pages; j++)
7002 struct ElementChangeInfo *change = &ei->change_page[j];
7004 if (change->trigger_player == CH_PLAYER_NONE)
7005 change->trigger_player = CH_PLAYER_ANY;
7007 if (change->trigger_side == CH_SIDE_NONE)
7008 change->trigger_side = CH_SIDE_ANY;
7013 // initialize "can_explode" field for old levels which did not store this
7014 // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7015 if (level->game_version <= VERSION_IDENT(3,1,0,0))
7017 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7019 int element = EL_CUSTOM_START + i;
7021 if (EXPLODES_1X1_OLD(element))
7022 element_info[element].explosion_type = EXPLODES_1X1;
7024 SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7025 EXPLODES_SMASHED(element) ||
7026 EXPLODES_IMPACT(element)));
7030 // correct previously hard-coded move delay values for maze runner style
7031 if (level->game_version < VERSION_IDENT(3,1,1,0))
7033 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7035 int element = EL_CUSTOM_START + i;
7037 if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7039 // previously hard-coded and therefore ignored
7040 element_info[element].move_delay_fixed = 9;
7041 element_info[element].move_delay_random = 0;
7046 // set some other uninitialized values of custom elements in older levels
7047 if (level->game_version < VERSION_IDENT(3,1,0,0))
7049 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7051 int element = EL_CUSTOM_START + i;
7053 element_info[element].access_direction = MV_ALL_DIRECTIONS;
7055 element_info[element].explosion_delay = 17;
7056 element_info[element].ignition_delay = 8;
7060 // set mouse click change events to work for left/middle/right mouse button
7061 if (level->game_version < VERSION_IDENT(4,2,3,0))
7063 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7065 int element = EL_CUSTOM_START + i;
7066 struct ElementInfo *ei = &element_info[element];
7068 for (j = 0; j < ei->num_change_pages; j++)
7070 struct ElementChangeInfo *change = &ei->change_page[j];
7072 if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7073 change->has_event[CE_PRESSED_BY_MOUSE] ||
7074 change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7075 change->has_event[CE_MOUSE_PRESSED_ON_X])
7076 change->trigger_side = CH_SIDE_ANY;
7082 static void LoadLevel_InitElements(struct LevelInfo *level)
7084 LoadLevel_InitStandardElements(level);
7086 if (level->file_has_custom_elements)
7087 LoadLevel_InitCustomElements(level);
7089 // initialize element properties for level editor etc.
7090 InitElementPropertiesEngine(level->game_version);
7091 InitElementPropertiesGfxElement();
7094 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7098 // map elements that have changed in newer versions
7099 for (y = 0; y < level->fieldy; y++)
7100 for (x = 0; x < level->fieldx; x++)
7101 level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7102 level->game_version);
7104 // clear unused playfield data (nicer if level gets resized in editor)
7105 for (x = 0; x < MAX_LEV_FIELDX; x++)
7106 for (y = 0; y < MAX_LEV_FIELDY; y++)
7107 if (x >= level->fieldx || y >= level->fieldy)
7108 level->field[x][y] = EL_EMPTY;
7110 // copy elements to runtime playfield array
7111 for (x = 0; x < MAX_LEV_FIELDX; x++)
7112 for (y = 0; y < MAX_LEV_FIELDY; y++)
7113 Tile[x][y] = level->field[x][y];
7115 // initialize level size variables for faster access
7116 lev_fieldx = level->fieldx;
7117 lev_fieldy = level->fieldy;
7119 // determine border element for this level
7120 if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7121 BorderElement = EL_EMPTY; // (in editor, SetBorderElement() is used)
7126 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7128 struct LevelFileInfo *level_file_info = &level->file_info;
7130 if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7131 CopyNativeLevel_RND_to_Native(level);
7134 static void LoadLevelTemplate_LoadAndInit(void)
7136 LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7138 LoadLevel_InitVersion(&level_template);
7139 LoadLevel_InitElements(&level_template);
7140 LoadLevel_InitSettings(&level_template);
7142 ActivateLevelTemplate();
7145 void LoadLevelTemplate(int nr)
7147 if (!fileExists(getGlobalLevelTemplateFilename()))
7149 Warn("no level template found for this level");
7154 setLevelFileInfo(&level_template.file_info, nr);
7156 LoadLevelTemplate_LoadAndInit();
7159 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7161 copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7163 LoadLevelTemplate_LoadAndInit();
7166 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7168 LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7170 if (level.use_custom_template)
7172 if (network_level != NULL)
7173 LoadNetworkLevelTemplate(network_level);
7175 LoadLevelTemplate(-1);
7178 LoadLevel_InitVersion(&level);
7179 LoadLevel_InitElements(&level);
7180 LoadLevel_InitPlayfield(&level);
7181 LoadLevel_InitSettings(&level);
7183 LoadLevel_InitNativeEngines(&level);
7186 void LoadLevel(int nr)
7188 SetLevelSetInfo(leveldir_current->identifier, nr);
7190 setLevelFileInfo(&level.file_info, nr);
7192 LoadLevel_LoadAndInit(NULL);
7195 void LoadLevelInfoOnly(int nr)
7197 setLevelFileInfo(&level.file_info, nr);
7199 LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7202 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7204 SetLevelSetInfo(network_level->leveldir_identifier,
7205 network_level->file_info.nr);
7207 copyLevelFileInfo(&network_level->file_info, &level.file_info);
7209 LoadLevel_LoadAndInit(network_level);
7212 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7216 chunk_size += putFileVersion(file, level->file_version);
7217 chunk_size += putFileVersion(file, level->game_version);
7222 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7226 chunk_size += putFile16BitBE(file, level->creation_date.year);
7227 chunk_size += putFile8Bit(file, level->creation_date.month);
7228 chunk_size += putFile8Bit(file, level->creation_date.day);
7233 #if ENABLE_HISTORIC_CHUNKS
7234 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7238 putFile8Bit(file, level->fieldx);
7239 putFile8Bit(file, level->fieldy);
7241 putFile16BitBE(file, level->time);
7242 putFile16BitBE(file, level->gems_needed);
7244 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7245 putFile8Bit(file, level->name[i]);
7247 for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7248 putFile8Bit(file, level->score[i]);
7250 for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7251 for (y = 0; y < 3; y++)
7252 for (x = 0; x < 3; x++)
7253 putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7254 level->yamyam_content[i].e[x][y]));
7255 putFile8Bit(file, level->amoeba_speed);
7256 putFile8Bit(file, level->time_magic_wall);
7257 putFile8Bit(file, level->time_wheel);
7258 putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7259 level->amoeba_content));
7260 putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7261 putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7262 putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7263 putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7265 putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7267 putFile8Bit(file, (level->block_last_field ? 1 : 0));
7268 putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7269 putFile32BitBE(file, level->can_move_into_acid_bits);
7270 putFile8Bit(file, level->dont_collide_with_bits);
7272 putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7273 putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7275 putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7276 putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7277 putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7279 putFile8Bit(file, level->game_engine_type);
7281 WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7285 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7290 for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7291 chunk_size += putFile8Bit(file, level->name[i]);
7296 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7301 for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7302 chunk_size += putFile8Bit(file, level->author[i]);
7307 #if ENABLE_HISTORIC_CHUNKS
7308 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7313 for (y = 0; y < level->fieldy; y++)
7314 for (x = 0; x < level->fieldx; x++)
7315 if (level->encoding_16bit_field)
7316 chunk_size += putFile16BitBE(file, level->field[x][y]);
7318 chunk_size += putFile8Bit(file, level->field[x][y]);
7324 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7329 for (y = 0; y < level->fieldy; y++)
7330 for (x = 0; x < level->fieldx; x++)
7331 chunk_size += putFile16BitBE(file, level->field[x][y]);
7336 #if ENABLE_HISTORIC_CHUNKS
7337 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7341 putFile8Bit(file, EL_YAMYAM);
7342 putFile8Bit(file, level->num_yamyam_contents);
7343 putFile8Bit(file, 0);
7344 putFile8Bit(file, 0);
7346 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7347 for (y = 0; y < 3; y++)
7348 for (x = 0; x < 3; x++)
7349 if (level->encoding_16bit_field)
7350 putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7352 putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7356 #if ENABLE_HISTORIC_CHUNKS
7357 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7360 int num_contents, content_xsize, content_ysize;
7361 int content_array[MAX_ELEMENT_CONTENTS][3][3];
7363 if (element == EL_YAMYAM)
7365 num_contents = level->num_yamyam_contents;
7369 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7370 for (y = 0; y < 3; y++)
7371 for (x = 0; x < 3; x++)
7372 content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7374 else if (element == EL_BD_AMOEBA)
7380 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7381 for (y = 0; y < 3; y++)
7382 for (x = 0; x < 3; x++)
7383 content_array[i][x][y] = EL_EMPTY;
7384 content_array[0][0][0] = level->amoeba_content;
7388 // chunk header already written -- write empty chunk data
7389 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7391 Warn("cannot save content for element '%d'", element);
7396 putFile16BitBE(file, element);
7397 putFile8Bit(file, num_contents);
7398 putFile8Bit(file, content_xsize);
7399 putFile8Bit(file, content_ysize);
7401 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7403 for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7404 for (y = 0; y < 3; y++)
7405 for (x = 0; x < 3; x++)
7406 putFile16BitBE(file, content_array[i][x][y]);
7410 #if ENABLE_HISTORIC_CHUNKS
7411 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7413 int envelope_nr = element - EL_ENVELOPE_1;
7414 int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7418 chunk_size += putFile16BitBE(file, element);
7419 chunk_size += putFile16BitBE(file, envelope_len);
7420 chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7421 chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7423 WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7424 chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7426 for (i = 0; i < envelope_len; i++)
7427 chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7433 #if ENABLE_HISTORIC_CHUNKS
7434 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7435 int num_changed_custom_elements)
7439 putFile16BitBE(file, num_changed_custom_elements);
7441 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7443 int element = EL_CUSTOM_START + i;
7445 struct ElementInfo *ei = &element_info[element];
7447 if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7449 if (check < num_changed_custom_elements)
7451 putFile16BitBE(file, element);
7452 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7459 if (check != num_changed_custom_elements) // should not happen
7460 Warn("inconsistent number of custom element properties");
7464 #if ENABLE_HISTORIC_CHUNKS
7465 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7466 int num_changed_custom_elements)
7470 putFile16BitBE(file, num_changed_custom_elements);
7472 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7474 int element = EL_CUSTOM_START + i;
7476 if (element_info[element].change->target_element != EL_EMPTY_SPACE)
7478 if (check < num_changed_custom_elements)
7480 putFile16BitBE(file, element);
7481 putFile16BitBE(file, element_info[element].change->target_element);
7488 if (check != num_changed_custom_elements) // should not happen
7489 Warn("inconsistent number of custom target elements");
7493 #if ENABLE_HISTORIC_CHUNKS
7494 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
7495 int num_changed_custom_elements)
7497 int i, j, x, y, check = 0;
7499 putFile16BitBE(file, num_changed_custom_elements);
7501 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7503 int element = EL_CUSTOM_START + i;
7504 struct ElementInfo *ei = &element_info[element];
7506 if (ei->modified_settings)
7508 if (check < num_changed_custom_elements)
7510 putFile16BitBE(file, element);
7512 for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
7513 putFile8Bit(file, ei->description[j]);
7515 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7517 // some free bytes for future properties and padding
7518 WriteUnusedBytesToFile(file, 7);
7520 putFile8Bit(file, ei->use_gfx_element);
7521 putFile16BitBE(file, ei->gfx_element_initial);
7523 putFile8Bit(file, ei->collect_score_initial);
7524 putFile8Bit(file, ei->collect_count_initial);
7526 putFile16BitBE(file, ei->push_delay_fixed);
7527 putFile16BitBE(file, ei->push_delay_random);
7528 putFile16BitBE(file, ei->move_delay_fixed);
7529 putFile16BitBE(file, ei->move_delay_random);
7531 putFile16BitBE(file, ei->move_pattern);
7532 putFile8Bit(file, ei->move_direction_initial);
7533 putFile8Bit(file, ei->move_stepsize);
7535 for (y = 0; y < 3; y++)
7536 for (x = 0; x < 3; x++)
7537 putFile16BitBE(file, ei->content.e[x][y]);
7539 putFile32BitBE(file, ei->change->events);
7541 putFile16BitBE(file, ei->change->target_element);
7543 putFile16BitBE(file, ei->change->delay_fixed);
7544 putFile16BitBE(file, ei->change->delay_random);
7545 putFile16BitBE(file, ei->change->delay_frames);
7547 putFile16BitBE(file, ei->change->initial_trigger_element);
7549 putFile8Bit(file, ei->change->explode);
7550 putFile8Bit(file, ei->change->use_target_content);
7551 putFile8Bit(file, ei->change->only_if_complete);
7552 putFile8Bit(file, ei->change->use_random_replace);
7554 putFile8Bit(file, ei->change->random_percentage);
7555 putFile8Bit(file, ei->change->replace_when);
7557 for (y = 0; y < 3; y++)
7558 for (x = 0; x < 3; x++)
7559 putFile16BitBE(file, ei->change->content.e[x][y]);
7561 putFile8Bit(file, ei->slippery_type);
7563 // some free bytes for future properties and padding
7564 WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
7571 if (check != num_changed_custom_elements) // should not happen
7572 Warn("inconsistent number of custom element properties");
7576 #if ENABLE_HISTORIC_CHUNKS
7577 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
7579 struct ElementInfo *ei = &element_info[element];
7582 // ---------- custom element base property values (96 bytes) ----------------
7584 putFile16BitBE(file, element);
7586 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7587 putFile8Bit(file, ei->description[i]);
7589 putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7591 WriteUnusedBytesToFile(file, 4); // reserved for more base properties
7593 putFile8Bit(file, ei->num_change_pages);
7595 putFile16BitBE(file, ei->ce_value_fixed_initial);
7596 putFile16BitBE(file, ei->ce_value_random_initial);
7597 putFile8Bit(file, ei->use_last_ce_value);
7599 putFile8Bit(file, ei->use_gfx_element);
7600 putFile16BitBE(file, ei->gfx_element_initial);
7602 putFile8Bit(file, ei->collect_score_initial);
7603 putFile8Bit(file, ei->collect_count_initial);
7605 putFile8Bit(file, ei->drop_delay_fixed);
7606 putFile8Bit(file, ei->push_delay_fixed);
7607 putFile8Bit(file, ei->drop_delay_random);
7608 putFile8Bit(file, ei->push_delay_random);
7609 putFile16BitBE(file, ei->move_delay_fixed);
7610 putFile16BitBE(file, ei->move_delay_random);
7612 // bits 0 - 15 of "move_pattern" ...
7613 putFile16BitBE(file, ei->move_pattern & 0xffff);
7614 putFile8Bit(file, ei->move_direction_initial);
7615 putFile8Bit(file, ei->move_stepsize);
7617 putFile8Bit(file, ei->slippery_type);
7619 for (y = 0; y < 3; y++)
7620 for (x = 0; x < 3; x++)
7621 putFile16BitBE(file, ei->content.e[x][y]);
7623 putFile16BitBE(file, ei->move_enter_element);
7624 putFile16BitBE(file, ei->move_leave_element);
7625 putFile8Bit(file, ei->move_leave_type);
7627 // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
7628 putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
7630 putFile8Bit(file, ei->access_direction);
7632 putFile8Bit(file, ei->explosion_delay);
7633 putFile8Bit(file, ei->ignition_delay);
7634 putFile8Bit(file, ei->explosion_type);
7636 // some free bytes for future custom property values and padding
7637 WriteUnusedBytesToFile(file, 1);
7639 // ---------- change page property values (48 bytes) ------------------------
7641 for (i = 0; i < ei->num_change_pages; i++)
7643 struct ElementChangeInfo *change = &ei->change_page[i];
7644 unsigned int event_bits;
7646 // bits 0 - 31 of "has_event[]" ...
7648 for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
7649 if (change->has_event[j])
7650 event_bits |= (1u << j);
7651 putFile32BitBE(file, event_bits);
7653 putFile16BitBE(file, change->target_element);
7655 putFile16BitBE(file, change->delay_fixed);
7656 putFile16BitBE(file, change->delay_random);
7657 putFile16BitBE(file, change->delay_frames);
7659 putFile16BitBE(file, change->initial_trigger_element);
7661 putFile8Bit(file, change->explode);
7662 putFile8Bit(file, change->use_target_content);
7663 putFile8Bit(file, change->only_if_complete);
7664 putFile8Bit(file, change->use_random_replace);
7666 putFile8Bit(file, change->random_percentage);
7667 putFile8Bit(file, change->replace_when);
7669 for (y = 0; y < 3; y++)
7670 for (x = 0; x < 3; x++)
7671 putFile16BitBE(file, change->target_content.e[x][y]);
7673 putFile8Bit(file, change->can_change);
7675 putFile8Bit(file, change->trigger_side);
7677 putFile8Bit(file, change->trigger_player);
7678 putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7679 log_2(change->trigger_page)));
7681 putFile8Bit(file, change->has_action);
7682 putFile8Bit(file, change->action_type);
7683 putFile8Bit(file, change->action_mode);
7684 putFile16BitBE(file, change->action_arg);
7686 // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
7688 for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7689 if (change->has_event[j])
7690 event_bits |= (1u << (j - 32));
7691 putFile8Bit(file, event_bits);
7696 #if ENABLE_HISTORIC_CHUNKS
7697 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7699 struct ElementInfo *ei = &element_info[element];
7700 struct ElementGroupInfo *group = ei->group;
7703 putFile16BitBE(file, element);
7705 for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7706 putFile8Bit(file, ei->description[i]);
7708 putFile8Bit(file, group->num_elements);
7710 putFile8Bit(file, ei->use_gfx_element);
7711 putFile16BitBE(file, ei->gfx_element_initial);
7713 putFile8Bit(file, group->choice_mode);
7715 // some free bytes for future values and padding
7716 WriteUnusedBytesToFile(file, 3);
7718 for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7719 putFile16BitBE(file, group->element[i]);
7723 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7724 boolean write_element)
7726 int save_type = entry->save_type;
7727 int data_type = entry->data_type;
7728 int conf_type = entry->conf_type;
7729 int byte_mask = conf_type & CONF_MASK_BYTES;
7730 int element = entry->element;
7731 int default_value = entry->default_value;
7733 boolean modified = FALSE;
7735 if (byte_mask != CONF_MASK_MULTI_BYTES)
7737 void *value_ptr = entry->value;
7738 int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7741 // check if any settings have been modified before saving them
7742 if (value != default_value)
7745 // do not save if explicitly told or if unmodified default settings
7746 if ((save_type == SAVE_CONF_NEVER) ||
7747 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7751 num_bytes += putFile16BitBE(file, element);
7753 num_bytes += putFile8Bit(file, conf_type);
7754 num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit (file, value) :
7755 byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7756 byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7759 else if (data_type == TYPE_STRING)
7761 char *default_string = entry->default_string;
7762 char *string = (char *)(entry->value);
7763 int string_length = strlen(string);
7766 // check if any settings have been modified before saving them
7767 if (!strEqual(string, default_string))
7770 // do not save if explicitly told or if unmodified default settings
7771 if ((save_type == SAVE_CONF_NEVER) ||
7772 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7776 num_bytes += putFile16BitBE(file, element);
7778 num_bytes += putFile8Bit(file, conf_type);
7779 num_bytes += putFile16BitBE(file, string_length);
7781 for (i = 0; i < string_length; i++)
7782 num_bytes += putFile8Bit(file, string[i]);
7784 else if (data_type == TYPE_ELEMENT_LIST)
7786 int *element_array = (int *)(entry->value);
7787 int num_elements = *(int *)(entry->num_entities);
7790 // check if any settings have been modified before saving them
7791 for (i = 0; i < num_elements; i++)
7792 if (element_array[i] != default_value)
7795 // do not save if explicitly told or if unmodified default settings
7796 if ((save_type == SAVE_CONF_NEVER) ||
7797 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7801 num_bytes += putFile16BitBE(file, element);
7803 num_bytes += putFile8Bit(file, conf_type);
7804 num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7806 for (i = 0; i < num_elements; i++)
7807 num_bytes += putFile16BitBE(file, element_array[i]);
7809 else if (data_type == TYPE_CONTENT_LIST)
7811 struct Content *content = (struct Content *)(entry->value);
7812 int num_contents = *(int *)(entry->num_entities);
7815 // check if any settings have been modified before saving them
7816 for (i = 0; i < num_contents; i++)
7817 for (y = 0; y < 3; y++)
7818 for (x = 0; x < 3; x++)
7819 if (content[i].e[x][y] != default_value)
7822 // do not save if explicitly told or if unmodified default settings
7823 if ((save_type == SAVE_CONF_NEVER) ||
7824 (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7828 num_bytes += putFile16BitBE(file, element);
7830 num_bytes += putFile8Bit(file, conf_type);
7831 num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7833 for (i = 0; i < num_contents; i++)
7834 for (y = 0; y < 3; y++)
7835 for (x = 0; x < 3; x++)
7836 num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7842 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7847 li = *level; // copy level data into temporary buffer
7849 for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7850 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7855 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7860 li = *level; // copy level data into temporary buffer
7862 for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7863 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7868 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7870 int envelope_nr = element - EL_ENVELOPE_1;
7874 chunk_size += putFile16BitBE(file, element);
7876 // copy envelope data into temporary buffer
7877 xx_envelope = level->envelope[envelope_nr];
7879 for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7880 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7885 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7887 struct ElementInfo *ei = &element_info[element];
7891 chunk_size += putFile16BitBE(file, element);
7893 xx_ei = *ei; // copy element data into temporary buffer
7895 // set default description string for this specific element
7896 strcpy(xx_default_description, getDefaultElementDescription(ei));
7898 for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7899 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7901 for (i = 0; i < ei->num_change_pages; i++)
7903 struct ElementChangeInfo *change = &ei->change_page[i];
7905 xx_current_change_page = i;
7907 xx_change = *change; // copy change data into temporary buffer
7910 setEventBitsFromEventFlags(change);
7912 for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7913 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7920 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7922 struct ElementInfo *ei = &element_info[element];
7923 struct ElementGroupInfo *group = ei->group;
7927 chunk_size += putFile16BitBE(file, element);
7929 xx_ei = *ei; // copy element data into temporary buffer
7930 xx_group = *group; // copy group data into temporary buffer
7932 // set default description string for this specific element
7933 strcpy(xx_default_description, getDefaultElementDescription(ei));
7935 for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7936 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7941 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
7943 struct ElementInfo *ei = &element_info[element];
7947 chunk_size += putFile16BitBE(file, element);
7949 xx_ei = *ei; // copy element data into temporary buffer
7951 for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
7952 chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
7957 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7958 boolean save_as_template)
7964 if (!(file = fopen(filename, MODE_WRITE)))
7966 Warn("cannot save level file '%s'", filename);
7971 level->file_version = FILE_VERSION_ACTUAL;
7972 level->game_version = GAME_VERSION_ACTUAL;
7974 level->creation_date = getCurrentDate();
7976 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7977 putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7979 chunk_size = SaveLevel_VERS(NULL, level);
7980 putFileChunkBE(file, "VERS", chunk_size);
7981 SaveLevel_VERS(file, level);
7983 chunk_size = SaveLevel_DATE(NULL, level);
7984 putFileChunkBE(file, "DATE", chunk_size);
7985 SaveLevel_DATE(file, level);
7987 chunk_size = SaveLevel_NAME(NULL, level);
7988 putFileChunkBE(file, "NAME", chunk_size);
7989 SaveLevel_NAME(file, level);
7991 chunk_size = SaveLevel_AUTH(NULL, level);
7992 putFileChunkBE(file, "AUTH", chunk_size);
7993 SaveLevel_AUTH(file, level);
7995 chunk_size = SaveLevel_INFO(NULL, level);
7996 putFileChunkBE(file, "INFO", chunk_size);
7997 SaveLevel_INFO(file, level);
7999 chunk_size = SaveLevel_BODY(NULL, level);
8000 putFileChunkBE(file, "BODY", chunk_size);
8001 SaveLevel_BODY(file, level);
8003 chunk_size = SaveLevel_ELEM(NULL, level);
8004 if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED) // save if changed
8006 putFileChunkBE(file, "ELEM", chunk_size);
8007 SaveLevel_ELEM(file, level);
8010 for (i = 0; i < NUM_ENVELOPES; i++)
8012 int element = EL_ENVELOPE_1 + i;
8014 chunk_size = SaveLevel_NOTE(NULL, level, element);
8015 if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED) // save if changed
8017 putFileChunkBE(file, "NOTE", chunk_size);
8018 SaveLevel_NOTE(file, level, element);
8022 // if not using template level, check for non-default custom/group elements
8023 if (!level->use_custom_template || save_as_template)
8025 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8027 int element = EL_CUSTOM_START + i;
8029 chunk_size = SaveLevel_CUSX(NULL, level, element);
8030 if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED) // save if changed
8032 putFileChunkBE(file, "CUSX", chunk_size);
8033 SaveLevel_CUSX(file, level, element);
8037 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8039 int element = EL_GROUP_START + i;
8041 chunk_size = SaveLevel_GRPX(NULL, level, element);
8042 if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED) // save if changed
8044 putFileChunkBE(file, "GRPX", chunk_size);
8045 SaveLevel_GRPX(file, level, element);
8049 for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8051 int element = GET_EMPTY_ELEMENT(i);
8053 chunk_size = SaveLevel_EMPX(NULL, level, element);
8054 if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED) // save if changed
8056 putFileChunkBE(file, "EMPX", chunk_size);
8057 SaveLevel_EMPX(file, level, element);
8064 SetFilePermissions(filename, PERMS_PRIVATE);
8067 void SaveLevel(int nr)
8069 char *filename = getDefaultLevelFilename(nr);
8071 SaveLevelFromFilename(&level, filename, FALSE);
8074 void SaveLevelTemplate(void)
8076 char *filename = getLocalLevelTemplateFilename();
8078 SaveLevelFromFilename(&level, filename, TRUE);
8081 boolean SaveLevelChecked(int nr)
8083 char *filename = getDefaultLevelFilename(nr);
8084 boolean new_level = !fileExists(filename);
8085 boolean level_saved = FALSE;
8087 if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8092 Request("Level saved!", REQ_CONFIRM);
8100 void DumpLevel(struct LevelInfo *level)
8102 if (level->no_level_file || level->no_valid_file)
8104 Warn("cannot dump -- no valid level file found");
8110 Print("Level xxx (file version %08d, game version %08d)\n",
8111 level->file_version, level->game_version);
8114 Print("Level author: '%s'\n", level->author);
8115 Print("Level title: '%s'\n", level->name);
8117 Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8119 Print("Level time: %d seconds\n", level->time);
8120 Print("Gems needed: %d\n", level->gems_needed);
8122 Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8123 Print("Time for wheel: %d seconds\n", level->time_wheel);
8124 Print("Time for light: %d seconds\n", level->time_light);
8125 Print("Time for timegate: %d seconds\n", level->time_timegate);
8127 Print("Amoeba speed: %d\n", level->amoeba_speed);
8130 Print("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
8131 Print("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
8132 Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8133 Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8134 Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8135 Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8141 for (i = 0; i < NUM_ENVELOPES; i++)
8143 char *text = level->envelope[i].text;
8144 int text_len = strlen(text);
8145 boolean has_text = FALSE;
8147 for (j = 0; j < text_len; j++)
8148 if (text[j] != ' ' && text[j] != '\n')
8154 Print("Envelope %d:\n'%s'\n", i + 1, text);
8162 void DumpLevels(void)
8164 static LevelDirTree *dumplevel_leveldir = NULL;
8166 dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8167 global.dumplevel_leveldir);
8169 if (dumplevel_leveldir == NULL)
8170 Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8172 if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8173 global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8174 Fail("no such level number: %d", global.dumplevel_level_nr);
8176 leveldir_current = dumplevel_leveldir;
8178 LoadLevel(global.dumplevel_level_nr);
8185 // ============================================================================
8186 // tape file functions
8187 // ============================================================================
8189 static void setTapeInfoToDefaults(void)
8193 // always start with reliable default values (empty tape)
8196 // default values (also for pre-1.2 tapes) with only the first player
8197 tape.player_participates[0] = TRUE;
8198 for (i = 1; i < MAX_PLAYERS; i++)
8199 tape.player_participates[i] = FALSE;
8201 // at least one (default: the first) player participates in every tape
8202 tape.num_participating_players = 1;
8204 tape.property_bits = TAPE_PROPERTY_NONE;
8206 tape.level_nr = level_nr;
8208 tape.changed = FALSE;
8209 tape.solved = FALSE;
8211 tape.recording = FALSE;
8212 tape.playing = FALSE;
8213 tape.pausing = FALSE;
8215 tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8216 tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8218 tape.no_info_chunk = TRUE;
8219 tape.no_valid_file = FALSE;
8222 static int getTapePosSize(struct TapeInfo *tape)
8224 int tape_pos_size = 0;
8226 if (tape->use_key_actions)
8227 tape_pos_size += tape->num_participating_players;
8229 if (tape->use_mouse_actions)
8230 tape_pos_size += 3; // x and y position and mouse button mask
8232 tape_pos_size += 1; // tape action delay value
8234 return tape_pos_size;
8237 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8239 tape->use_key_actions = FALSE;
8240 tape->use_mouse_actions = FALSE;
8242 if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8243 tape->use_key_actions = TRUE;
8245 if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8246 tape->use_mouse_actions = TRUE;
8249 static int getTapeActionValue(struct TapeInfo *tape)
8251 return (tape->use_key_actions &&
8252 tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8253 tape->use_key_actions ? TAPE_USE_KEY_ACTIONS_ONLY :
8254 tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8255 TAPE_ACTIONS_DEFAULT);
8258 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8260 tape->file_version = getFileVersion(file);
8261 tape->game_version = getFileVersion(file);
8266 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8270 tape->random_seed = getFile32BitBE(file);
8271 tape->date = getFile32BitBE(file);
8272 tape->length = getFile32BitBE(file);
8274 // read header fields that are new since version 1.2
8275 if (tape->file_version >= FILE_VERSION_1_2)
8277 byte store_participating_players = getFile8Bit(file);
8280 // since version 1.2, tapes store which players participate in the tape
8281 tape->num_participating_players = 0;
8282 for (i = 0; i < MAX_PLAYERS; i++)
8284 tape->player_participates[i] = FALSE;
8286 if (store_participating_players & (1 << i))
8288 tape->player_participates[i] = TRUE;
8289 tape->num_participating_players++;
8293 setTapeActionFlags(tape, getFile8Bit(file));
8295 tape->property_bits = getFile8Bit(file);
8296 tape->solved = getFile8Bit(file);
8298 engine_version = getFileVersion(file);
8299 if (engine_version > 0)
8300 tape->engine_version = engine_version;
8302 tape->engine_version = tape->game_version;
8308 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8310 tape->scr_fieldx = getFile8Bit(file);
8311 tape->scr_fieldy = getFile8Bit(file);
8316 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8318 char *level_identifier = NULL;
8319 int level_identifier_size;
8322 tape->no_info_chunk = FALSE;
8324 level_identifier_size = getFile16BitBE(file);
8326 level_identifier = checked_malloc(level_identifier_size);
8328 for (i = 0; i < level_identifier_size; i++)
8329 level_identifier[i] = getFile8Bit(file);
8331 strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8332 tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8334 checked_free(level_identifier);
8336 tape->level_nr = getFile16BitBE(file);
8338 chunk_size = 2 + level_identifier_size + 2;
8343 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8346 int tape_pos_size = getTapePosSize(tape);
8347 int chunk_size_expected = tape_pos_size * tape->length;
8349 if (chunk_size_expected != chunk_size)
8351 ReadUnusedBytesFromFile(file, chunk_size);
8352 return chunk_size_expected;
8355 for (i = 0; i < tape->length; i++)
8357 if (i >= MAX_TAPE_LEN)
8359 Warn("tape truncated -- size exceeds maximum tape size %d",
8362 // tape too large; read and ignore remaining tape data from this chunk
8363 for (;i < tape->length; i++)
8364 ReadUnusedBytesFromFile(file, tape_pos_size);
8369 if (tape->use_key_actions)
8371 for (j = 0; j < MAX_PLAYERS; j++)
8373 tape->pos[i].action[j] = MV_NONE;
8375 if (tape->player_participates[j])
8376 tape->pos[i].action[j] = getFile8Bit(file);
8380 if (tape->use_mouse_actions)
8382 tape->pos[i].action[TAPE_ACTION_LX] = getFile8Bit(file);
8383 tape->pos[i].action[TAPE_ACTION_LY] = getFile8Bit(file);
8384 tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8387 tape->pos[i].delay = getFile8Bit(file);
8389 if (tape->file_version == FILE_VERSION_1_0)
8391 // eliminate possible diagonal moves in old tapes
8392 // this is only for backward compatibility
8394 byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8395 byte action = tape->pos[i].action[0];
8396 int k, num_moves = 0;
8398 for (k = 0; k < 4; k++)
8400 if (action & joy_dir[k])
8402 tape->pos[i + num_moves].action[0] = joy_dir[k];
8404 tape->pos[i + num_moves].delay = 0;
8413 tape->length += num_moves;
8416 else if (tape->file_version < FILE_VERSION_2_0)
8418 // convert pre-2.0 tapes to new tape format
8420 if (tape->pos[i].delay > 1)
8423 tape->pos[i + 1] = tape->pos[i];
8424 tape->pos[i + 1].delay = 1;
8427 for (j = 0; j < MAX_PLAYERS; j++)
8428 tape->pos[i].action[j] = MV_NONE;
8429 tape->pos[i].delay--;
8436 if (checkEndOfFile(file))
8440 if (i != tape->length)
8441 chunk_size = tape_pos_size * i;
8446 static void LoadTape_SokobanSolution(char *filename)
8449 int move_delay = TILESIZE / level.initial_player_stepsize[0];
8451 if (!(file = openFile(filename, MODE_READ)))
8453 tape.no_valid_file = TRUE;
8458 while (!checkEndOfFile(file))
8460 unsigned char c = getByteFromFile(file);
8462 if (checkEndOfFile(file))
8469 tape.pos[tape.length].action[0] = MV_UP;
8470 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8476 tape.pos[tape.length].action[0] = MV_DOWN;
8477 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8483 tape.pos[tape.length].action[0] = MV_LEFT;
8484 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8490 tape.pos[tape.length].action[0] = MV_RIGHT;
8491 tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
8499 // ignore white-space characters
8503 tape.no_valid_file = TRUE;
8505 Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
8513 if (tape.no_valid_file)
8516 tape.length_frames = GetTapeLengthFrames();
8517 tape.length_seconds = GetTapeLengthSeconds();
8520 void LoadTapeFromFilename(char *filename)
8522 char cookie[MAX_LINE_LEN];
8523 char chunk_name[CHUNK_ID_LEN + 1];
8527 // always start with reliable default values
8528 setTapeInfoToDefaults();
8530 if (strSuffix(filename, ".sln"))
8532 LoadTape_SokobanSolution(filename);
8537 if (!(file = openFile(filename, MODE_READ)))
8539 tape.no_valid_file = TRUE;
8544 getFileChunkBE(file, chunk_name, NULL);
8545 if (strEqual(chunk_name, "RND1"))
8547 getFile32BitBE(file); // not used
8549 getFileChunkBE(file, chunk_name, NULL);
8550 if (!strEqual(chunk_name, "TAPE"))
8552 tape.no_valid_file = TRUE;
8554 Warn("unknown format of tape file '%s'", filename);
8561 else // check for pre-2.0 file format with cookie string
8563 strcpy(cookie, chunk_name);
8564 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
8566 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8567 cookie[strlen(cookie) - 1] = '\0';
8569 if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
8571 tape.no_valid_file = TRUE;
8573 Warn("unknown format of tape file '%s'", filename);
8580 if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
8582 tape.no_valid_file = TRUE;
8584 Warn("unsupported version of tape file '%s'", filename);
8591 // pre-2.0 tape files have no game version, so use file version here
8592 tape.game_version = tape.file_version;
8595 if (tape.file_version < FILE_VERSION_1_2)
8597 // tape files from versions before 1.2.0 without chunk structure
8598 LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
8599 LoadTape_BODY(file, 2 * tape.length, &tape);
8607 int (*loader)(File *, int, struct TapeInfo *);
8611 { "VERS", TAPE_CHUNK_VERS_SIZE, LoadTape_VERS },
8612 { "HEAD", TAPE_CHUNK_HEAD_SIZE, LoadTape_HEAD },
8613 { "SCRN", TAPE_CHUNK_SCRN_SIZE, LoadTape_SCRN },
8614 { "INFO", -1, LoadTape_INFO },
8615 { "BODY", -1, LoadTape_BODY },
8619 while (getFileChunkBE(file, chunk_name, &chunk_size))
8623 while (chunk_info[i].name != NULL &&
8624 !strEqual(chunk_name, chunk_info[i].name))
8627 if (chunk_info[i].name == NULL)
8629 Warn("unknown chunk '%s' in tape file '%s'",
8630 chunk_name, filename);
8632 ReadUnusedBytesFromFile(file, chunk_size);
8634 else if (chunk_info[i].size != -1 &&
8635 chunk_info[i].size != chunk_size)
8637 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8638 chunk_size, chunk_name, filename);
8640 ReadUnusedBytesFromFile(file, chunk_size);
8644 // call function to load this tape chunk
8645 int chunk_size_expected =
8646 (chunk_info[i].loader)(file, chunk_size, &tape);
8648 // the size of some chunks cannot be checked before reading other
8649 // chunks first (like "HEAD" and "BODY") that contain some header
8650 // information, so check them here
8651 if (chunk_size_expected != chunk_size)
8653 Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
8654 chunk_size, chunk_name, filename);
8662 tape.length_frames = GetTapeLengthFrames();
8663 tape.length_seconds = GetTapeLengthSeconds();
8666 Debug("files:LoadTapeFromFilename", "tape file version: %d",
8668 Debug("files:LoadTapeFromFilename", "tape game version: %d",
8670 Debug("files:LoadTapeFromFilename", "tape engine version: %d",
8671 tape.engine_version);
8675 void LoadTape(int nr)
8677 char *filename = getTapeFilename(nr);
8679 LoadTapeFromFilename(filename);
8682 void LoadSolutionTape(int nr)
8684 char *filename = getSolutionTapeFilename(nr);
8686 LoadTapeFromFilename(filename);
8688 if (TAPE_IS_EMPTY(tape))
8690 if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
8691 level.native_bd_level->replay != NULL)
8692 CopyNativeTape_BD_to_RND(&level);
8693 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
8694 level.native_sp_level->demo.is_available)
8695 CopyNativeTape_SP_to_RND(&level);
8699 void LoadScoreTape(char *score_tape_basename, int nr)
8701 char *filename = getScoreTapeFilename(score_tape_basename, nr);
8703 LoadTapeFromFilename(filename);
8706 void LoadScoreCacheTape(char *score_tape_basename, int nr)
8708 char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
8710 LoadTapeFromFilename(filename);
8713 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
8715 // chunk required for team mode tapes with non-default screen size
8716 return (tape->num_participating_players > 1 &&
8717 (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
8718 tape->scr_fieldy != SCR_FIELDY_DEFAULT));
8721 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
8723 putFileVersion(file, tape->file_version);
8724 putFileVersion(file, tape->game_version);
8727 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
8730 byte store_participating_players = 0;
8732 // set bits for participating players for compact storage
8733 for (i = 0; i < MAX_PLAYERS; i++)
8734 if (tape->player_participates[i])
8735 store_participating_players |= (1 << i);
8737 putFile32BitBE(file, tape->random_seed);
8738 putFile32BitBE(file, tape->date);
8739 putFile32BitBE(file, tape->length);
8741 putFile8Bit(file, store_participating_players);
8743 putFile8Bit(file, getTapeActionValue(tape));
8745 putFile8Bit(file, tape->property_bits);
8746 putFile8Bit(file, tape->solved);
8748 putFileVersion(file, tape->engine_version);
8751 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
8753 putFile8Bit(file, tape->scr_fieldx);
8754 putFile8Bit(file, tape->scr_fieldy);
8757 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
8759 int level_identifier_size = strlen(tape->level_identifier) + 1;
8762 putFile16BitBE(file, level_identifier_size);
8764 for (i = 0; i < level_identifier_size; i++)
8765 putFile8Bit(file, tape->level_identifier[i]);
8767 putFile16BitBE(file, tape->level_nr);
8770 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
8774 for (i = 0; i < tape->length; i++)
8776 if (tape->use_key_actions)
8778 for (j = 0; j < MAX_PLAYERS; j++)
8779 if (tape->player_participates[j])
8780 putFile8Bit(file, tape->pos[i].action[j]);
8783 if (tape->use_mouse_actions)
8785 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
8786 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
8787 putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
8790 putFile8Bit(file, tape->pos[i].delay);
8794 void SaveTapeToFilename(char *filename)
8798 int info_chunk_size;
8799 int body_chunk_size;
8801 if (!(file = fopen(filename, MODE_WRITE)))
8803 Warn("cannot save level recording file '%s'", filename);
8808 tape_pos_size = getTapePosSize(&tape);
8810 info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
8811 body_chunk_size = tape_pos_size * tape.length;
8813 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8814 putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
8816 putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
8817 SaveTape_VERS(file, &tape);
8819 putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
8820 SaveTape_HEAD(file, &tape);
8822 if (checkSaveTape_SCRN(&tape))
8824 putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
8825 SaveTape_SCRN(file, &tape);
8828 putFileChunkBE(file, "INFO", info_chunk_size);
8829 SaveTape_INFO(file, &tape);
8831 putFileChunkBE(file, "BODY", body_chunk_size);
8832 SaveTape_BODY(file, &tape);
8836 SetFilePermissions(filename, PERMS_PRIVATE);
8839 static void SaveTapeExt(char *filename)
8843 tape.file_version = FILE_VERSION_ACTUAL;
8844 tape.game_version = GAME_VERSION_ACTUAL;
8846 tape.num_participating_players = 0;
8848 // count number of participating players
8849 for (i = 0; i < MAX_PLAYERS; i++)
8850 if (tape.player_participates[i])
8851 tape.num_participating_players++;
8853 SaveTapeToFilename(filename);
8855 tape.changed = FALSE;
8858 void SaveTape(int nr)
8860 char *filename = getTapeFilename(nr);
8862 InitTapeDirectory(leveldir_current->subdir);
8864 SaveTapeExt(filename);
8867 void SaveScoreTape(int nr)
8869 char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
8871 // used instead of "leveldir_current->subdir" (for network games)
8872 InitScoreTapeDirectory(levelset.identifier, nr);
8874 SaveTapeExt(filename);
8877 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
8878 unsigned int req_state_added)
8880 char *filename = getTapeFilename(nr);
8881 boolean new_tape = !fileExists(filename);
8882 boolean tape_saved = FALSE;
8884 if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
8889 Request(msg_saved, REQ_CONFIRM | req_state_added);
8897 boolean SaveTapeChecked(int nr)
8899 return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
8902 boolean SaveTapeChecked_LevelSolved(int nr)
8904 return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
8905 "Level solved! Tape saved!", REQ_STAY_OPEN);
8908 void DumpTape(struct TapeInfo *tape)
8910 int tape_frame_counter;
8913 if (tape->no_valid_file)
8915 Warn("cannot dump -- no valid tape file found");
8922 Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8923 tape->level_nr, tape->file_version, tape->game_version);
8924 Print(" (effective engine version %08d)\n",
8925 tape->engine_version);
8926 Print("Level series identifier: '%s'\n", tape->level_identifier);
8928 Print("Solution tape: %s\n",
8929 tape->solved ? "yes" :
8930 tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
8932 Print("Special tape properties: ");
8933 if (tape->property_bits == TAPE_PROPERTY_NONE)
8935 if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
8936 Print("[em_random_bug]");
8937 if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
8938 Print("[game_speed]");
8939 if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
8941 if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
8942 Print("[single_step]");
8943 if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
8944 Print("[snapshot]");
8945 if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
8946 Print("[replayed]");
8947 if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
8948 Print("[tas_keys]");
8949 if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
8950 Print("[small_graphics]");
8953 int year2 = tape->date / 10000;
8954 int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
8955 int month_index_raw = (tape->date / 100) % 100;
8956 int month_index = month_index_raw % 12; // prevent invalid index
8957 int month = month_index + 1;
8958 int day = tape->date % 100;
8960 Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
8964 tape_frame_counter = 0;
8966 for (i = 0; i < tape->length; i++)
8968 if (i >= MAX_TAPE_LEN)
8973 for (j = 0; j < MAX_PLAYERS; j++)
8975 if (tape->player_participates[j])
8977 int action = tape->pos[i].action[j];
8979 Print("%d:%02x ", j, action);
8980 Print("[%c%c%c%c|%c%c] - ",
8981 (action & JOY_LEFT ? '<' : ' '),
8982 (action & JOY_RIGHT ? '>' : ' '),
8983 (action & JOY_UP ? '^' : ' '),
8984 (action & JOY_DOWN ? 'v' : ' '),
8985 (action & JOY_BUTTON_1 ? '1' : ' '),
8986 (action & JOY_BUTTON_2 ? '2' : ' '));
8990 Print("(%03d) ", tape->pos[i].delay);
8991 Print("[%05d]\n", tape_frame_counter);
8993 tape_frame_counter += tape->pos[i].delay;
8999 void DumpTapes(void)
9001 static LevelDirTree *dumptape_leveldir = NULL;
9003 dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9004 global.dumptape_leveldir);
9006 if (dumptape_leveldir == NULL)
9007 Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9009 if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9010 global.dumptape_level_nr > dumptape_leveldir->last_level)
9011 Fail("no such level number: %d", global.dumptape_level_nr);
9013 leveldir_current = dumptape_leveldir;
9015 if (options.mytapes)
9016 LoadTape(global.dumptape_level_nr);
9018 LoadSolutionTape(global.dumptape_level_nr);
9026 // ============================================================================
9027 // score file functions
9028 // ============================================================================
9030 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9034 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9036 strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9037 strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9038 scores->entry[i].score = 0;
9039 scores->entry[i].time = 0;
9041 scores->entry[i].id = -1;
9042 strcpy(scores->entry[i].tape_date, UNKNOWN_NAME);
9043 strcpy(scores->entry[i].platform, UNKNOWN_NAME);
9044 strcpy(scores->entry[i].version, UNKNOWN_NAME);
9045 strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9046 strcpy(scores->entry[i].country_code, "??");
9049 scores->num_entries = 0;
9050 scores->last_added = -1;
9051 scores->last_added_local = -1;
9053 scores->updated = FALSE;
9054 scores->uploaded = FALSE;
9055 scores->tape_downloaded = FALSE;
9056 scores->force_last_added = FALSE;
9058 // The following values are intentionally not reset here:
9062 // - continue_playing
9063 // - continue_on_return
9066 static void setScoreInfoToDefaults(void)
9068 setScoreInfoToDefaultsExt(&scores);
9071 static void setServerScoreInfoToDefaults(void)
9073 setScoreInfoToDefaultsExt(&server_scores);
9076 static void LoadScore_OLD(int nr)
9079 char *filename = getScoreFilename(nr);
9080 char cookie[MAX_LINE_LEN];
9081 char line[MAX_LINE_LEN];
9085 if (!(file = fopen(filename, MODE_READ)))
9088 // check file identifier
9089 if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9091 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9092 cookie[strlen(cookie) - 1] = '\0';
9094 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9096 Warn("unknown format of score file '%s'", filename);
9103 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9105 if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9106 Warn("fscanf() failed; %s", strerror(errno));
9108 if (fgets(line, MAX_LINE_LEN, file) == NULL)
9111 if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9112 line[strlen(line) - 1] = '\0';
9114 for (line_ptr = line; *line_ptr; line_ptr++)
9116 if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9118 strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9119 scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9128 static void ConvertScore_OLD(void)
9130 // only convert score to time for levels that rate playing time over score
9131 if (!level.rate_time_over_score)
9134 // convert old score to playing time for score-less levels (like Supaplex)
9135 int time_final_max = 999;
9138 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9140 int score = scores.entry[i].score;
9142 if (score > 0 && score < time_final_max)
9143 scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9147 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9149 scores->file_version = getFileVersion(file);
9150 scores->game_version = getFileVersion(file);
9155 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9157 char *level_identifier = NULL;
9158 int level_identifier_size;
9161 level_identifier_size = getFile16BitBE(file);
9163 level_identifier = checked_malloc(level_identifier_size);
9165 for (i = 0; i < level_identifier_size; i++)
9166 level_identifier[i] = getFile8Bit(file);
9168 strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9169 scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9171 checked_free(level_identifier);
9173 scores->level_nr = getFile16BitBE(file);
9174 scores->num_entries = getFile16BitBE(file);
9176 chunk_size = 2 + level_identifier_size + 2 + 2;
9181 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9185 for (i = 0; i < scores->num_entries; i++)
9187 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9188 scores->entry[i].name[j] = getFile8Bit(file);
9190 scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9193 chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9198 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9202 for (i = 0; i < scores->num_entries; i++)
9203 scores->entry[i].score = getFile16BitBE(file);
9205 chunk_size = scores->num_entries * 2;
9210 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9214 for (i = 0; i < scores->num_entries; i++)
9215 scores->entry[i].score = getFile32BitBE(file);
9217 chunk_size = scores->num_entries * 4;
9222 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9226 for (i = 0; i < scores->num_entries; i++)
9227 scores->entry[i].time = getFile32BitBE(file);
9229 chunk_size = scores->num_entries * 4;
9234 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9238 for (i = 0; i < scores->num_entries; i++)
9240 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9241 scores->entry[i].tape_basename[j] = getFile8Bit(file);
9243 scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9246 chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9251 void LoadScore(int nr)
9253 char *filename = getScoreFilename(nr);
9254 char cookie[MAX_LINE_LEN];
9255 char chunk_name[CHUNK_ID_LEN + 1];
9257 boolean old_score_file_format = FALSE;
9260 // always start with reliable default values
9261 setScoreInfoToDefaults();
9263 if (!(file = openFile(filename, MODE_READ)))
9266 getFileChunkBE(file, chunk_name, NULL);
9267 if (strEqual(chunk_name, "RND1"))
9269 getFile32BitBE(file); // not used
9271 getFileChunkBE(file, chunk_name, NULL);
9272 if (!strEqual(chunk_name, "SCOR"))
9274 Warn("unknown format of score file '%s'", filename);
9281 else // check for old file format with cookie string
9283 strcpy(cookie, chunk_name);
9284 if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9286 if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9287 cookie[strlen(cookie) - 1] = '\0';
9289 if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9291 Warn("unknown format of score file '%s'", filename);
9298 old_score_file_format = TRUE;
9301 if (old_score_file_format)
9303 // score files from versions before 4.2.4.0 without chunk structure
9306 // convert score to time, if possible (mainly for Supaplex levels)
9315 int (*loader)(File *, int, struct ScoreInfo *);
9319 { "VERS", SCORE_CHUNK_VERS_SIZE, LoadScore_VERS },
9320 { "INFO", -1, LoadScore_INFO },
9321 { "NAME", -1, LoadScore_NAME },
9322 { "SCOR", -1, LoadScore_SCOR },
9323 { "SC4R", -1, LoadScore_SC4R },
9324 { "TIME", -1, LoadScore_TIME },
9325 { "TAPE", -1, LoadScore_TAPE },
9330 while (getFileChunkBE(file, chunk_name, &chunk_size))
9334 while (chunk_info[i].name != NULL &&
9335 !strEqual(chunk_name, chunk_info[i].name))
9338 if (chunk_info[i].name == NULL)
9340 Warn("unknown chunk '%s' in score file '%s'",
9341 chunk_name, filename);
9343 ReadUnusedBytesFromFile(file, chunk_size);
9345 else if (chunk_info[i].size != -1 &&
9346 chunk_info[i].size != chunk_size)
9348 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9349 chunk_size, chunk_name, filename);
9351 ReadUnusedBytesFromFile(file, chunk_size);
9355 // call function to load this score chunk
9356 int chunk_size_expected =
9357 (chunk_info[i].loader)(file, chunk_size, &scores);
9359 // the size of some chunks cannot be checked before reading other
9360 // chunks first (like "HEAD" and "BODY") that contain some header
9361 // information, so check them here
9362 if (chunk_size_expected != chunk_size)
9364 Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9365 chunk_size, chunk_name, filename);
9374 #if ENABLE_HISTORIC_CHUNKS
9375 void SaveScore_OLD(int nr)
9378 char *filename = getScoreFilename(nr);
9381 // used instead of "leveldir_current->subdir" (for network games)
9382 InitScoreDirectory(levelset.identifier);
9384 if (!(file = fopen(filename, MODE_WRITE)))
9386 Warn("cannot save score for level %d", nr);
9391 fprintf(file, "%s\n\n", SCORE_COOKIE);
9393 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9394 fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9398 SetFilePermissions(filename, PERMS_PRIVATE);
9402 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9404 putFileVersion(file, scores->file_version);
9405 putFileVersion(file, scores->game_version);
9408 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9410 int level_identifier_size = strlen(scores->level_identifier) + 1;
9413 putFile16BitBE(file, level_identifier_size);
9415 for (i = 0; i < level_identifier_size; i++)
9416 putFile8Bit(file, scores->level_identifier[i]);
9418 putFile16BitBE(file, scores->level_nr);
9419 putFile16BitBE(file, scores->num_entries);
9422 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9426 for (i = 0; i < scores->num_entries; i++)
9428 int name_size = strlen(scores->entry[i].name);
9430 for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9431 putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9435 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9439 for (i = 0; i < scores->num_entries; i++)
9440 putFile16BitBE(file, scores->entry[i].score);
9443 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9447 for (i = 0; i < scores->num_entries; i++)
9448 putFile32BitBE(file, scores->entry[i].score);
9451 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9455 for (i = 0; i < scores->num_entries; i++)
9456 putFile32BitBE(file, scores->entry[i].time);
9459 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9463 for (i = 0; i < scores->num_entries; i++)
9465 int size = strlen(scores->entry[i].tape_basename);
9467 for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9468 putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
9472 static void SaveScoreToFilename(char *filename)
9475 int info_chunk_size;
9476 int name_chunk_size;
9477 int scor_chunk_size;
9478 int sc4r_chunk_size;
9479 int time_chunk_size;
9480 int tape_chunk_size;
9481 boolean has_large_score_values;
9484 if (!(file = fopen(filename, MODE_WRITE)))
9486 Warn("cannot save score file '%s'", filename);
9491 info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
9492 name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
9493 scor_chunk_size = scores.num_entries * 2;
9494 sc4r_chunk_size = scores.num_entries * 4;
9495 time_chunk_size = scores.num_entries * 4;
9496 tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9498 has_large_score_values = FALSE;
9499 for (i = 0; i < scores.num_entries; i++)
9500 if (scores.entry[i].score > 0xffff)
9501 has_large_score_values = TRUE;
9503 putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9504 putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
9506 putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
9507 SaveScore_VERS(file, &scores);
9509 putFileChunkBE(file, "INFO", info_chunk_size);
9510 SaveScore_INFO(file, &scores);
9512 putFileChunkBE(file, "NAME", name_chunk_size);
9513 SaveScore_NAME(file, &scores);
9515 if (has_large_score_values)
9517 putFileChunkBE(file, "SC4R", sc4r_chunk_size);
9518 SaveScore_SC4R(file, &scores);
9522 putFileChunkBE(file, "SCOR", scor_chunk_size);
9523 SaveScore_SCOR(file, &scores);
9526 putFileChunkBE(file, "TIME", time_chunk_size);
9527 SaveScore_TIME(file, &scores);
9529 putFileChunkBE(file, "TAPE", tape_chunk_size);
9530 SaveScore_TAPE(file, &scores);
9534 SetFilePermissions(filename, PERMS_PRIVATE);
9537 void SaveScore(int nr)
9539 char *filename = getScoreFilename(nr);
9542 // used instead of "leveldir_current->subdir" (for network games)
9543 InitScoreDirectory(levelset.identifier);
9545 scores.file_version = FILE_VERSION_ACTUAL;
9546 scores.game_version = GAME_VERSION_ACTUAL;
9548 strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
9549 scores.level_identifier[MAX_FILENAME_LEN] = '\0';
9550 scores.level_nr = level_nr;
9552 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9553 if (scores.entry[i].score == 0 &&
9554 scores.entry[i].time == 0 &&
9555 strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
9558 scores.num_entries = i;
9560 if (scores.num_entries == 0)
9563 SaveScoreToFilename(filename);
9566 static void LoadServerScoreFromCache(int nr)
9568 struct ScoreEntry score_entry;
9577 { &score_entry.score, FALSE, 0 },
9578 { &score_entry.time, FALSE, 0 },
9579 { score_entry.name, TRUE, MAX_PLAYER_NAME_LEN },
9580 { score_entry.tape_basename, TRUE, MAX_FILENAME_LEN },
9581 { score_entry.tape_date, TRUE, MAX_ISO_DATE_LEN },
9582 { &score_entry.id, FALSE, 0 },
9583 { score_entry.platform, TRUE, MAX_PLATFORM_TEXT_LEN },
9584 { score_entry.version, TRUE, MAX_VERSION_TEXT_LEN },
9585 { score_entry.country_code, TRUE, MAX_COUNTRY_CODE_LEN },
9586 { score_entry.country_name, TRUE, MAX_COUNTRY_NAME_LEN },
9590 char *filename = getScoreCacheFilename(nr);
9591 SetupFileHash *score_hash = loadSetupFileHash(filename);
9594 server_scores.num_entries = 0;
9596 if (score_hash == NULL)
9599 for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9601 score_entry = server_scores.entry[i];
9603 for (j = 0; score_mapping[j].value != NULL; j++)
9607 sprintf(token, "%02d.%d", i, j);
9609 char *value = getHashEntry(score_hash, token);
9614 if (score_mapping[j].is_string)
9616 char *score_value = (char *)score_mapping[j].value;
9617 int value_size = score_mapping[j].string_size;
9619 strncpy(score_value, value, value_size);
9620 score_value[value_size] = '\0';
9624 int *score_value = (int *)score_mapping[j].value;
9626 *score_value = atoi(value);
9629 server_scores.num_entries = i + 1;
9632 server_scores.entry[i] = score_entry;
9635 freeSetupFileHash(score_hash);
9638 void LoadServerScore(int nr, boolean download_score)
9640 if (!setup.use_api_server)
9643 // always start with reliable default values
9644 setServerScoreInfoToDefaults();
9646 // 1st step: load server scores from cache file (which may not exist)
9647 // (this should prevent reading it while the thread is writing to it)
9648 LoadServerScoreFromCache(nr);
9650 if (download_score && runtime.use_api_server)
9652 // 2nd step: download server scores from score server to cache file
9653 // (as thread, as it might time out if the server is not reachable)
9654 ApiGetScoreAsThread(nr);
9658 void PrepareScoreTapesForUpload(char *leveldir_subdir)
9660 MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
9662 // if score tape not uploaded, ask for uploading missing tapes later
9663 if (!setup.has_remaining_tapes)
9664 setup.ask_for_remaining_tapes = TRUE;
9666 setup.provide_uploading_tapes = TRUE;
9667 setup.has_remaining_tapes = TRUE;
9669 SaveSetup_ServerSetup();
9672 void SaveServerScore(int nr, boolean tape_saved)
9674 if (!runtime.use_api_server)
9676 PrepareScoreTapesForUpload(leveldir_current->subdir);
9681 ApiAddScoreAsThread(nr, tape_saved, NULL);
9684 void SaveServerScoreFromFile(int nr, boolean tape_saved,
9685 char *score_tape_filename)
9687 if (!runtime.use_api_server)
9690 ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
9693 void LoadLocalAndServerScore(int nr, boolean download_score)
9695 int last_added_local = scores.last_added_local;
9696 boolean force_last_added = scores.force_last_added;
9698 // needed if only showing server scores
9699 setScoreInfoToDefaults();
9701 if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
9704 // restore last added local score entry (before merging server scores)
9705 scores.last_added = scores.last_added_local = last_added_local;
9707 if (setup.use_api_server &&
9708 !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
9710 // load server scores from cache file and trigger update from server
9711 LoadServerScore(nr, download_score);
9713 // merge local scores with scores from server
9717 if (force_last_added)
9718 scores.force_last_added = force_last_added;
9722 // ============================================================================
9723 // setup file functions
9724 // ============================================================================
9726 #define TOKEN_STR_PLAYER_PREFIX "player_"
9729 static struct TokenInfo global_setup_tokens[] =
9733 &setup.player_name, "player_name"
9737 &setup.multiple_users, "multiple_users"
9741 &setup.sound, "sound"
9745 &setup.sound_loops, "repeating_sound_loops"
9749 &setup.sound_music, "background_music"
9753 &setup.sound_simple, "simple_sound_effects"
9757 &setup.toons, "toons"
9761 &setup.global_animations, "global_animations"
9765 &setup.scroll_delay, "scroll_delay"
9769 &setup.forced_scroll_delay, "forced_scroll_delay"
9773 &setup.scroll_delay_value, "scroll_delay_value"
9777 &setup.engine_snapshot_mode, "engine_snapshot_mode"
9781 &setup.engine_snapshot_memory, "engine_snapshot_memory"
9785 &setup.fade_screens, "fade_screens"
9789 &setup.autorecord, "automatic_tape_recording"
9793 &setup.autorecord_after_replay, "autorecord_after_replay"
9797 &setup.auto_pause_on_start, "auto_pause_on_start"
9801 &setup.show_titlescreen, "show_titlescreen"
9805 &setup.quick_doors, "quick_doors"
9809 &setup.team_mode, "team_mode"
9813 &setup.handicap, "handicap"
9817 &setup.skip_levels, "skip_levels"
9821 &setup.increment_levels, "increment_levels"
9825 &setup.auto_play_next_level, "auto_play_next_level"
9829 &setup.count_score_after_game, "count_score_after_game"
9833 &setup.show_scores_after_game, "show_scores_after_game"
9837 &setup.time_limit, "time_limit"
9841 &setup.fullscreen, "fullscreen"
9845 &setup.window_scaling_percent, "window_scaling_percent"
9849 &setup.window_scaling_quality, "window_scaling_quality"
9853 &setup.screen_rendering_mode, "screen_rendering_mode"
9857 &setup.vsync_mode, "vsync_mode"
9861 &setup.ask_on_escape, "ask_on_escape"
9865 &setup.ask_on_escape_editor, "ask_on_escape_editor"
9869 &setup.ask_on_game_over, "ask_on_game_over"
9873 &setup.ask_on_quit_game, "ask_on_quit_game"
9877 &setup.ask_on_quit_program, "ask_on_quit_program"
9881 &setup.quick_switch, "quick_player_switch"
9885 &setup.input_on_focus, "input_on_focus"
9889 &setup.prefer_aga_graphics, "prefer_aga_graphics"
9893 &setup.prefer_lowpass_sounds, "prefer_lowpass_sounds"
9897 &setup.prefer_extra_panel_items, "prefer_extra_panel_items"
9901 &setup.game_speed_extended, "game_speed_extended"
9905 &setup.game_frame_delay, "game_frame_delay"
9909 &setup.bd_skip_uncovering, "bd_skip_uncovering"
9913 &setup.bd_skip_hatching, "bd_skip_hatching"
9917 &setup.bd_scroll_delay, "bd_scroll_delay"
9921 &setup.bd_smooth_movements, "bd_smooth_movements"
9925 &setup.sp_show_border_elements, "sp_show_border_elements"
9929 &setup.small_game_graphics, "small_game_graphics"
9933 &setup.show_load_save_buttons, "show_load_save_buttons"
9937 &setup.show_undo_redo_buttons, "show_undo_redo_buttons"
9941 &setup.scores_in_highscore_list, "scores_in_highscore_list"
9945 &setup.graphics_set, "graphics_set"
9949 &setup.sounds_set, "sounds_set"
9953 &setup.music_set, "music_set"
9957 &setup.override_level_graphics, "override_level_graphics"
9961 &setup.override_level_sounds, "override_level_sounds"
9965 &setup.override_level_music, "override_level_music"
9969 &setup.volume_simple, "volume_simple"
9973 &setup.volume_loops, "volume_loops"
9977 &setup.volume_music, "volume_music"
9981 &setup.network_mode, "network_mode"
9985 &setup.network_player_nr, "network_player"
9989 &setup.network_server_hostname, "network_server_hostname"
9993 &setup.touch.control_type, "touch.control_type"
9997 &setup.touch.move_distance, "touch.move_distance"
10001 &setup.touch.drop_distance, "touch.drop_distance"
10005 &setup.touch.transparency, "touch.transparency"
10009 &setup.touch.draw_outlined, "touch.draw_outlined"
10013 &setup.touch.draw_pressed, "touch.draw_pressed"
10017 &setup.touch.grid_xsize[0], "touch.virtual_buttons.0.xsize"
10021 &setup.touch.grid_ysize[0], "touch.virtual_buttons.0.ysize"
10025 &setup.touch.grid_xsize[1], "touch.virtual_buttons.1.xsize"
10029 &setup.touch.grid_ysize[1], "touch.virtual_buttons.1.ysize"
10033 &setup.touch.overlay_buttons, "touch.overlay_buttons"
10037 static struct TokenInfo auto_setup_tokens[] =
10041 &setup.auto_setup.editor_zoom_tilesize, "editor.zoom_tilesize"
10045 static struct TokenInfo server_setup_tokens[] =
10049 &setup.player_uuid, "player_uuid"
10053 &setup.player_version, "player_version"
10057 &setup.use_api_server, TEST_PREFIX "use_api_server"
10061 &setup.api_server_hostname, TEST_PREFIX "api_server_hostname"
10065 &setup.api_server_password, TEST_PREFIX "api_server_password"
10069 &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10073 &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10077 &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10081 &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10085 &setup.has_remaining_tapes, TEST_PREFIX "has_remaining_tapes"
10089 static struct TokenInfo editor_setup_tokens[] =
10093 &setup.editor.el_classic, "editor.el_classic"
10097 &setup.editor.el_custom, "editor.el_custom"
10101 &setup.editor.el_user_defined, "editor.el_user_defined"
10105 &setup.editor.el_dynamic, "editor.el_dynamic"
10109 &setup.editor.el_headlines, "editor.el_headlines"
10113 &setup.editor.show_element_token, "editor.show_element_token"
10117 &setup.editor.show_read_only_warning, "editor.show_read_only_warning"
10121 static struct TokenInfo editor_cascade_setup_tokens[] =
10125 &setup.editor_cascade.el_bd, "editor.cascade.el_bd"
10129 &setup.editor_cascade.el_bd_native, "editor.cascade.el_bd_native"
10133 &setup.editor_cascade.el_em, "editor.cascade.el_em"
10137 &setup.editor_cascade.el_emc, "editor.cascade.el_emc"
10141 &setup.editor_cascade.el_rnd, "editor.cascade.el_rnd"
10145 &setup.editor_cascade.el_sb, "editor.cascade.el_sb"
10149 &setup.editor_cascade.el_sp, "editor.cascade.el_sp"
10153 &setup.editor_cascade.el_dc, "editor.cascade.el_dc"
10157 &setup.editor_cascade.el_dx, "editor.cascade.el_dx"
10161 &setup.editor_cascade.el_mm, "editor.cascade.el_mm"
10165 &setup.editor_cascade.el_df, "editor.cascade.el_df"
10169 &setup.editor_cascade.el_chars, "editor.cascade.el_chars"
10173 &setup.editor_cascade.el_steel_chars, "editor.cascade.el_steel_chars"
10177 &setup.editor_cascade.el_ce, "editor.cascade.el_ce"
10181 &setup.editor_cascade.el_ge, "editor.cascade.el_ge"
10185 &setup.editor_cascade.el_es, "editor.cascade.el_es"
10189 &setup.editor_cascade.el_ref, "editor.cascade.el_ref"
10193 &setup.editor_cascade.el_user, "editor.cascade.el_user"
10197 &setup.editor_cascade.el_dynamic, "editor.cascade.el_dynamic"
10201 static struct TokenInfo shortcut_setup_tokens[] =
10205 &setup.shortcut.save_game, "shortcut.save_game"
10209 &setup.shortcut.load_game, "shortcut.load_game"
10213 &setup.shortcut.restart_game, "shortcut.restart_game"
10217 &setup.shortcut.pause_before_end, "shortcut.pause_before_end"
10221 &setup.shortcut.toggle_pause, "shortcut.toggle_pause"
10225 &setup.shortcut.focus_player[0], "shortcut.focus_player_1"
10229 &setup.shortcut.focus_player[1], "shortcut.focus_player_2"
10233 &setup.shortcut.focus_player[2], "shortcut.focus_player_3"
10237 &setup.shortcut.focus_player[3], "shortcut.focus_player_4"
10241 &setup.shortcut.focus_player_all, "shortcut.focus_player_all"
10245 &setup.shortcut.tape_eject, "shortcut.tape_eject"
10249 &setup.shortcut.tape_extra, "shortcut.tape_extra"
10253 &setup.shortcut.tape_stop, "shortcut.tape_stop"
10257 &setup.shortcut.tape_pause, "shortcut.tape_pause"
10261 &setup.shortcut.tape_record, "shortcut.tape_record"
10265 &setup.shortcut.tape_play, "shortcut.tape_play"
10269 &setup.shortcut.sound_simple, "shortcut.sound_simple"
10273 &setup.shortcut.sound_loops, "shortcut.sound_loops"
10277 &setup.shortcut.sound_music, "shortcut.sound_music"
10281 &setup.shortcut.snap_left, "shortcut.snap_left"
10285 &setup.shortcut.snap_right, "shortcut.snap_right"
10289 &setup.shortcut.snap_up, "shortcut.snap_up"
10293 &setup.shortcut.snap_down, "shortcut.snap_down"
10297 static struct SetupInputInfo setup_input;
10298 static struct TokenInfo player_setup_tokens[] =
10302 &setup_input.use_joystick, ".use_joystick"
10306 &setup_input.joy.device_name, ".joy.device_name"
10310 &setup_input.joy.xleft, ".joy.xleft"
10314 &setup_input.joy.xmiddle, ".joy.xmiddle"
10318 &setup_input.joy.xright, ".joy.xright"
10322 &setup_input.joy.yupper, ".joy.yupper"
10326 &setup_input.joy.ymiddle, ".joy.ymiddle"
10330 &setup_input.joy.ylower, ".joy.ylower"
10334 &setup_input.joy.snap, ".joy.snap_field"
10338 &setup_input.joy.drop, ".joy.place_bomb"
10342 &setup_input.key.left, ".key.move_left"
10346 &setup_input.key.right, ".key.move_right"
10350 &setup_input.key.up, ".key.move_up"
10354 &setup_input.key.down, ".key.move_down"
10358 &setup_input.key.snap, ".key.snap_field"
10362 &setup_input.key.drop, ".key.place_bomb"
10366 static struct TokenInfo system_setup_tokens[] =
10370 &setup.system.sdl_renderdriver, "system.sdl_renderdriver"
10374 &setup.system.sdl_videodriver, "system.sdl_videodriver"
10378 &setup.system.sdl_audiodriver, "system.sdl_audiodriver"
10382 &setup.system.audio_fragment_size, "system.audio_fragment_size"
10386 static struct TokenInfo internal_setup_tokens[] =
10390 &setup.internal.program_title, "program_title"
10394 &setup.internal.program_version, "program_version"
10398 &setup.internal.program_author, "program_author"
10402 &setup.internal.program_email, "program_email"
10406 &setup.internal.program_website, "program_website"
10410 &setup.internal.program_copyright, "program_copyright"
10414 &setup.internal.program_company, "program_company"
10418 &setup.internal.program_icon_file, "program_icon_file"
10422 &setup.internal.default_graphics_set, "default_graphics_set"
10426 &setup.internal.default_sounds_set, "default_sounds_set"
10430 &setup.internal.default_music_set, "default_music_set"
10434 &setup.internal.fallback_graphics_file, "fallback_graphics_file"
10438 &setup.internal.fallback_sounds_file, "fallback_sounds_file"
10442 &setup.internal.fallback_music_file, "fallback_music_file"
10446 &setup.internal.default_level_series, "default_level_series"
10450 &setup.internal.default_window_width, "default_window_width"
10454 &setup.internal.default_window_height, "default_window_height"
10458 &setup.internal.choose_from_top_leveldir, "choose_from_top_leveldir"
10462 &setup.internal.show_scaling_in_title, "show_scaling_in_title"
10466 &setup.internal.create_user_levelset, "create_user_levelset"
10470 &setup.internal.info_screens_from_main, "info_screens_from_main"
10474 &setup.internal.menu_game, "menu_game"
10478 &setup.internal.menu_engines, "menu_engines"
10482 &setup.internal.menu_editor, "menu_editor"
10486 &setup.internal.menu_graphics, "menu_graphics"
10490 &setup.internal.menu_sound, "menu_sound"
10494 &setup.internal.menu_artwork, "menu_artwork"
10498 &setup.internal.menu_input, "menu_input"
10502 &setup.internal.menu_touch, "menu_touch"
10506 &setup.internal.menu_shortcuts, "menu_shortcuts"
10510 &setup.internal.menu_exit, "menu_exit"
10514 &setup.internal.menu_save_and_exit, "menu_save_and_exit"
10518 &setup.internal.menu_shortcuts_various, "menu_shortcuts_various"
10522 &setup.internal.menu_shortcuts_focus, "menu_shortcuts_focus"
10526 &setup.internal.menu_shortcuts_tape, "menu_shortcuts_tape"
10530 &setup.internal.menu_shortcuts_sound, "menu_shortcuts_sound"
10534 &setup.internal.menu_shortcuts_snap, "menu_shortcuts_snap"
10538 &setup.internal.info_title, "info_title"
10542 &setup.internal.info_elements, "info_elements"
10546 &setup.internal.info_music, "info_music"
10550 &setup.internal.info_credits, "info_credits"
10554 &setup.internal.info_program, "info_program"
10558 &setup.internal.info_version, "info_version"
10562 &setup.internal.info_levelset, "info_levelset"
10566 &setup.internal.info_exit, "info_exit"
10570 static struct TokenInfo debug_setup_tokens[] =
10574 &setup.debug.frame_delay[0], "debug.frame_delay_0"
10578 &setup.debug.frame_delay[1], "debug.frame_delay_1"
10582 &setup.debug.frame_delay[2], "debug.frame_delay_2"
10586 &setup.debug.frame_delay[3], "debug.frame_delay_3"
10590 &setup.debug.frame_delay[4], "debug.frame_delay_4"
10594 &setup.debug.frame_delay[5], "debug.frame_delay_5"
10598 &setup.debug.frame_delay[6], "debug.frame_delay_6"
10602 &setup.debug.frame_delay[7], "debug.frame_delay_7"
10606 &setup.debug.frame_delay[8], "debug.frame_delay_8"
10610 &setup.debug.frame_delay[9], "debug.frame_delay_9"
10614 &setup.debug.frame_delay_key[0], "debug.key.frame_delay_0"
10618 &setup.debug.frame_delay_key[1], "debug.key.frame_delay_1"
10622 &setup.debug.frame_delay_key[2], "debug.key.frame_delay_2"
10626 &setup.debug.frame_delay_key[3], "debug.key.frame_delay_3"
10630 &setup.debug.frame_delay_key[4], "debug.key.frame_delay_4"
10634 &setup.debug.frame_delay_key[5], "debug.key.frame_delay_5"
10638 &setup.debug.frame_delay_key[6], "debug.key.frame_delay_6"
10642 &setup.debug.frame_delay_key[7], "debug.key.frame_delay_7"
10646 &setup.debug.frame_delay_key[8], "debug.key.frame_delay_8"
10650 &setup.debug.frame_delay_key[9], "debug.key.frame_delay_9"
10654 &setup.debug.frame_delay_use_mod_key, "debug.frame_delay.use_mod_key"},
10657 &setup.debug.frame_delay_game_only, "debug.frame_delay.game_only"
10661 &setup.debug.show_frames_per_second, "debug.show_frames_per_second"
10665 &setup.debug.xsn_mode, "debug.xsn_mode"
10669 &setup.debug.xsn_percent, "debug.xsn_percent"
10673 static struct TokenInfo options_setup_tokens[] =
10677 &setup.options.verbose, "options.verbose"
10681 &setup.options.debug, "options.debug"
10685 &setup.options.debug_mode, "options.debug_mode"
10689 static void setSetupInfoToDefaults(struct SetupInfo *si)
10693 si->player_name = getStringCopy(getDefaultUserName(user.nr));
10695 si->multiple_users = TRUE;
10698 si->sound_loops = TRUE;
10699 si->sound_music = TRUE;
10700 si->sound_simple = TRUE;
10702 si->global_animations = TRUE;
10703 si->scroll_delay = TRUE;
10704 si->forced_scroll_delay = FALSE;
10705 si->scroll_delay_value = STD_SCROLL_DELAY;
10706 si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
10707 si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
10708 si->fade_screens = TRUE;
10709 si->autorecord = TRUE;
10710 si->autorecord_after_replay = TRUE;
10711 si->auto_pause_on_start = FALSE;
10712 si->show_titlescreen = TRUE;
10713 si->quick_doors = FALSE;
10714 si->team_mode = FALSE;
10715 si->handicap = TRUE;
10716 si->skip_levels = TRUE;
10717 si->increment_levels = TRUE;
10718 si->auto_play_next_level = TRUE;
10719 si->count_score_after_game = TRUE;
10720 si->show_scores_after_game = TRUE;
10721 si->time_limit = TRUE;
10722 si->fullscreen = FALSE;
10723 si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
10724 si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
10725 si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
10726 si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
10727 si->ask_on_escape = TRUE;
10728 si->ask_on_escape_editor = TRUE;
10729 si->ask_on_game_over = TRUE;
10730 si->ask_on_quit_game = TRUE;
10731 si->ask_on_quit_program = TRUE;
10732 si->quick_switch = FALSE;
10733 si->input_on_focus = FALSE;
10734 si->prefer_aga_graphics = TRUE;
10735 si->prefer_lowpass_sounds = FALSE;
10736 si->prefer_extra_panel_items = TRUE;
10737 si->game_speed_extended = FALSE;
10738 si->game_frame_delay = GAME_FRAME_DELAY;
10739 si->bd_skip_uncovering = FALSE;
10740 si->bd_skip_hatching = FALSE;
10741 si->bd_scroll_delay = TRUE;
10742 si->bd_smooth_movements = AUTO;
10743 si->sp_show_border_elements = FALSE;
10744 si->small_game_graphics = FALSE;
10745 si->show_load_save_buttons = FALSE;
10746 si->show_undo_redo_buttons = FALSE;
10747 si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
10749 si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10750 si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10751 si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10753 si->override_level_graphics = FALSE;
10754 si->override_level_sounds = FALSE;
10755 si->override_level_music = FALSE;
10757 si->volume_simple = 100; // percent
10758 si->volume_loops = 100; // percent
10759 si->volume_music = 100; // percent
10761 si->network_mode = FALSE;
10762 si->network_player_nr = 0; // first player
10763 si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
10765 si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
10766 si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT; // percent
10767 si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT; // percent
10768 si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT; // percent
10769 si->touch.draw_outlined = TRUE;
10770 si->touch.draw_pressed = TRUE;
10772 for (i = 0; i < 2; i++)
10774 char *default_grid_button[6][2] =
10780 { "111222", " vv " },
10781 { "111222", " vv " }
10783 int grid_xsize = DEFAULT_GRID_XSIZE(i);
10784 int grid_ysize = DEFAULT_GRID_YSIZE(i);
10785 int min_xsize = MIN(6, grid_xsize);
10786 int min_ysize = MIN(6, grid_ysize);
10787 int startx = grid_xsize - min_xsize;
10788 int starty = grid_ysize - min_ysize;
10791 // virtual buttons grid can only be set to defaults if video is initialized
10792 // (this will be repeated if virtual buttons are not loaded from setup file)
10793 if (video.initialized)
10795 si->touch.grid_xsize[i] = grid_xsize;
10796 si->touch.grid_ysize[i] = grid_ysize;
10800 si->touch.grid_xsize[i] = -1;
10801 si->touch.grid_ysize[i] = -1;
10804 for (x = 0; x < MAX_GRID_XSIZE; x++)
10805 for (y = 0; y < MAX_GRID_YSIZE; y++)
10806 si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
10808 for (x = 0; x < min_xsize; x++)
10809 for (y = 0; y < min_ysize; y++)
10810 si->touch.grid_button[i][x][starty + y] =
10811 default_grid_button[y][0][x];
10813 for (x = 0; x < min_xsize; x++)
10814 for (y = 0; y < min_ysize; y++)
10815 si->touch.grid_button[i][startx + x][starty + y] =
10816 default_grid_button[y][1][x];
10819 si->touch.grid_initialized = video.initialized;
10821 si->touch.overlay_buttons = FALSE;
10823 si->editor.el_boulderdash = TRUE;
10824 si->editor.el_boulderdash_native = TRUE;
10825 si->editor.el_emerald_mine = TRUE;
10826 si->editor.el_emerald_mine_club = TRUE;
10827 si->editor.el_more = TRUE;
10828 si->editor.el_sokoban = TRUE;
10829 si->editor.el_supaplex = TRUE;
10830 si->editor.el_diamond_caves = TRUE;
10831 si->editor.el_dx_boulderdash = TRUE;
10833 si->editor.el_mirror_magic = TRUE;
10834 si->editor.el_deflektor = TRUE;
10836 si->editor.el_chars = TRUE;
10837 si->editor.el_steel_chars = TRUE;
10839 si->editor.el_classic = TRUE;
10840 si->editor.el_custom = TRUE;
10842 si->editor.el_user_defined = FALSE;
10843 si->editor.el_dynamic = TRUE;
10845 si->editor.el_headlines = TRUE;
10847 si->editor.show_element_token = FALSE;
10849 si->editor.show_read_only_warning = TRUE;
10851 si->editor.use_template_for_new_levels = TRUE;
10853 si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
10854 si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
10855 si->shortcut.restart_game = DEFAULT_KEY_RESTART_GAME;
10856 si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
10857 si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
10859 si->shortcut.focus_player[0] = DEFAULT_KEY_FOCUS_PLAYER_1;
10860 si->shortcut.focus_player[1] = DEFAULT_KEY_FOCUS_PLAYER_2;
10861 si->shortcut.focus_player[2] = DEFAULT_KEY_FOCUS_PLAYER_3;
10862 si->shortcut.focus_player[3] = DEFAULT_KEY_FOCUS_PLAYER_4;
10863 si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
10865 si->shortcut.tape_eject = DEFAULT_KEY_TAPE_EJECT;
10866 si->shortcut.tape_extra = DEFAULT_KEY_TAPE_EXTRA;
10867 si->shortcut.tape_stop = DEFAULT_KEY_TAPE_STOP;
10868 si->shortcut.tape_pause = DEFAULT_KEY_TAPE_PAUSE;
10869 si->shortcut.tape_record = DEFAULT_KEY_TAPE_RECORD;
10870 si->shortcut.tape_play = DEFAULT_KEY_TAPE_PLAY;
10872 si->shortcut.sound_simple = DEFAULT_KEY_SOUND_SIMPLE;
10873 si->shortcut.sound_loops = DEFAULT_KEY_SOUND_LOOPS;
10874 si->shortcut.sound_music = DEFAULT_KEY_SOUND_MUSIC;
10876 si->shortcut.snap_left = DEFAULT_KEY_SNAP_LEFT;
10877 si->shortcut.snap_right = DEFAULT_KEY_SNAP_RIGHT;
10878 si->shortcut.snap_up = DEFAULT_KEY_SNAP_UP;
10879 si->shortcut.snap_down = DEFAULT_KEY_SNAP_DOWN;
10881 for (i = 0; i < MAX_PLAYERS; i++)
10883 si->input[i].use_joystick = FALSE;
10884 si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
10885 si->input[i].joy.xleft = JOYSTICK_XLEFT;
10886 si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
10887 si->input[i].joy.xright = JOYSTICK_XRIGHT;
10888 si->input[i].joy.yupper = JOYSTICK_YUPPER;
10889 si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
10890 si->input[i].joy.ylower = JOYSTICK_YLOWER;
10891 si->input[i].joy.snap = (i == 0 ? JOY_BUTTON_1 : 0);
10892 si->input[i].joy.drop = (i == 0 ? JOY_BUTTON_2 : 0);
10893 si->input[i].key.left = (i == 0 ? DEFAULT_KEY_LEFT : KSYM_UNDEFINED);
10894 si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
10895 si->input[i].key.up = (i == 0 ? DEFAULT_KEY_UP : KSYM_UNDEFINED);
10896 si->input[i].key.down = (i == 0 ? DEFAULT_KEY_DOWN : KSYM_UNDEFINED);
10897 si->input[i].key.snap = (i == 0 ? DEFAULT_KEY_SNAP : KSYM_UNDEFINED);
10898 si->input[i].key.drop = (i == 0 ? DEFAULT_KEY_DROP : KSYM_UNDEFINED);
10901 si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
10902 si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
10903 si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
10904 si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
10906 si->internal.program_title = getStringCopy(PROGRAM_TITLE_STRING);
10907 si->internal.program_version = getStringCopy(getProgramRealVersionString());
10908 si->internal.program_author = getStringCopy(PROGRAM_AUTHOR_STRING);
10909 si->internal.program_email = getStringCopy(PROGRAM_EMAIL_STRING);
10910 si->internal.program_website = getStringCopy(PROGRAM_WEBSITE_STRING);
10911 si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
10912 si->internal.program_company = getStringCopy(PROGRAM_COMPANY_STRING);
10914 si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
10916 si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
10917 si->internal.default_sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
10918 si->internal.default_music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
10920 si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
10921 si->internal.fallback_sounds_file = getStringCopy(UNDEFINED_FILENAME);
10922 si->internal.fallback_music_file = getStringCopy(UNDEFINED_FILENAME);
10924 si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
10925 si->internal.choose_from_top_leveldir = FALSE;
10926 si->internal.show_scaling_in_title = TRUE;
10927 si->internal.create_user_levelset = TRUE;
10928 si->internal.info_screens_from_main = FALSE;
10930 si->internal.default_window_width = WIN_XSIZE_DEFAULT;
10931 si->internal.default_window_height = WIN_YSIZE_DEFAULT;
10933 si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
10934 si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
10935 si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
10936 si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
10937 si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
10938 si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
10939 si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
10940 si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
10941 si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
10942 si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
10944 si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
10945 si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
10946 si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
10947 si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
10948 si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
10949 si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
10950 si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
10951 si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
10952 si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
10953 si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
10955 si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
10956 si->debug.frame_delay_game_only = DEFAULT_FRAME_DELAY_GAME_ONLY;
10958 si->debug.show_frames_per_second = FALSE;
10960 si->debug.xsn_mode = AUTO;
10961 si->debug.xsn_percent = 0;
10963 si->options.verbose = FALSE;
10964 si->options.debug = FALSE;
10965 si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
10967 #if defined(PLATFORM_ANDROID)
10968 si->fullscreen = TRUE;
10969 si->touch.overlay_buttons = TRUE;
10972 setHideSetupEntry(&setup.debug.xsn_mode);
10975 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
10977 si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
10980 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
10982 si->player_uuid = NULL; // (will be set later)
10983 si->player_version = 1; // (will be set later)
10985 si->use_api_server = TRUE;
10986 si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
10987 si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
10988 si->ask_for_uploading_tapes = TRUE;
10989 si->ask_for_remaining_tapes = FALSE;
10990 si->provide_uploading_tapes = TRUE;
10991 si->ask_for_using_api_server = TRUE;
10992 si->has_remaining_tapes = FALSE;
10995 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
10997 si->editor_cascade.el_bd = TRUE;
10998 si->editor_cascade.el_bd_native = TRUE;
10999 si->editor_cascade.el_em = TRUE;
11000 si->editor_cascade.el_emc = TRUE;
11001 si->editor_cascade.el_rnd = TRUE;
11002 si->editor_cascade.el_sb = TRUE;
11003 si->editor_cascade.el_sp = TRUE;
11004 si->editor_cascade.el_dc = TRUE;
11005 si->editor_cascade.el_dx = TRUE;
11007 si->editor_cascade.el_mm = TRUE;
11008 si->editor_cascade.el_df = TRUE;
11010 si->editor_cascade.el_chars = FALSE;
11011 si->editor_cascade.el_steel_chars = FALSE;
11012 si->editor_cascade.el_ce = FALSE;
11013 si->editor_cascade.el_ge = FALSE;
11014 si->editor_cascade.el_es = FALSE;
11015 si->editor_cascade.el_ref = FALSE;
11016 si->editor_cascade.el_user = FALSE;
11017 si->editor_cascade.el_dynamic = FALSE;
11020 #define MAX_HIDE_SETUP_TOKEN_SIZE 20
11022 static char *getHideSetupToken(void *setup_value)
11024 static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11026 if (setup_value != NULL)
11027 snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11029 return hide_setup_token;
11032 void setHideSetupEntry(void *setup_value)
11034 char *hide_setup_token = getHideSetupToken(setup_value);
11036 if (hide_setup_hash == NULL)
11037 hide_setup_hash = newSetupFileHash();
11039 if (setup_value != NULL)
11040 setHashEntry(hide_setup_hash, hide_setup_token, "");
11043 void removeHideSetupEntry(void *setup_value)
11045 char *hide_setup_token = getHideSetupToken(setup_value);
11047 if (setup_value != NULL)
11048 removeHashEntry(hide_setup_hash, hide_setup_token);
11051 boolean hideSetupEntry(void *setup_value)
11053 char *hide_setup_token = getHideSetupToken(setup_value);
11055 return (setup_value != NULL &&
11056 getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11059 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11060 struct TokenInfo *token_info,
11061 int token_nr, char *token_text)
11063 char *token_hide_text = getStringCat2(token_text, ".hide");
11064 char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11066 // set the value of this setup option in the setup option structure
11067 setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11069 // check if this setup option should be hidden in the setup menu
11070 if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11071 setHideSetupEntry(token_info[token_nr].value);
11073 free(token_hide_text);
11076 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11077 struct TokenInfo *token_info,
11080 setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11081 token_info[token_nr].text);
11084 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11088 if (!setup_file_hash)
11091 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11092 setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11094 setup.touch.grid_initialized = TRUE;
11095 for (i = 0; i < 2; i++)
11097 int grid_xsize = setup.touch.grid_xsize[i];
11098 int grid_ysize = setup.touch.grid_ysize[i];
11101 // if virtual buttons are not loaded from setup file, repeat initializing
11102 // virtual buttons grid with default values later when video is initialized
11103 if (grid_xsize == -1 ||
11106 setup.touch.grid_initialized = FALSE;
11111 for (y = 0; y < grid_ysize; y++)
11113 char token_string[MAX_LINE_LEN];
11115 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11117 char *value_string = getHashEntry(setup_file_hash, token_string);
11119 if (value_string == NULL)
11122 for (x = 0; x < grid_xsize; x++)
11124 char c = value_string[x];
11126 setup.touch.grid_button[i][x][y] =
11127 (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11132 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11133 setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11135 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11136 setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11138 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11142 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11144 setup_input = setup.input[pnr];
11145 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11147 char full_token[100];
11149 sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11150 setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11153 setup.input[pnr] = setup_input;
11156 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11157 setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11159 for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11160 setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11162 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11163 setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11165 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11166 setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11168 setHideRelatedSetupEntries();
11171 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11175 if (!setup_file_hash)
11178 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11179 setSetupInfo(auto_setup_tokens, i,
11180 getHashEntry(setup_file_hash,
11181 auto_setup_tokens[i].text));
11184 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11188 if (!setup_file_hash)
11191 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11192 setSetupInfo(server_setup_tokens, i,
11193 getHashEntry(setup_file_hash,
11194 server_setup_tokens[i].text));
11197 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11201 if (!setup_file_hash)
11204 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11205 setSetupInfo(editor_cascade_setup_tokens, i,
11206 getHashEntry(setup_file_hash,
11207 editor_cascade_setup_tokens[i].text));
11210 void LoadUserNames(void)
11212 int last_user_nr = user.nr;
11215 if (global.user_names != NULL)
11217 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11218 checked_free(global.user_names[i]);
11220 checked_free(global.user_names);
11223 global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11225 for (i = 0; i < MAX_PLAYER_NAMES; i++)
11229 SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11231 if (setup_file_hash)
11233 char *player_name = getHashEntry(setup_file_hash, "player_name");
11235 global.user_names[i] = getFixedUserName(player_name);
11237 freeSetupFileHash(setup_file_hash);
11240 if (global.user_names[i] == NULL)
11241 global.user_names[i] = getStringCopy(getDefaultUserName(i));
11244 user.nr = last_user_nr;
11247 void LoadSetupFromFilename(char *filename)
11249 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11251 if (setup_file_hash)
11253 decodeSetupFileHash_Default(setup_file_hash);
11255 freeSetupFileHash(setup_file_hash);
11259 Debug("setup", "using default setup values");
11263 static void LoadSetup_SpecialPostProcessing(void)
11265 char *player_name_new;
11267 // needed to work around problems with fixed length strings
11268 player_name_new = getFixedUserName(setup.player_name);
11269 free(setup.player_name);
11270 setup.player_name = player_name_new;
11272 // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11273 if (setup.scroll_delay == FALSE)
11275 setup.scroll_delay_value = MIN_SCROLL_DELAY;
11276 setup.scroll_delay = TRUE; // now always "on"
11279 // make sure that scroll delay value stays inside valid range
11280 setup.scroll_delay_value =
11281 MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11284 void LoadSetup_Default(void)
11288 // always start with reliable default values
11289 setSetupInfoToDefaults(&setup);
11291 // try to load setup values from default setup file
11292 filename = getDefaultSetupFilename();
11294 if (fileExists(filename))
11295 LoadSetupFromFilename(filename);
11297 // try to load setup values from platform setup file
11298 filename = getPlatformSetupFilename();
11300 if (fileExists(filename))
11301 LoadSetupFromFilename(filename);
11303 // try to load setup values from user setup file
11304 filename = getSetupFilename();
11306 LoadSetupFromFilename(filename);
11308 LoadSetup_SpecialPostProcessing();
11311 void LoadSetup_AutoSetup(void)
11313 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11314 SetupFileHash *setup_file_hash = NULL;
11316 // always start with reliable default values
11317 setSetupInfoToDefaults_AutoSetup(&setup);
11319 setup_file_hash = loadSetupFileHash(filename);
11321 if (setup_file_hash)
11323 decodeSetupFileHash_AutoSetup(setup_file_hash);
11325 freeSetupFileHash(setup_file_hash);
11331 void LoadSetup_ServerSetup(void)
11333 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11334 SetupFileHash *setup_file_hash = NULL;
11336 // always start with reliable default values
11337 setSetupInfoToDefaults_ServerSetup(&setup);
11339 setup_file_hash = loadSetupFileHash(filename);
11341 if (setup_file_hash)
11343 decodeSetupFileHash_ServerSetup(setup_file_hash);
11345 freeSetupFileHash(setup_file_hash);
11350 if (setup.player_uuid == NULL)
11352 // player UUID does not yet exist in setup file
11353 setup.player_uuid = getStringCopy(getUUID());
11354 setup.player_version = 2;
11356 SaveSetup_ServerSetup();
11360 void LoadSetup_EditorCascade(void)
11362 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11363 SetupFileHash *setup_file_hash = NULL;
11365 // always start with reliable default values
11366 setSetupInfoToDefaults_EditorCascade(&setup);
11368 setup_file_hash = loadSetupFileHash(filename);
11370 if (setup_file_hash)
11372 decodeSetupFileHash_EditorCascade(setup_file_hash);
11374 freeSetupFileHash(setup_file_hash);
11380 void LoadSetup(void)
11382 LoadSetup_Default();
11383 LoadSetup_AutoSetup();
11384 LoadSetup_ServerSetup();
11385 LoadSetup_EditorCascade();
11388 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11389 char *mapping_line)
11391 char mapping_guid[MAX_LINE_LEN];
11392 char *mapping_start, *mapping_end;
11394 // get GUID from game controller mapping line: copy complete line
11395 strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11396 mapping_guid[MAX_LINE_LEN - 1] = '\0';
11398 // get GUID from game controller mapping line: cut after GUID part
11399 mapping_start = strchr(mapping_guid, ',');
11400 if (mapping_start != NULL)
11401 *mapping_start = '\0';
11403 // cut newline from game controller mapping line
11404 mapping_end = strchr(mapping_line, '\n');
11405 if (mapping_end != NULL)
11406 *mapping_end = '\0';
11408 // add mapping entry to game controller mappings hash
11409 setHashEntry(mappings_hash, mapping_guid, mapping_line);
11412 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11417 if (!(file = fopen(filename, MODE_READ)))
11419 Warn("cannot read game controller mappings file '%s'", filename);
11424 while (!feof(file))
11426 char line[MAX_LINE_LEN];
11428 if (!fgets(line, MAX_LINE_LEN, file))
11431 addGameControllerMappingToHash(mappings_hash, line);
11437 void SaveSetup_Default(void)
11439 char *filename = getSetupFilename();
11443 InitUserDataDirectory();
11445 if (!(file = fopen(filename, MODE_WRITE)))
11447 Warn("cannot write setup file '%s'", filename);
11452 fprintFileHeader(file, SETUP_FILENAME);
11454 for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11456 // just to make things nicer :)
11457 if (global_setup_tokens[i].value == &setup.multiple_users ||
11458 global_setup_tokens[i].value == &setup.sound ||
11459 global_setup_tokens[i].value == &setup.graphics_set ||
11460 global_setup_tokens[i].value == &setup.volume_simple ||
11461 global_setup_tokens[i].value == &setup.network_mode ||
11462 global_setup_tokens[i].value == &setup.touch.control_type ||
11463 global_setup_tokens[i].value == &setup.touch.grid_xsize[0] ||
11464 global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
11465 fprintf(file, "\n");
11467 fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
11470 for (i = 0; i < 2; i++)
11472 int grid_xsize = setup.touch.grid_xsize[i];
11473 int grid_ysize = setup.touch.grid_ysize[i];
11476 fprintf(file, "\n");
11478 for (y = 0; y < grid_ysize; y++)
11480 char token_string[MAX_LINE_LEN];
11481 char value_string[MAX_LINE_LEN];
11483 sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11485 for (x = 0; x < grid_xsize; x++)
11487 char c = setup.touch.grid_button[i][x][y];
11489 value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
11492 value_string[grid_xsize] = '\0';
11494 fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
11498 fprintf(file, "\n");
11499 for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11500 fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
11502 fprintf(file, "\n");
11503 for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11504 fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
11506 for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11510 sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11511 fprintf(file, "\n");
11513 setup_input = setup.input[pnr];
11514 for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11515 fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
11518 fprintf(file, "\n");
11519 for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11520 fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
11522 // (internal setup values not saved to user setup file)
11524 fprintf(file, "\n");
11525 for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11526 if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
11527 setup.debug.xsn_mode != AUTO)
11528 fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
11530 fprintf(file, "\n");
11531 for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11532 fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
11536 SetFilePermissions(filename, PERMS_PRIVATE);
11539 void SaveSetup_AutoSetup(void)
11541 char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11545 InitUserDataDirectory();
11547 if (!(file = fopen(filename, MODE_WRITE)))
11549 Warn("cannot write auto setup file '%s'", filename);
11556 fprintFileHeader(file, AUTOSETUP_FILENAME);
11558 for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11559 fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
11563 SetFilePermissions(filename, PERMS_PRIVATE);
11568 void SaveSetup_ServerSetup(void)
11570 char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11574 InitUserDataDirectory();
11576 if (!(file = fopen(filename, MODE_WRITE)))
11578 Warn("cannot write server setup file '%s'", filename);
11585 fprintFileHeader(file, SERVERSETUP_FILENAME);
11587 for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11589 // just to make things nicer :)
11590 if (server_setup_tokens[i].value == &setup.use_api_server)
11591 fprintf(file, "\n");
11593 fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
11598 SetFilePermissions(filename, PERMS_PRIVATE);
11603 void SaveSetup_EditorCascade(void)
11605 char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11609 InitUserDataDirectory();
11611 if (!(file = fopen(filename, MODE_WRITE)))
11613 Warn("cannot write editor cascade state file '%s'", filename);
11620 fprintFileHeader(file, EDITORCASCADE_FILENAME);
11622 for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11623 fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
11627 SetFilePermissions(filename, PERMS_PRIVATE);
11632 void SaveSetup(void)
11634 SaveSetup_Default();
11635 SaveSetup_AutoSetup();
11636 SaveSetup_ServerSetup();
11637 SaveSetup_EditorCascade();
11640 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
11645 if (!(file = fopen(filename, MODE_WRITE)))
11647 Warn("cannot write game controller mappings file '%s'", filename);
11652 BEGIN_HASH_ITERATION(mappings_hash, itr)
11654 fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
11656 END_HASH_ITERATION(mappings_hash, itr)
11661 void SaveSetup_AddGameControllerMapping(char *mapping)
11663 char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
11664 SetupFileHash *mappings_hash = newSetupFileHash();
11666 InitUserDataDirectory();
11668 // load existing personal game controller mappings
11669 LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
11671 // add new mapping to personal game controller mappings
11672 addGameControllerMappingToHash(mappings_hash, mapping);
11674 // save updated personal game controller mappings
11675 SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
11677 freeSetupFileHash(mappings_hash);
11681 void LoadCustomElementDescriptions(void)
11683 char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
11684 SetupFileHash *setup_file_hash;
11687 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11689 if (element_info[i].custom_description != NULL)
11691 free(element_info[i].custom_description);
11692 element_info[i].custom_description = NULL;
11696 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
11699 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
11701 char *token = getStringCat2(element_info[i].token_name, ".name");
11702 char *value = getHashEntry(setup_file_hash, token);
11705 element_info[i].custom_description = getStringCopy(value);
11710 freeSetupFileHash(setup_file_hash);
11713 static int getElementFromToken(char *token)
11715 char *value = getHashEntry(element_token_hash, token);
11718 return atoi(value);
11720 Warn("unknown element token '%s'", token);
11722 return EL_UNDEFINED;
11725 void FreeGlobalAnimEventInfo(void)
11727 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11729 if (gaei->event_list == NULL)
11734 for (i = 0; i < gaei->num_event_lists; i++)
11736 checked_free(gaei->event_list[i]->event_value);
11737 checked_free(gaei->event_list[i]);
11740 checked_free(gaei->event_list);
11742 gaei->event_list = NULL;
11743 gaei->num_event_lists = 0;
11746 static int AddGlobalAnimEventList(void)
11748 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11749 int list_pos = gaei->num_event_lists++;
11751 gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
11752 sizeof(struct GlobalAnimEventListInfo *));
11754 gaei->event_list[list_pos] =
11755 checked_calloc(sizeof(struct GlobalAnimEventListInfo));
11757 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11759 gaeli->event_value = NULL;
11760 gaeli->num_event_values = 0;
11765 static int AddGlobalAnimEventValue(int list_pos, int event_value)
11767 // do not add empty global animation events
11768 if (event_value == ANIM_EVENT_NONE)
11771 // if list position is undefined, create new list
11772 if (list_pos == ANIM_EVENT_UNDEFINED)
11773 list_pos = AddGlobalAnimEventList();
11775 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11776 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11777 int value_pos = gaeli->num_event_values++;
11779 gaeli->event_value = checked_realloc(gaeli->event_value,
11780 gaeli->num_event_values * sizeof(int *));
11782 gaeli->event_value[value_pos] = event_value;
11787 int GetGlobalAnimEventValue(int list_pos, int value_pos)
11789 if (list_pos == ANIM_EVENT_UNDEFINED)
11790 return ANIM_EVENT_NONE;
11792 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11793 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11795 return gaeli->event_value[value_pos];
11798 int GetGlobalAnimEventValueCount(int list_pos)
11800 if (list_pos == ANIM_EVENT_UNDEFINED)
11803 struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
11804 struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
11806 return gaeli->num_event_values;
11809 // This function checks if a string <s> of the format "string1, string2, ..."
11810 // exactly contains a string <s_contained>.
11812 static boolean string_has_parameter(char *s, char *s_contained)
11816 if (s == NULL || s_contained == NULL)
11819 if (strlen(s_contained) > strlen(s))
11822 if (strncmp(s, s_contained, strlen(s_contained)) == 0)
11824 char next_char = s[strlen(s_contained)];
11826 // check if next character is delimiter or whitespace
11827 if (next_char == ',' || next_char == '\0' ||
11828 next_char == ' ' || next_char == '\t')
11832 // check if string contains another parameter string after a comma
11833 substring = strchr(s, ',');
11834 if (substring == NULL) // string does not contain a comma
11837 // advance string pointer to next character after the comma
11840 // skip potential whitespaces after the comma
11841 while (*substring == ' ' || *substring == '\t')
11844 return string_has_parameter(substring, s_contained);
11847 static int get_anim_parameter_value_ce(char *s)
11850 char *pattern_1 = "ce_change:custom_";
11851 char *pattern_2 = ".page_";
11852 int pattern_1_len = strlen(pattern_1);
11853 char *matching_char = strstr(s_ptr, pattern_1);
11854 int result = ANIM_EVENT_NONE;
11856 if (matching_char == NULL)
11857 return ANIM_EVENT_NONE;
11859 result = ANIM_EVENT_CE_CHANGE;
11861 s_ptr = matching_char + pattern_1_len;
11863 // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
11864 if (*s_ptr >= '0' && *s_ptr <= '9')
11866 int gic_ce_nr = (*s_ptr++ - '0');
11868 if (*s_ptr >= '0' && *s_ptr <= '9')
11870 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11872 if (*s_ptr >= '0' && *s_ptr <= '9')
11873 gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
11876 if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
11877 return ANIM_EVENT_NONE;
11879 // custom element stored as 0 to 255
11882 result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
11886 // invalid custom element number specified
11888 return ANIM_EVENT_NONE;
11891 // check for change page number ("page_X" or "page_XX") (optional)
11892 if (strPrefix(s_ptr, pattern_2))
11894 s_ptr += strlen(pattern_2);
11896 if (*s_ptr >= '0' && *s_ptr <= '9')
11898 int gic_page_nr = (*s_ptr++ - '0');
11900 if (*s_ptr >= '0' && *s_ptr <= '9')
11901 gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
11903 if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
11904 return ANIM_EVENT_NONE;
11906 // change page stored as 1 to 32 (0 means "all change pages")
11908 result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
11912 // invalid animation part number specified
11914 return ANIM_EVENT_NONE;
11918 // discard result if next character is neither delimiter nor whitespace
11919 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
11920 *s_ptr == ' ' || *s_ptr == '\t'))
11921 return ANIM_EVENT_NONE;
11926 static int get_anim_parameter_value(char *s)
11928 int event_value[] =
11936 char *pattern_1[] =
11944 char *pattern_2 = ".part_";
11945 char *matching_char = NULL;
11947 int pattern_1_len = 0;
11948 int result = ANIM_EVENT_NONE;
11951 result = get_anim_parameter_value_ce(s);
11953 if (result != ANIM_EVENT_NONE)
11956 for (i = 0; i < ARRAY_SIZE(event_value); i++)
11958 matching_char = strstr(s_ptr, pattern_1[i]);
11959 pattern_1_len = strlen(pattern_1[i]);
11960 result = event_value[i];
11962 if (matching_char != NULL)
11966 if (matching_char == NULL)
11967 return ANIM_EVENT_NONE;
11969 s_ptr = matching_char + pattern_1_len;
11971 // check for main animation number ("anim_X" or "anim_XX")
11972 if (*s_ptr >= '0' && *s_ptr <= '9')
11974 int gic_anim_nr = (*s_ptr++ - '0');
11976 if (*s_ptr >= '0' && *s_ptr <= '9')
11977 gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
11979 if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
11980 return ANIM_EVENT_NONE;
11982 result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
11986 // invalid main animation number specified
11988 return ANIM_EVENT_NONE;
11991 // check for animation part number ("part_X" or "part_XX") (optional)
11992 if (strPrefix(s_ptr, pattern_2))
11994 s_ptr += strlen(pattern_2);
11996 if (*s_ptr >= '0' && *s_ptr <= '9')
11998 int gic_part_nr = (*s_ptr++ - '0');
12000 if (*s_ptr >= '0' && *s_ptr <= '9')
12001 gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12003 if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12004 return ANIM_EVENT_NONE;
12006 result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12010 // invalid animation part number specified
12012 return ANIM_EVENT_NONE;
12016 // discard result if next character is neither delimiter nor whitespace
12017 if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12018 *s_ptr == ' ' || *s_ptr == '\t'))
12019 return ANIM_EVENT_NONE;
12024 static int get_anim_parameter_values(char *s)
12026 int list_pos = ANIM_EVENT_UNDEFINED;
12027 int event_value = ANIM_EVENT_DEFAULT;
12029 if (string_has_parameter(s, "any"))
12030 event_value |= ANIM_EVENT_ANY;
12032 if (string_has_parameter(s, "click:self") ||
12033 string_has_parameter(s, "click") ||
12034 string_has_parameter(s, "self"))
12035 event_value |= ANIM_EVENT_SELF;
12037 if (string_has_parameter(s, "unclick:any"))
12038 event_value |= ANIM_EVENT_UNCLICK_ANY;
12040 // if animation event found, add it to global animation event list
12041 if (event_value != ANIM_EVENT_NONE)
12042 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12046 // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12047 event_value = get_anim_parameter_value(s);
12049 // if animation event found, add it to global animation event list
12050 if (event_value != ANIM_EVENT_NONE)
12051 list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12053 // continue with next part of the string, starting with next comma
12054 s = strchr(s + 1, ',');
12060 static int get_anim_action_parameter_value(char *token)
12062 // check most common default case first to massively speed things up
12063 if (strEqual(token, ARG_UNDEFINED))
12064 return ANIM_EVENT_ACTION_NONE;
12066 int result = getImageIDFromToken(token);
12070 char *gfx_token = getStringCat2("gfx.", token);
12072 result = getImageIDFromToken(gfx_token);
12074 checked_free(gfx_token);
12079 Key key = getKeyFromX11KeyName(token);
12081 if (key != KSYM_UNDEFINED)
12082 result = -(int)key;
12089 result = get_hash_from_string(token); // unsigned int => int
12090 result = ABS(result); // may be negative now
12091 result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12093 setHashEntry(anim_url_hash, int2str(result, 0), token);
12098 result = ANIM_EVENT_ACTION_NONE;
12103 int get_parameter_value(char *value_raw, char *suffix, int type)
12105 char *value = getStringToLower(value_raw);
12106 int result = 0; // probably a save default value
12108 if (strEqual(suffix, ".direction"))
12110 result = (strEqual(value, "left") ? MV_LEFT :
12111 strEqual(value, "right") ? MV_RIGHT :
12112 strEqual(value, "up") ? MV_UP :
12113 strEqual(value, "down") ? MV_DOWN : MV_NONE);
12115 else if (strEqual(suffix, ".position"))
12117 result = (strEqual(value, "left") ? POS_LEFT :
12118 strEqual(value, "right") ? POS_RIGHT :
12119 strEqual(value, "top") ? POS_TOP :
12120 strEqual(value, "upper") ? POS_UPPER :
12121 strEqual(value, "middle") ? POS_MIDDLE :
12122 strEqual(value, "lower") ? POS_LOWER :
12123 strEqual(value, "bottom") ? POS_BOTTOM :
12124 strEqual(value, "any") ? POS_ANY :
12125 strEqual(value, "ce") ? POS_CE :
12126 strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12127 strEqual(value, "last") ? POS_LAST : POS_UNDEFINED);
12129 else if (strEqual(suffix, ".align"))
12131 result = (strEqual(value, "left") ? ALIGN_LEFT :
12132 strEqual(value, "right") ? ALIGN_RIGHT :
12133 strEqual(value, "center") ? ALIGN_CENTER :
12134 strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12136 else if (strEqual(suffix, ".valign"))
12138 result = (strEqual(value, "top") ? VALIGN_TOP :
12139 strEqual(value, "bottom") ? VALIGN_BOTTOM :
12140 strEqual(value, "middle") ? VALIGN_MIDDLE :
12141 strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12143 else if (strEqual(suffix, ".anim_mode"))
12145 result = (string_has_parameter(value, "none") ? ANIM_NONE :
12146 string_has_parameter(value, "loop") ? ANIM_LOOP :
12147 string_has_parameter(value, "linear") ? ANIM_LINEAR :
12148 string_has_parameter(value, "pingpong") ? ANIM_PINGPONG :
12149 string_has_parameter(value, "pingpong2") ? ANIM_PINGPONG2 :
12150 string_has_parameter(value, "random") ? ANIM_RANDOM :
12151 string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12152 string_has_parameter(value, "ce_value") ? ANIM_CE_VALUE :
12153 string_has_parameter(value, "ce_score") ? ANIM_CE_SCORE :
12154 string_has_parameter(value, "ce_delay") ? ANIM_CE_DELAY :
12155 string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12156 string_has_parameter(value, "vertical") ? ANIM_VERTICAL :
12157 string_has_parameter(value, "centered") ? ANIM_CENTERED :
12158 string_has_parameter(value, "all") ? ANIM_ALL :
12159 string_has_parameter(value, "tiled") ? ANIM_TILED :
12160 string_has_parameter(value, "level_nr") ? ANIM_LEVEL_NR :
12163 if (string_has_parameter(value, "once"))
12164 result |= ANIM_ONCE;
12166 if (string_has_parameter(value, "reverse"))
12167 result |= ANIM_REVERSE;
12169 if (string_has_parameter(value, "opaque_player"))
12170 result |= ANIM_OPAQUE_PLAYER;
12172 if (string_has_parameter(value, "static_panel"))
12173 result |= ANIM_STATIC_PANEL;
12175 else if (strEqual(suffix, ".init_event") ||
12176 strEqual(suffix, ".anim_event"))
12178 result = get_anim_parameter_values(value);
12180 else if (strEqual(suffix, ".init_delay_action") ||
12181 strEqual(suffix, ".anim_delay_action") ||
12182 strEqual(suffix, ".post_delay_action") ||
12183 strEqual(suffix, ".init_event_action") ||
12184 strEqual(suffix, ".anim_event_action"))
12186 result = get_anim_action_parameter_value(value_raw);
12188 else if (strEqual(suffix, ".class"))
12190 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12191 get_hash_from_string(value));
12193 else if (strEqual(suffix, ".style"))
12195 result = STYLE_DEFAULT;
12197 if (string_has_parameter(value, "accurate_borders"))
12198 result |= STYLE_ACCURATE_BORDERS;
12200 if (string_has_parameter(value, "inner_corners"))
12201 result |= STYLE_INNER_CORNERS;
12203 if (string_has_parameter(value, "reverse"))
12204 result |= STYLE_REVERSE;
12206 if (string_has_parameter(value, "leftmost_position"))
12207 result |= STYLE_LEFTMOST_POSITION;
12209 if (string_has_parameter(value, "block_clicks"))
12210 result |= STYLE_BLOCK;
12212 if (string_has_parameter(value, "passthrough_clicks"))
12213 result |= STYLE_PASSTHROUGH;
12215 if (string_has_parameter(value, "multiple_actions"))
12216 result |= STYLE_MULTIPLE_ACTIONS;
12218 if (string_has_parameter(value, "consume_ce_event"))
12219 result |= STYLE_CONSUME_CE_EVENT;
12221 else if (strEqual(suffix, ".fade_mode"))
12223 result = (string_has_parameter(value, "none") ? FADE_MODE_NONE :
12224 string_has_parameter(value, "fade") ? FADE_MODE_FADE :
12225 string_has_parameter(value, "fade_in") ? FADE_MODE_FADE_IN :
12226 string_has_parameter(value, "fade_out") ? FADE_MODE_FADE_OUT :
12227 string_has_parameter(value, "crossfade") ? FADE_MODE_CROSSFADE :
12228 string_has_parameter(value, "melt") ? FADE_MODE_MELT :
12229 string_has_parameter(value, "curtain") ? FADE_MODE_CURTAIN :
12230 FADE_MODE_DEFAULT);
12232 else if (strEqual(suffix, ".auto_delay_unit"))
12234 result = (string_has_parameter(value, "ms") ? AUTO_DELAY_UNIT_MS :
12235 string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12236 AUTO_DELAY_UNIT_DEFAULT);
12238 else if (strPrefix(suffix, ".font")) // (may also be ".font_xyz")
12240 result = gfx.get_font_from_token_function(value);
12242 else // generic parameter of type integer or boolean
12244 result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12245 type == TYPE_INTEGER ? get_integer_from_string(value) :
12246 type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12247 ARG_UNDEFINED_VALUE);
12255 static int get_token_parameter_value(char *token, char *value_raw)
12259 if (token == NULL || value_raw == NULL)
12260 return ARG_UNDEFINED_VALUE;
12262 suffix = strrchr(token, '.');
12263 if (suffix == NULL)
12266 if (strEqual(suffix, ".element"))
12267 return getElementFromToken(value_raw);
12269 // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12270 return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12273 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12274 boolean ignore_defaults)
12278 for (i = 0; image_config_vars[i].token != NULL; i++)
12280 char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12282 // (ignore definitions set to "[DEFAULT]" which are already initialized)
12283 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12287 *image_config_vars[i].value =
12288 get_token_parameter_value(image_config_vars[i].token, value);
12292 void InitMenuDesignSettings_Static(void)
12294 // always start with reliable default values from static default config
12295 InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12298 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12302 // the following initializes hierarchical values from static configuration
12304 // special case: initialize "ARG_DEFAULT" values in static default config
12305 // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12306 titlescreen_initial_first_default.fade_mode =
12307 title_initial_first_default.fade_mode;
12308 titlescreen_initial_first_default.fade_delay =
12309 title_initial_first_default.fade_delay;
12310 titlescreen_initial_first_default.post_delay =
12311 title_initial_first_default.post_delay;
12312 titlescreen_initial_first_default.auto_delay =
12313 title_initial_first_default.auto_delay;
12314 titlescreen_initial_first_default.auto_delay_unit =
12315 title_initial_first_default.auto_delay_unit;
12316 titlescreen_first_default.fade_mode = title_first_default.fade_mode;
12317 titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12318 titlescreen_first_default.post_delay = title_first_default.post_delay;
12319 titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12320 titlescreen_first_default.auto_delay_unit =
12321 title_first_default.auto_delay_unit;
12322 titlemessage_initial_first_default.fade_mode =
12323 title_initial_first_default.fade_mode;
12324 titlemessage_initial_first_default.fade_delay =
12325 title_initial_first_default.fade_delay;
12326 titlemessage_initial_first_default.post_delay =
12327 title_initial_first_default.post_delay;
12328 titlemessage_initial_first_default.auto_delay =
12329 title_initial_first_default.auto_delay;
12330 titlemessage_initial_first_default.auto_delay_unit =
12331 title_initial_first_default.auto_delay_unit;
12332 titlemessage_first_default.fade_mode = title_first_default.fade_mode;
12333 titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12334 titlemessage_first_default.post_delay = title_first_default.post_delay;
12335 titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12336 titlemessage_first_default.auto_delay_unit =
12337 title_first_default.auto_delay_unit;
12339 titlescreen_initial_default.fade_mode = title_initial_default.fade_mode;
12340 titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12341 titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12342 titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12343 titlescreen_initial_default.auto_delay_unit =
12344 title_initial_default.auto_delay_unit;
12345 titlescreen_default.fade_mode = title_default.fade_mode;
12346 titlescreen_default.fade_delay = title_default.fade_delay;
12347 titlescreen_default.post_delay = title_default.post_delay;
12348 titlescreen_default.auto_delay = title_default.auto_delay;
12349 titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12350 titlemessage_initial_default.fade_mode = title_initial_default.fade_mode;
12351 titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12352 titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12353 titlemessage_initial_default.auto_delay_unit =
12354 title_initial_default.auto_delay_unit;
12355 titlemessage_default.fade_mode = title_default.fade_mode;
12356 titlemessage_default.fade_delay = title_default.fade_delay;
12357 titlemessage_default.post_delay = title_default.post_delay;
12358 titlemessage_default.auto_delay = title_default.auto_delay;
12359 titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12361 // special case: initialize "ARG_DEFAULT" values in static default config
12362 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12363 for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12365 titlescreen_initial_first[i] = titlescreen_initial_first_default;
12366 titlescreen_first[i] = titlescreen_first_default;
12367 titlemessage_initial_first[i] = titlemessage_initial_first_default;
12368 titlemessage_first[i] = titlemessage_first_default;
12370 titlescreen_initial[i] = titlescreen_initial_default;
12371 titlescreen[i] = titlescreen_default;
12372 titlemessage_initial[i] = titlemessage_initial_default;
12373 titlemessage[i] = titlemessage_default;
12376 // special case: initialize "ARG_DEFAULT" values in static default config
12377 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12378 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12380 if (i == GFX_SPECIAL_ARG_TITLE) // title values already initialized
12383 menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12384 menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12385 menu.next_screen[i] = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12388 // special case: initialize "ARG_DEFAULT" values in static default config
12389 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12390 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12392 viewport.window[i] = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12393 viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12394 viewport.door_1[i] = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12396 if (i == GFX_SPECIAL_ARG_EDITOR) // editor values already initialized
12399 viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12403 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12407 struct XY *dst, *src;
12409 game_buttons_xy[] =
12411 { &game.button.save, &game.button.stop },
12412 { &game.button.pause2, &game.button.pause },
12413 { &game.button.load, &game.button.play },
12414 { &game.button.undo, &game.button.stop },
12415 { &game.button.redo, &game.button.play },
12421 // special case: initialize later added SETUP list size from LEVELS value
12422 if (menu.list_size[GAME_MODE_SETUP] == -1)
12423 menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12425 // set default position for snapshot buttons to stop/pause/play buttons
12426 for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12427 if ((*game_buttons_xy[i].dst).x == -1 &&
12428 (*game_buttons_xy[i].dst).y == -1)
12429 *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12431 // --------------------------------------------------------------------------
12432 // dynamic viewports (including playfield margins, borders and alignments)
12433 // --------------------------------------------------------------------------
12435 // dynamic viewports currently only supported for landscape mode
12436 int display_width = MAX(video.display_width, video.display_height);
12437 int display_height = MIN(video.display_width, video.display_height);
12439 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12441 struct RectWithBorder *vp_window = &viewport.window[i];
12442 struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12443 struct RectWithBorder *vp_door_1 = &viewport.door_1[i];
12444 struct RectWithBorder *vp_door_2 = &viewport.door_2[i];
12445 boolean dynamic_window_width = (vp_window->min_width != -1);
12446 boolean dynamic_window_height = (vp_window->min_height != -1);
12447 boolean dynamic_playfield_width = (vp_playfield->min_width != -1);
12448 boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12450 // adjust window size if min/max width/height is specified
12452 if (vp_window->min_width != -1)
12454 int window_width = display_width;
12456 // when using static window height, use aspect ratio of display
12457 if (vp_window->min_height == -1)
12458 window_width = vp_window->height * display_width / display_height;
12460 vp_window->width = MAX(vp_window->min_width, window_width);
12463 if (vp_window->min_height != -1)
12465 int window_height = display_height;
12467 // when using static window width, use aspect ratio of display
12468 if (vp_window->min_width == -1)
12469 window_height = vp_window->width * display_height / display_width;
12471 vp_window->height = MAX(vp_window->min_height, window_height);
12474 if (vp_window->max_width != -1)
12475 vp_window->width = MIN(vp_window->width, vp_window->max_width);
12477 if (vp_window->max_height != -1)
12478 vp_window->height = MIN(vp_window->height, vp_window->max_height);
12480 int playfield_width = vp_window->width;
12481 int playfield_height = vp_window->height;
12483 // adjust playfield size and position according to specified margins
12485 playfield_width -= vp_playfield->margin_left;
12486 playfield_width -= vp_playfield->margin_right;
12488 playfield_height -= vp_playfield->margin_top;
12489 playfield_height -= vp_playfield->margin_bottom;
12491 // adjust playfield size if min/max width/height is specified
12493 if (vp_playfield->min_width != -1)
12494 vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
12496 if (vp_playfield->min_height != -1)
12497 vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
12499 if (vp_playfield->max_width != -1)
12500 vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
12502 if (vp_playfield->max_height != -1)
12503 vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
12505 // adjust playfield position according to specified alignment
12507 if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
12508 vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
12509 else if (vp_playfield->align == ALIGN_CENTER)
12510 vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
12511 else if (vp_playfield->align == ALIGN_RIGHT)
12512 vp_playfield->x += playfield_width - vp_playfield->width;
12514 if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
12515 vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
12516 else if (vp_playfield->valign == VALIGN_MIDDLE)
12517 vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
12518 else if (vp_playfield->valign == VALIGN_BOTTOM)
12519 vp_playfield->y += playfield_height - vp_playfield->height;
12521 vp_playfield->x += vp_playfield->margin_left;
12522 vp_playfield->y += vp_playfield->margin_top;
12524 // adjust individual playfield borders if only default border is specified
12526 if (vp_playfield->border_left == -1)
12527 vp_playfield->border_left = vp_playfield->border_size;
12528 if (vp_playfield->border_right == -1)
12529 vp_playfield->border_right = vp_playfield->border_size;
12530 if (vp_playfield->border_top == -1)
12531 vp_playfield->border_top = vp_playfield->border_size;
12532 if (vp_playfield->border_bottom == -1)
12533 vp_playfield->border_bottom = vp_playfield->border_size;
12535 // set dynamic playfield borders if borders are specified as undefined
12536 // (but only if window size was dynamic and playfield size was static)
12538 if (dynamic_window_width && !dynamic_playfield_width)
12540 if (vp_playfield->border_left == -1)
12542 vp_playfield->border_left = (vp_playfield->x -
12543 vp_playfield->margin_left);
12544 vp_playfield->x -= vp_playfield->border_left;
12545 vp_playfield->width += vp_playfield->border_left;
12548 if (vp_playfield->border_right == -1)
12550 vp_playfield->border_right = (vp_window->width -
12552 vp_playfield->width -
12553 vp_playfield->margin_right);
12554 vp_playfield->width += vp_playfield->border_right;
12558 if (dynamic_window_height && !dynamic_playfield_height)
12560 if (vp_playfield->border_top == -1)
12562 vp_playfield->border_top = (vp_playfield->y -
12563 vp_playfield->margin_top);
12564 vp_playfield->y -= vp_playfield->border_top;
12565 vp_playfield->height += vp_playfield->border_top;
12568 if (vp_playfield->border_bottom == -1)
12570 vp_playfield->border_bottom = (vp_window->height -
12572 vp_playfield->height -
12573 vp_playfield->margin_bottom);
12574 vp_playfield->height += vp_playfield->border_bottom;
12578 // adjust playfield size to be a multiple of a defined alignment tile size
12580 int align_size = vp_playfield->align_size;
12581 int playfield_xtiles = vp_playfield->width / align_size;
12582 int playfield_ytiles = vp_playfield->height / align_size;
12583 int playfield_width_corrected = playfield_xtiles * align_size;
12584 int playfield_height_corrected = playfield_ytiles * align_size;
12585 boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
12586 i == GFX_SPECIAL_ARG_EDITOR);
12588 if (is_playfield_mode &&
12589 dynamic_playfield_width &&
12590 vp_playfield->width != playfield_width_corrected)
12592 int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
12594 vp_playfield->width = playfield_width_corrected;
12596 if (vp_playfield->align == ALIGN_LEFT)
12598 vp_playfield->border_left += playfield_xdiff;
12600 else if (vp_playfield->align == ALIGN_RIGHT)
12602 vp_playfield->border_right += playfield_xdiff;
12604 else if (vp_playfield->align == ALIGN_CENTER)
12606 int border_left_diff = playfield_xdiff / 2;
12607 int border_right_diff = playfield_xdiff - border_left_diff;
12609 vp_playfield->border_left += border_left_diff;
12610 vp_playfield->border_right += border_right_diff;
12614 if (is_playfield_mode &&
12615 dynamic_playfield_height &&
12616 vp_playfield->height != playfield_height_corrected)
12618 int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
12620 vp_playfield->height = playfield_height_corrected;
12622 if (vp_playfield->valign == VALIGN_TOP)
12624 vp_playfield->border_top += playfield_ydiff;
12626 else if (vp_playfield->align == VALIGN_BOTTOM)
12628 vp_playfield->border_right += playfield_ydiff;
12630 else if (vp_playfield->align == VALIGN_MIDDLE)
12632 int border_top_diff = playfield_ydiff / 2;
12633 int border_bottom_diff = playfield_ydiff - border_top_diff;
12635 vp_playfield->border_top += border_top_diff;
12636 vp_playfield->border_bottom += border_bottom_diff;
12640 // adjust door positions according to specified alignment
12642 for (j = 0; j < 2; j++)
12644 struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
12646 if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
12647 vp_door->x = ALIGNED_VP_XPOS(vp_door);
12648 else if (vp_door->align == ALIGN_CENTER)
12649 vp_door->x = vp_window->width / 2 - vp_door->width / 2;
12650 else if (vp_door->align == ALIGN_RIGHT)
12651 vp_door->x += vp_window->width - vp_door->width;
12653 if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
12654 vp_door->y = ALIGNED_VP_YPOS(vp_door);
12655 else if (vp_door->valign == VALIGN_MIDDLE)
12656 vp_door->y = vp_window->height / 2 - vp_door->height / 2;
12657 else if (vp_door->valign == VALIGN_BOTTOM)
12658 vp_door->y += vp_window->height - vp_door->height;
12663 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
12667 struct XYTileSize *dst, *src;
12670 editor_buttons_xy[] =
12673 &editor.button.element_left, &editor.palette.element_left,
12674 IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
12677 &editor.button.element_middle, &editor.palette.element_middle,
12678 IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
12681 &editor.button.element_right, &editor.palette.element_right,
12682 IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
12689 // set default position for element buttons to element graphics
12690 for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
12692 if ((*editor_buttons_xy[i].dst).x == -1 &&
12693 (*editor_buttons_xy[i].dst).y == -1)
12695 struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
12697 gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
12699 *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
12703 // adjust editor palette rows and columns if specified to be dynamic
12705 if (editor.palette.cols == -1)
12707 int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
12708 int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
12709 int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
12711 editor.palette.cols = (vp_width - sc_width) / bt_width;
12713 if (editor.palette.x == -1)
12715 int palette_width = editor.palette.cols * bt_width + sc_width;
12717 editor.palette.x = (vp_width - palette_width) / 2;
12721 if (editor.palette.rows == -1)
12723 int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
12724 int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
12725 int tx_height = getFontHeight(FONT_TEXT_2);
12727 editor.palette.rows = (vp_height - tx_height) / bt_height;
12729 if (editor.palette.y == -1)
12731 int palette_height = editor.palette.rows * bt_height + tx_height;
12733 editor.palette.y = (vp_height - palette_height) / 2;
12738 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
12739 boolean initialize)
12741 // special case: check if network and preview player positions are redefined,
12742 // to compare this later against the main menu level preview being redefined
12743 struct TokenIntPtrInfo menu_config_players[] =
12745 { "main.network_players.x", &menu.main.network_players.redefined },
12746 { "main.network_players.y", &menu.main.network_players.redefined },
12747 { "main.preview_players.x", &menu.main.preview_players.redefined },
12748 { "main.preview_players.y", &menu.main.preview_players.redefined },
12749 { "preview.x", &preview.redefined },
12750 { "preview.y", &preview.redefined }
12756 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12757 *menu_config_players[i].value = FALSE;
12761 for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
12762 if (getHashEntry(hash, menu_config_players[i].token) != NULL)
12763 *menu_config_players[i].value = TRUE;
12767 static void InitMenuDesignSettings_PreviewPlayers(void)
12769 InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
12772 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
12774 InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
12777 static void LoadMenuDesignSettingsFromFilename(char *filename)
12779 static struct TitleFadingInfo tfi;
12780 static struct TitleMessageInfo tmi;
12781 static struct TokenInfo title_tokens[] =
12783 { TYPE_INTEGER, &tfi.fade_mode, ".fade_mode" },
12784 { TYPE_INTEGER, &tfi.fade_delay, ".fade_delay" },
12785 { TYPE_INTEGER, &tfi.post_delay, ".post_delay" },
12786 { TYPE_INTEGER, &tfi.auto_delay, ".auto_delay" },
12787 { TYPE_INTEGER, &tfi.auto_delay_unit, ".auto_delay_unit" },
12791 static struct TokenInfo titlemessage_tokens[] =
12793 { TYPE_INTEGER, &tmi.x, ".x" },
12794 { TYPE_INTEGER, &tmi.y, ".y" },
12795 { TYPE_INTEGER, &tmi.width, ".width" },
12796 { TYPE_INTEGER, &tmi.height, ".height" },
12797 { TYPE_INTEGER, &tmi.chars, ".chars" },
12798 { TYPE_INTEGER, &tmi.lines, ".lines" },
12799 { TYPE_INTEGER, &tmi.align, ".align" },
12800 { TYPE_INTEGER, &tmi.valign, ".valign" },
12801 { TYPE_INTEGER, &tmi.font, ".font" },
12802 { TYPE_BOOLEAN, &tmi.autowrap, ".autowrap" },
12803 { TYPE_BOOLEAN, &tmi.centered, ".centered" },
12804 { TYPE_BOOLEAN, &tmi.parse_comments, ".parse_comments" },
12805 { TYPE_INTEGER, &tmi.sort_priority, ".sort_priority" },
12806 { TYPE_INTEGER, &tmi.fade_mode, ".fade_mode" },
12807 { TYPE_INTEGER, &tmi.fade_delay, ".fade_delay" },
12808 { TYPE_INTEGER, &tmi.post_delay, ".post_delay" },
12809 { TYPE_INTEGER, &tmi.auto_delay, ".auto_delay" },
12810 { TYPE_INTEGER, &tmi.auto_delay_unit, ".auto_delay_unit" },
12816 struct TitleFadingInfo *info;
12821 // initialize first titles from "enter screen" definitions, if defined
12822 { &title_initial_first_default, "menu.enter_screen.TITLE" },
12823 { &title_first_default, "menu.enter_screen.TITLE" },
12825 // initialize title screens from "next screen" definitions, if defined
12826 { &title_initial_default, "menu.next_screen.TITLE" },
12827 { &title_default, "menu.next_screen.TITLE" },
12833 struct TitleMessageInfo *array;
12836 titlemessage_arrays[] =
12838 // initialize first titles from "enter screen" definitions, if defined
12839 { titlescreen_initial_first, "menu.enter_screen.TITLE" },
12840 { titlescreen_first, "menu.enter_screen.TITLE" },
12841 { titlemessage_initial_first, "menu.enter_screen.TITLE" },
12842 { titlemessage_first, "menu.enter_screen.TITLE" },
12844 // initialize titles from "next screen" definitions, if defined
12845 { titlescreen_initial, "menu.next_screen.TITLE" },
12846 { titlescreen, "menu.next_screen.TITLE" },
12847 { titlemessage_initial, "menu.next_screen.TITLE" },
12848 { titlemessage, "menu.next_screen.TITLE" },
12850 // overwrite titles with title definitions, if defined
12851 { titlescreen_initial_first, "[title_initial]" },
12852 { titlescreen_first, "[title]" },
12853 { titlemessage_initial_first, "[title_initial]" },
12854 { titlemessage_first, "[title]" },
12856 { titlescreen_initial, "[title_initial]" },
12857 { titlescreen, "[title]" },
12858 { titlemessage_initial, "[title_initial]" },
12859 { titlemessage, "[title]" },
12861 // overwrite titles with title screen/message definitions, if defined
12862 { titlescreen_initial_first, "[titlescreen_initial]" },
12863 { titlescreen_first, "[titlescreen]" },
12864 { titlemessage_initial_first, "[titlemessage_initial]" },
12865 { titlemessage_first, "[titlemessage]" },
12867 { titlescreen_initial, "[titlescreen_initial]" },
12868 { titlescreen, "[titlescreen]" },
12869 { titlemessage_initial, "[titlemessage_initial]" },
12870 { titlemessage, "[titlemessage]" },
12874 SetupFileHash *setup_file_hash;
12877 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12880 // the following initializes hierarchical values from dynamic configuration
12882 // special case: initialize with default values that may be overwritten
12883 // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
12884 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12886 struct TokenIntPtrInfo menu_config[] =
12888 { "menu.draw_xoffset", &menu.draw_xoffset[i] },
12889 { "menu.draw_yoffset", &menu.draw_yoffset[i] },
12890 { "menu.list_size", &menu.list_size[i] }
12893 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12895 char *token = menu_config[j].token;
12896 char *value = getHashEntry(setup_file_hash, token);
12899 *menu_config[j].value = get_integer_from_string(value);
12903 // special case: initialize with default values that may be overwritten
12904 // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
12905 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12907 struct TokenIntPtrInfo menu_config[] =
12909 { "menu.draw_xoffset.INFO", &menu.draw_xoffset_info[i] },
12910 { "menu.draw_yoffset.INFO", &menu.draw_yoffset_info[i] },
12911 { "menu.list_size.INFO", &menu.list_size_info[i] },
12912 { "menu.list_entry_size.INFO", &menu.list_entry_size_info[i] },
12913 { "menu.tile_size.INFO", &menu.tile_size_info[i] }
12916 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12918 char *token = menu_config[j].token;
12919 char *value = getHashEntry(setup_file_hash, token);
12922 *menu_config[j].value = get_integer_from_string(value);
12926 // special case: initialize with default values that may be overwritten
12927 // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
12928 for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
12930 struct TokenIntPtrInfo menu_config[] =
12932 { "menu.draw_xoffset.SETUP", &menu.draw_xoffset_setup[i] },
12933 { "menu.draw_yoffset.SETUP", &menu.draw_yoffset_setup[i] }
12936 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12938 char *token = menu_config[j].token;
12939 char *value = getHashEntry(setup_file_hash, token);
12942 *menu_config[j].value = get_integer_from_string(value);
12946 // special case: initialize with default values that may be overwritten
12947 // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
12948 for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
12950 struct TokenIntPtrInfo menu_config[] =
12952 { "menu.left_spacing.INFO", &menu.left_spacing_info[i] },
12953 { "menu.middle_spacing.INFO", &menu.middle_spacing_info[i] },
12954 { "menu.right_spacing.INFO", &menu.right_spacing_info[i] },
12955 { "menu.top_spacing.INFO", &menu.top_spacing_info[i] },
12956 { "menu.bottom_spacing.INFO", &menu.bottom_spacing_info[i] },
12957 { "menu.paragraph_spacing.INFO", &menu.paragraph_spacing_info[i] },
12958 { "menu.headline1_spacing.INFO", &menu.headline1_spacing_info[i] },
12959 { "menu.headline2_spacing.INFO", &menu.headline2_spacing_info[i] },
12960 { "menu.line_spacing.INFO", &menu.line_spacing_info[i] },
12961 { "menu.extra_spacing.INFO", &menu.extra_spacing_info[i] },
12964 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12966 char *token = menu_config[j].token;
12967 char *value = getHashEntry(setup_file_hash, token);
12970 *menu_config[j].value = get_integer_from_string(value);
12974 // special case: initialize with default values that may be overwritten
12975 // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12976 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12978 struct TokenIntPtrInfo menu_config[] =
12980 { "menu.enter_screen.fade_mode", &menu.enter_screen[i].fade_mode },
12981 { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
12982 { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
12983 { "menu.leave_screen.fade_mode", &menu.leave_screen[i].fade_mode },
12984 { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
12985 { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
12986 { "menu.next_screen.fade_mode", &menu.next_screen[i].fade_mode },
12987 { "menu.next_screen.fade_delay", &menu.next_screen[i].fade_delay },
12988 { "menu.next_screen.post_delay", &menu.next_screen[i].post_delay }
12991 for (j = 0; j < ARRAY_SIZE(menu_config); j++)
12993 char *token = menu_config[j].token;
12994 char *value = getHashEntry(setup_file_hash, token);
12997 *menu_config[j].value = get_token_parameter_value(token, value);
13001 // special case: initialize with default values that may be overwritten
13002 // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13003 for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13007 char *token_prefix;
13008 struct RectWithBorder *struct_ptr;
13012 { "viewport.window", &viewport.window[i] },
13013 { "viewport.playfield", &viewport.playfield[i] },
13014 { "viewport.door_1", &viewport.door_1[i] },
13015 { "viewport.door_2", &viewport.door_2[i] }
13018 for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13020 struct TokenIntPtrInfo vp_config[] =
13022 { ".x", &vp_struct[j].struct_ptr->x },
13023 { ".y", &vp_struct[j].struct_ptr->y },
13024 { ".width", &vp_struct[j].struct_ptr->width },
13025 { ".height", &vp_struct[j].struct_ptr->height },
13026 { ".min_width", &vp_struct[j].struct_ptr->min_width },
13027 { ".min_height", &vp_struct[j].struct_ptr->min_height },
13028 { ".max_width", &vp_struct[j].struct_ptr->max_width },
13029 { ".max_height", &vp_struct[j].struct_ptr->max_height },
13030 { ".margin_left", &vp_struct[j].struct_ptr->margin_left },
13031 { ".margin_right", &vp_struct[j].struct_ptr->margin_right },
13032 { ".margin_top", &vp_struct[j].struct_ptr->margin_top },
13033 { ".margin_bottom", &vp_struct[j].struct_ptr->margin_bottom },
13034 { ".border_left", &vp_struct[j].struct_ptr->border_left },
13035 { ".border_right", &vp_struct[j].struct_ptr->border_right },
13036 { ".border_top", &vp_struct[j].struct_ptr->border_top },
13037 { ".border_bottom", &vp_struct[j].struct_ptr->border_bottom },
13038 { ".border_size", &vp_struct[j].struct_ptr->border_size },
13039 { ".align_size", &vp_struct[j].struct_ptr->align_size },
13040 { ".align", &vp_struct[j].struct_ptr->align },
13041 { ".valign", &vp_struct[j].struct_ptr->valign }
13044 for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13046 char *token = getStringCat2(vp_struct[j].token_prefix,
13047 vp_config[k].token);
13048 char *value = getHashEntry(setup_file_hash, token);
13051 *vp_config[k].value = get_token_parameter_value(token, value);
13058 // special case: initialize with default values that may be overwritten
13059 // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13060 for (i = 0; title_info[i].info != NULL; i++)
13062 struct TitleFadingInfo *info = title_info[i].info;
13063 char *base_token = title_info[i].text;
13065 for (j = 0; title_tokens[j].type != -1; j++)
13067 char *token = getStringCat2(base_token, title_tokens[j].text);
13068 char *value = getHashEntry(setup_file_hash, token);
13072 int parameter_value = get_token_parameter_value(token, value);
13076 *(int *)title_tokens[j].value = (int)parameter_value;
13085 // special case: initialize with default values that may be overwritten
13086 // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13087 for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13089 struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13090 char *base_token = titlemessage_arrays[i].text;
13092 for (j = 0; titlemessage_tokens[j].type != -1; j++)
13094 char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13095 char *value = getHashEntry(setup_file_hash, token);
13099 int parameter_value = get_token_parameter_value(token, value);
13101 for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13105 if (titlemessage_tokens[j].type == TYPE_INTEGER)
13106 *(int *)titlemessage_tokens[j].value = (int)parameter_value;
13108 *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13118 // read (and overwrite with) values that may be specified in config file
13119 InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13121 // special case: check if network and preview player positions are redefined
13122 InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13124 freeSetupFileHash(setup_file_hash);
13127 void LoadMenuDesignSettings(void)
13129 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13131 InitMenuDesignSettings_Static();
13132 InitMenuDesignSettings_SpecialPreProcessing();
13133 InitMenuDesignSettings_PreviewPlayers();
13135 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13137 // first look for special settings configured in level series config
13138 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13140 if (fileExists(filename_base))
13141 LoadMenuDesignSettingsFromFilename(filename_base);
13144 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13146 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13147 LoadMenuDesignSettingsFromFilename(filename_local);
13149 InitMenuDesignSettings_SpecialPostProcessing();
13152 void LoadMenuDesignSettings_AfterGraphics(void)
13154 InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13157 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13158 boolean ignore_defaults)
13162 for (i = 0; sound_config_vars[i].token != NULL; i++)
13164 char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13166 // (ignore definitions set to "[DEFAULT]" which are already initialized)
13167 if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13171 *sound_config_vars[i].value =
13172 get_token_parameter_value(sound_config_vars[i].token, value);
13176 void InitSoundSettings_Static(void)
13178 // always start with reliable default values from static default config
13179 InitSoundSettings_FromHash(sound_config_hash, FALSE);
13182 static void LoadSoundSettingsFromFilename(char *filename)
13184 SetupFileHash *setup_file_hash;
13186 if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13189 // read (and overwrite with) values that may be specified in config file
13190 InitSoundSettings_FromHash(setup_file_hash, TRUE);
13192 freeSetupFileHash(setup_file_hash);
13195 void LoadSoundSettings(void)
13197 char *filename_base = UNDEFINED_FILENAME, *filename_local;
13199 InitSoundSettings_Static();
13201 if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13203 // first look for special settings configured in level series config
13204 filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13206 if (fileExists(filename_base))
13207 LoadSoundSettingsFromFilename(filename_base);
13210 filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13212 if (filename_local != NULL && !strEqual(filename_base, filename_local))
13213 LoadSoundSettingsFromFilename(filename_local);
13216 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13218 char *filename = getEditorSetupFilename();
13219 SetupFileList *setup_file_list, *list;
13220 SetupFileHash *element_hash;
13221 int num_unknown_tokens = 0;
13224 if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13227 element_hash = newSetupFileHash();
13229 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13230 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13232 // determined size may be larger than needed (due to unknown elements)
13234 for (list = setup_file_list; list != NULL; list = list->next)
13237 // add space for up to 3 more elements for padding that may be needed
13238 *num_elements += 3;
13240 // free memory for old list of elements, if needed
13241 checked_free(*elements);
13243 // allocate memory for new list of elements
13244 *elements = checked_malloc(*num_elements * sizeof(int));
13247 for (list = setup_file_list; list != NULL; list = list->next)
13249 char *value = getHashEntry(element_hash, list->token);
13251 if (value == NULL) // try to find obsolete token mapping
13253 char *mapped_token = get_mapped_token(list->token);
13255 if (mapped_token != NULL)
13257 value = getHashEntry(element_hash, mapped_token);
13259 free(mapped_token);
13265 (*elements)[(*num_elements)++] = atoi(value);
13269 if (num_unknown_tokens == 0)
13272 Warn("unknown token(s) found in config file:");
13273 Warn("- config file: '%s'", filename);
13275 num_unknown_tokens++;
13278 Warn("- token: '%s'", list->token);
13282 if (num_unknown_tokens > 0)
13285 while (*num_elements % 4) // pad with empty elements, if needed
13286 (*elements)[(*num_elements)++] = EL_EMPTY;
13288 freeSetupFileList(setup_file_list);
13289 freeSetupFileHash(element_hash);
13292 for (i = 0; i < *num_elements; i++)
13293 Debug("editor", "element '%s' [%d]\n",
13294 element_info[(*elements)[i]].token_name, (*elements)[i]);
13298 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13301 SetupFileHash *setup_file_hash = NULL;
13302 struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13303 char *filename_music, *filename_prefix, *filename_info;
13309 token_to_value_ptr[] =
13311 { "title_header", &tmp_music_file_info.title_header },
13312 { "artist_header", &tmp_music_file_info.artist_header },
13313 { "album_header", &tmp_music_file_info.album_header },
13314 { "year_header", &tmp_music_file_info.year_header },
13315 { "played_header", &tmp_music_file_info.played_header },
13317 { "title", &tmp_music_file_info.title },
13318 { "artist", &tmp_music_file_info.artist },
13319 { "album", &tmp_music_file_info.album },
13320 { "year", &tmp_music_file_info.year },
13321 { "played", &tmp_music_file_info.played },
13327 filename_music = (is_sound ? getCustomSoundFilename(basename) :
13328 getCustomMusicFilename(basename));
13330 if (filename_music == NULL)
13333 // ---------- try to replace file extension ----------
13335 filename_prefix = getStringCopy(filename_music);
13336 if (strrchr(filename_prefix, '.') != NULL)
13337 *strrchr(filename_prefix, '.') = '\0';
13338 filename_info = getStringCat2(filename_prefix, ".txt");
13340 if (fileExists(filename_info))
13341 setup_file_hash = loadSetupFileHash(filename_info);
13343 free(filename_prefix);
13344 free(filename_info);
13346 if (setup_file_hash == NULL)
13348 // ---------- try to add file extension ----------
13350 filename_prefix = getStringCopy(filename_music);
13351 filename_info = getStringCat2(filename_prefix, ".txt");
13353 if (fileExists(filename_info))
13354 setup_file_hash = loadSetupFileHash(filename_info);
13356 free(filename_prefix);
13357 free(filename_info);
13360 if (setup_file_hash == NULL)
13363 // ---------- music file info found ----------
13365 clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13367 for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13369 char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13371 *token_to_value_ptr[i].value_ptr =
13372 getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13375 tmp_music_file_info.basename = getStringCopy(basename);
13376 tmp_music_file_info.music = music;
13377 tmp_music_file_info.is_sound = is_sound;
13379 new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13380 *new_music_file_info = tmp_music_file_info;
13382 return new_music_file_info;
13385 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13387 return get_music_file_info_ext(basename, music, FALSE);
13390 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13392 return get_music_file_info_ext(basename, sound, TRUE);
13395 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13396 char *basename, boolean is_sound)
13398 for (; list != NULL; list = list->next)
13399 if (list->is_sound == is_sound && strEqual(list->basename, basename))
13405 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13407 return music_info_listed_ext(list, basename, FALSE);
13410 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13412 return music_info_listed_ext(list, basename, TRUE);
13415 void LoadMusicInfo(void)
13417 int num_music_noconf = getMusicListSize_NoConf();
13418 int num_music = getMusicListSize();
13419 int num_sounds = getSoundListSize();
13420 struct FileInfo *music, *sound;
13421 struct MusicFileInfo *next, **new;
13425 while (music_file_info != NULL)
13427 next = music_file_info->next;
13429 checked_free(music_file_info->basename);
13431 checked_free(music_file_info->title_header);
13432 checked_free(music_file_info->artist_header);
13433 checked_free(music_file_info->album_header);
13434 checked_free(music_file_info->year_header);
13435 checked_free(music_file_info->played_header);
13437 checked_free(music_file_info->title);
13438 checked_free(music_file_info->artist);
13439 checked_free(music_file_info->album);
13440 checked_free(music_file_info->year);
13441 checked_free(music_file_info->played);
13443 free(music_file_info);
13445 music_file_info = next;
13448 new = &music_file_info;
13450 // get (configured or unconfigured) music file info for all levels
13451 for (i = leveldir_current->first_level;
13452 i <= leveldir_current->last_level; i++)
13456 if (levelset.music[i] != MUS_UNDEFINED)
13458 // get music file info for configured level music
13459 music_nr = levelset.music[i];
13461 else if (num_music_noconf > 0)
13463 // get music file info for unconfigured level music
13464 int level_pos = i - leveldir_current->first_level;
13466 music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
13473 char *basename = getMusicInfoEntryFilename(music_nr);
13475 if (basename == NULL)
13478 if (!music_info_listed(music_file_info, basename))
13480 *new = get_music_file_info(basename, music_nr);
13483 new = &(*new)->next;
13487 // get music file info for all remaining configured music files
13488 for (i = 0; i < num_music; i++)
13490 music = getMusicListEntry(i);
13492 if (music->filename == NULL)
13495 if (strEqual(music->filename, UNDEFINED_FILENAME))
13498 // a configured file may be not recognized as music
13499 if (!FileIsMusic(music->filename))
13502 if (!music_info_listed(music_file_info, music->filename))
13504 *new = get_music_file_info(music->filename, i);
13507 new = &(*new)->next;
13511 // get sound file info for all configured sound files
13512 for (i = 0; i < num_sounds; i++)
13514 sound = getSoundListEntry(i);
13516 if (sound->filename == NULL)
13519 if (strEqual(sound->filename, UNDEFINED_FILENAME))
13522 // a configured file may be not recognized as sound
13523 if (!FileIsSound(sound->filename))
13526 if (!sound_info_listed(music_file_info, sound->filename))
13528 *new = get_sound_file_info(sound->filename, i);
13530 new = &(*new)->next;
13534 // add pointers to previous list nodes
13536 struct MusicFileInfo *node = music_file_info;
13538 while (node != NULL)
13541 node->next->prev = node;
13547 static void add_helpanim_entry(int element, int action, int direction,
13548 int delay, int *num_list_entries)
13550 struct HelpAnimInfo *new_list_entry;
13551 (*num_list_entries)++;
13554 checked_realloc(helpanim_info,
13555 *num_list_entries * sizeof(struct HelpAnimInfo));
13556 new_list_entry = &helpanim_info[*num_list_entries - 1];
13558 new_list_entry->element = element;
13559 new_list_entry->action = action;
13560 new_list_entry->direction = direction;
13561 new_list_entry->delay = delay;
13564 static void print_unknown_token(char *filename, char *token, int token_nr)
13569 Warn("unknown token(s) found in config file:");
13570 Warn("- config file: '%s'", filename);
13573 Warn("- token: '%s'", token);
13576 static void print_unknown_token_end(int token_nr)
13582 void LoadHelpAnimInfo(void)
13584 char *filename = getHelpAnimFilename();
13585 SetupFileList *setup_file_list = NULL, *list;
13586 SetupFileHash *element_hash, *action_hash, *direction_hash;
13587 int num_list_entries = 0;
13588 int num_unknown_tokens = 0;
13591 if (fileExists(filename))
13592 setup_file_list = loadSetupFileList(filename);
13594 if (setup_file_list == NULL)
13596 // use reliable default values from static configuration
13597 SetupFileList *insert_ptr;
13599 insert_ptr = setup_file_list =
13600 newSetupFileList(helpanim_config[0].token,
13601 helpanim_config[0].value);
13603 for (i = 1; helpanim_config[i].token; i++)
13604 insert_ptr = addListEntry(insert_ptr,
13605 helpanim_config[i].token,
13606 helpanim_config[i].value);
13609 element_hash = newSetupFileHash();
13610 action_hash = newSetupFileHash();
13611 direction_hash = newSetupFileHash();
13613 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
13614 setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13616 for (i = 0; i < NUM_ACTIONS; i++)
13617 setHashEntry(action_hash, element_action_info[i].suffix,
13618 i_to_a(element_action_info[i].value));
13620 // do not store direction index (bit) here, but direction value!
13621 for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
13622 setHashEntry(direction_hash, element_direction_info[i].suffix,
13623 i_to_a(1 << element_direction_info[i].value));
13625 for (list = setup_file_list; list != NULL; list = list->next)
13627 char *element_token, *action_token, *direction_token;
13628 char *element_value, *action_value, *direction_value;
13629 int delay = atoi(list->value);
13631 if (strEqual(list->token, "end"))
13633 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13638 /* first try to break element into element/action/direction parts;
13639 if this does not work, also accept combined "element[.act][.dir]"
13640 elements (like "dynamite.active"), which are unique elements */
13642 if (strchr(list->token, '.') == NULL) // token contains no '.'
13644 element_value = getHashEntry(element_hash, list->token);
13645 if (element_value != NULL) // element found
13646 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13647 &num_list_entries);
13650 // no further suffixes found -- this is not an element
13651 print_unknown_token(filename, list->token, num_unknown_tokens++);
13657 // token has format "<prefix>.<something>"
13659 action_token = strchr(list->token, '.'); // suffix may be action ...
13660 direction_token = action_token; // ... or direction
13662 element_token = getStringCopy(list->token);
13663 *strchr(element_token, '.') = '\0';
13665 element_value = getHashEntry(element_hash, element_token);
13667 if (element_value == NULL) // this is no element
13669 element_value = getHashEntry(element_hash, list->token);
13670 if (element_value != NULL) // combined element found
13671 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13672 &num_list_entries);
13674 print_unknown_token(filename, list->token, num_unknown_tokens++);
13676 free(element_token);
13681 action_value = getHashEntry(action_hash, action_token);
13683 if (action_value != NULL) // action found
13685 add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
13686 &num_list_entries);
13688 free(element_token);
13693 direction_value = getHashEntry(direction_hash, direction_token);
13695 if (direction_value != NULL) // direction found
13697 add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
13698 &num_list_entries);
13700 free(element_token);
13705 if (strchr(action_token + 1, '.') == NULL)
13707 // no further suffixes found -- this is not an action nor direction
13709 element_value = getHashEntry(element_hash, list->token);
13710 if (element_value != NULL) // combined element found
13711 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13712 &num_list_entries);
13714 print_unknown_token(filename, list->token, num_unknown_tokens++);
13716 free(element_token);
13721 // token has format "<prefix>.<suffix>.<something>"
13723 direction_token = strchr(action_token + 1, '.');
13725 action_token = getStringCopy(action_token);
13726 *strchr(action_token + 1, '.') = '\0';
13728 action_value = getHashEntry(action_hash, action_token);
13730 if (action_value == NULL) // this is no action
13732 element_value = getHashEntry(element_hash, list->token);
13733 if (element_value != NULL) // combined element found
13734 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13735 &num_list_entries);
13737 print_unknown_token(filename, list->token, num_unknown_tokens++);
13739 free(element_token);
13740 free(action_token);
13745 direction_value = getHashEntry(direction_hash, direction_token);
13747 if (direction_value != NULL) // direction found
13749 add_helpanim_entry(atoi(element_value), atoi(action_value),
13750 atoi(direction_value), delay, &num_list_entries);
13752 free(element_token);
13753 free(action_token);
13758 // this is no direction
13760 element_value = getHashEntry(element_hash, list->token);
13761 if (element_value != NULL) // combined element found
13762 add_helpanim_entry(atoi(element_value), -1, -1, delay,
13763 &num_list_entries);
13765 print_unknown_token(filename, list->token, num_unknown_tokens++);
13767 free(element_token);
13768 free(action_token);
13771 print_unknown_token_end(num_unknown_tokens);
13773 add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
13774 add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
13776 freeSetupFileList(setup_file_list);
13777 freeSetupFileHash(element_hash);
13778 freeSetupFileHash(action_hash);
13779 freeSetupFileHash(direction_hash);
13782 for (i = 0; i < num_list_entries; i++)
13783 Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
13784 EL_NAME(helpanim_info[i].element),
13785 helpanim_info[i].element,
13786 helpanim_info[i].action,
13787 helpanim_info[i].direction,
13788 helpanim_info[i].delay);
13792 void LoadHelpTextInfo(void)
13794 char *filename = getHelpTextFilename();
13797 if (helptext_info != NULL)
13799 freeSetupFileHash(helptext_info);
13800 helptext_info = NULL;
13803 if (fileExists(filename))
13804 helptext_info = loadSetupFileHash(filename);
13806 if (helptext_info == NULL)
13808 // use reliable default values from static configuration
13809 helptext_info = newSetupFileHash();
13811 for (i = 0; helptext_config[i].token; i++)
13812 setHashEntry(helptext_info,
13813 helptext_config[i].token,
13814 helptext_config[i].value);
13818 BEGIN_HASH_ITERATION(helptext_info, itr)
13820 Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
13821 HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
13823 END_HASH_ITERATION(hash, itr)
13828 // ----------------------------------------------------------------------------
13830 // ----------------------------------------------------------------------------
13832 #define MAX_NUM_CONVERT_LEVELS 1000
13834 void ConvertLevels(void)
13836 static LevelDirTree *convert_leveldir = NULL;
13837 static int convert_level_nr = -1;
13838 static int num_levels_handled = 0;
13839 static int num_levels_converted = 0;
13840 static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
13843 convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
13844 global.convert_leveldir);
13846 if (convert_leveldir == NULL)
13847 Fail("no such level identifier: '%s'", global.convert_leveldir);
13849 leveldir_current = convert_leveldir;
13851 if (global.convert_level_nr != -1)
13853 convert_leveldir->first_level = global.convert_level_nr;
13854 convert_leveldir->last_level = global.convert_level_nr;
13857 convert_level_nr = convert_leveldir->first_level;
13859 PrintLine("=", 79);
13860 Print("Converting levels\n");
13861 PrintLine("-", 79);
13862 Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
13863 Print("Level series name: '%s'\n", convert_leveldir->name);
13864 Print("Level series author: '%s'\n", convert_leveldir->author);
13865 Print("Number of levels: %d\n", convert_leveldir->levels);
13866 PrintLine("=", 79);
13869 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13870 levels_failed[i] = FALSE;
13872 while (convert_level_nr <= convert_leveldir->last_level)
13874 char *level_filename;
13877 level_nr = convert_level_nr++;
13879 Print("Level %03d: ", level_nr);
13881 LoadLevel(level_nr);
13882 if (level.no_level_file || level.no_valid_file)
13884 Print("(no level)\n");
13888 Print("converting level ... ");
13891 // special case: conversion of some EMC levels as requested by ACME
13892 level.game_engine_type = GAME_ENGINE_TYPE_RND;
13895 level_filename = getDefaultLevelFilename(level_nr);
13896 new_level = !fileExists(level_filename);
13900 SaveLevel(level_nr);
13902 num_levels_converted++;
13904 Print("converted.\n");
13908 if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
13909 levels_failed[level_nr] = TRUE;
13911 Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
13914 num_levels_handled++;
13918 PrintLine("=", 79);
13919 Print("Number of levels handled: %d\n", num_levels_handled);
13920 Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
13921 (num_levels_handled ?
13922 num_levels_converted * 100 / num_levels_handled : 0));
13923 PrintLine("-", 79);
13924 Print("Summary (for automatic parsing by scripts):\n");
13925 Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
13926 convert_leveldir->identifier, num_levels_converted,
13927 num_levels_handled,
13928 (num_levels_handled ?
13929 num_levels_converted * 100 / num_levels_handled : 0));
13931 if (num_levels_handled != num_levels_converted)
13933 Print(", FAILED:");
13934 for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
13935 if (levels_failed[i])
13940 PrintLine("=", 79);
13942 CloseAllAndExit(0);
13946 // ----------------------------------------------------------------------------
13947 // create and save images for use in level sketches (raw BMP format)
13948 // ----------------------------------------------------------------------------
13950 void CreateLevelSketchImages(void)
13956 InitElementPropertiesGfxElement();
13958 bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
13959 bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
13961 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13963 int element = getMappedElement(i);
13964 char basename1[16];
13965 char basename2[16];
13969 sprintf(basename1, "%04d.bmp", i);
13970 sprintf(basename2, "%04ds.bmp", i);
13972 filename1 = getPath2(global.create_sketch_images_dir, basename1);
13973 filename2 = getPath2(global.create_sketch_images_dir, basename2);
13975 DrawSizedElement(0, 0, element, TILESIZE);
13976 BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
13978 if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
13979 Fail("cannot save level sketch image file '%s'", filename1);
13981 DrawSizedElement(0, 0, element, MINI_TILESIZE);
13982 BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
13984 if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
13985 Fail("cannot save level sketch image file '%s'", filename2);
13990 // create corresponding SQL statements (for normal and small images)
13993 printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13994 printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
13997 printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
13998 printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14000 // optional: create content for forum level sketch demonstration post
14002 fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14005 FreeBitmap(bitmap1);
14006 FreeBitmap(bitmap2);
14009 fprintf(stderr, "\n");
14011 Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14013 CloseAllAndExit(0);
14017 // ----------------------------------------------------------------------------
14018 // create and save images for element collecting animations (raw BMP format)
14019 // ----------------------------------------------------------------------------
14021 static boolean createCollectImage(int element)
14023 return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14026 void CreateCollectElementImages(void)
14030 int anim_frames = num_steps - 1;
14031 int tile_size = TILESIZE;
14032 int anim_width = tile_size * anim_frames;
14033 int anim_height = tile_size;
14034 int num_collect_images = 0;
14035 int pos_collect_images = 0;
14037 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14038 if (createCollectImage(i))
14039 num_collect_images++;
14041 Info("Creating %d element collecting animation images ...",
14042 num_collect_images);
14044 int dst_width = anim_width * 2;
14045 int dst_height = anim_height * num_collect_images / 2;
14046 Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14047 char *basename_bmp = "RocksCollect.bmp";
14048 char *basename_png = "RocksCollect.png";
14049 char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14050 char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14051 int len_filename_bmp = strlen(filename_bmp);
14052 int len_filename_png = strlen(filename_png);
14053 int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14054 char cmd_convert[max_command_len];
14056 snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14060 // force using RGBA surface for destination bitmap
14061 SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14062 SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14064 dst_bitmap->surface =
14065 SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14067 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14069 if (!createCollectImage(i))
14072 int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14073 int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14074 int graphic = el2img(i);
14075 char *token_name = element_info[i].token_name;
14076 Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14077 Bitmap *src_bitmap;
14080 Info("- creating collecting image for '%s' ...", token_name);
14082 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14084 BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14085 tile_size, tile_size, 0, 0);
14087 // force using RGBA surface for temporary bitmap (using transparent black)
14088 SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14089 SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14091 tmp_bitmap->surface =
14092 SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14094 tmp_bitmap->surface_masked = tmp_bitmap->surface;
14096 for (j = 0; j < anim_frames; j++)
14098 int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14099 int frame_size = frame_size_final * num_steps;
14100 int offset = (tile_size - frame_size_final) / 2;
14101 Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14103 while (frame_size > frame_size_final)
14107 Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14109 FreeBitmap(frame_bitmap);
14111 frame_bitmap = half_bitmap;
14114 BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14115 frame_size_final, frame_size_final,
14116 dst_x + j * tile_size + offset, dst_y + offset);
14118 FreeBitmap(frame_bitmap);
14121 tmp_bitmap->surface_masked = NULL;
14123 FreeBitmap(tmp_bitmap);
14125 pos_collect_images++;
14128 if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14129 Fail("cannot save element collecting image file '%s'", filename_bmp);
14131 FreeBitmap(dst_bitmap);
14133 Info("Converting image file from BMP to PNG ...");
14135 if (system(cmd_convert) != 0)
14136 Fail("converting image file failed");
14138 unlink(filename_bmp);
14142 CloseAllAndExit(0);
14146 // ----------------------------------------------------------------------------
14147 // create and save images for custom and group elements (raw BMP format)
14148 // ----------------------------------------------------------------------------
14150 void CreateCustomElementImages(char *directory)
14152 char *src_basename = "RocksCE-template.ilbm";
14153 char *dst_basename = "RocksCE.bmp";
14154 char *src_filename = getPath2(directory, src_basename);
14155 char *dst_filename = getPath2(directory, dst_basename);
14156 Bitmap *src_bitmap;
14158 int yoffset_ce = 0;
14159 int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14162 InitVideoDefaults();
14164 ReCreateBitmap(&backbuffer, video.width, video.height);
14166 src_bitmap = LoadImage(src_filename);
14168 bitmap = CreateBitmap(TILEX * 16 * 2,
14169 TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14172 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14179 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14180 TILEX * x, TILEY * y + yoffset_ce);
14182 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14184 TILEX * x + TILEX * 16,
14185 TILEY * y + yoffset_ce);
14187 for (j = 2; j >= 0; j--)
14191 BlitBitmap(src_bitmap, bitmap,
14192 TILEX + c * 7, 0, 6, 10,
14193 TILEX * x + 6 + j * 7,
14194 TILEY * y + 11 + yoffset_ce);
14196 BlitBitmap(src_bitmap, bitmap,
14197 TILEX + c * 8, TILEY, 6, 10,
14198 TILEX * 16 + TILEX * x + 6 + j * 8,
14199 TILEY * y + 10 + yoffset_ce);
14205 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14212 BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14213 TILEX * x, TILEY * y + yoffset_ge);
14215 BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14217 TILEX * x + TILEX * 16,
14218 TILEY * y + yoffset_ge);
14220 for (j = 1; j >= 0; j--)
14224 BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14225 TILEX * x + 6 + j * 10,
14226 TILEY * y + 11 + yoffset_ge);
14228 BlitBitmap(src_bitmap, bitmap,
14229 TILEX + c * 8, TILEY + 12, 6, 10,
14230 TILEX * 16 + TILEX * x + 10 + j * 8,
14231 TILEY * y + 10 + yoffset_ge);
14237 if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14238 Fail("cannot save CE graphics file '%s'", dst_filename);
14240 FreeBitmap(bitmap);
14242 CloseAllAndExit(0);